Graphics

Interface to Graphics

The ui_test interface is an MFC single window application using simple graphics to display SMS geometry objects for demonstration and debugging purposes. The ui_test application and its approach to graphics management can be used as an example of how to write an NMTLib based geometry application but it is assumed most customers will want to use ui_test as an example and primarily rewrite the graphics interface for their application.

Display Architecture

The ui_test program is a Microsoft MFC windows application using state-based graphics, that is, the user interface is used to specify a set of graphic state bits and values which control the graphics displayed in the output window. The Windows message system framework converts user I/O actions into calls to the methods of the CSMLibView class defined in file ui_test/cubeview.h. Most of the CSMLibView class methods are simple wrapper functions that pass their calls on to an IwView object whose methods do the actual work.

The CSMLibView and IwView methods that control the ui_test graphics make iwgfx_SetCalls() declared in file iwgfx_extern.h to set the global view and drawing parameters. The global viewing and drawing parameters are stored together within two static global objects called s_Disp and s_View of type IwDisplayParameters and IwViewParameters respectively.

Display and view parameters are stored in the following classes:

  • IwDisplayParameters Draw Bits, Parameters, and Colors
  • IwViewParameters Display Draw State and View Orientation
  • IwView Window Size, View Orientation, and Transformation, Pick Object Data, Window handle, and Cursor Operation Type.
  • ViewData View Orientation for Orientation save/restore.

The ui_test 3D graphics are output using the OpenGL DisplayList mechanism. All 3D graphics to be displayed are first compiled into an OpenGL DisplayList and added to a global array of display lists stored in s_View::m_ pActiveLists. All the stored display lists are drawn to the output window with a call to iwgfx_DrawLists(). The iwgfx_DrawLists() function is called either directly or by the IwView::DrawScene() method which clears the OpenGL color and depth buffers, applies the s_View::m_aViewMatrix to the current (ModelView) matrix stack, outputs the display lists stored in s_View::m_pActiveLists with a call to iwgfx_DrawLists(), restores the matrix stack, and swap buffers.
DisplayLists are created and added to the global DisplayList array by Draw() methods implemented for various members of the NMTLib IwObject class hierarchy. The structures of these Draw methods are all supposed to be the same. First they create and add a DisplayList to the global DisplayList array with a call to iwgfx_Open(), followed by a sequence of draw commands designed to display the object which all get compiled into the open DisplayList, and ended with a call to iwgfx_Close() which ends the construction of the DisplayList.

The iwgfx_Open()/iwgfx_Close() commands concatenate nested calls. That is a 2nd call to iwgfx_Open() made before calling iwgfx_Close() does not create a new nested DisplayList but rather continues using the currently open DisplayList and increments a counter. Each iwgfx_Close() call decrements the counter until the matching iwgfx_Close() call is made for the initial iwgfx_Open() call at which time the Display list completed and closed. This property is important in limiting the total number of display lists generated by objects like faces which draw edges and points as part of their own display.

For objects with simple graphics, the draw commands are contained within the Draw() functions otherwise the draw commands for objects with more complicated graphics are isolated in methods called OutputGraphics(). The basic draw commands include the following:

  • iwgfx_DrawPoint(),
  • iwgfx_DrawLine(),
  • iwgfx_DrawPolyLine(),
  • iwgfx_DrawTriangle(), and for drawing meshes
  • iwgfx_NewNurbsRenderer(),
  • iwgfx_NurbsSurface(), and
  • iwgfx_NurbsCurve().

The Draw() and OutputGraphics() methods use the s_Disp IwDisplayParameters object values as needed to control the graphics that they add to the open DisplayList.
The ui_test implementations of these draw command functions make OpenGL calls, however other rendering engines can be used to display the ui_test graphics by rewriting these functions for the desired display engine.

Some of the classes derived from IwObject that have Draw() methods, with or without OutputGraphics() methods, include:

  • IwBrep IwPolyBrep IwBrepCache
  • IwRegion IwPolyFace IwSurfaceCache
  • IwShell IwCPolyFace IwCurveCache
  • IwFace IwPolyLoop
  • IwLoopuse IwPolyEdge IwVector2d
  • IwEdge IwVector3d
  • IwEdgeuse IwSurface IwExtend2d
  • IwVertex IwCubicBezierSurface IwExtend3D
  • IwFilletEdge IwBSplineSurface IwGrid
  • IwCurve IwTangentField
  • IwCurveClassification IwCompositeCurve IwAxis2Placement
  • IwPointClassification IwPolarBox
  • IwLineSegmentClassification IwCutter IwPseudoBox
  • IwPolyPointClassification IwPlaneCutter
  • IwCurveInterval IwGaps
  • IwSolution IwObjGaps
  • IwSolutionArray

The Draw() methods can be called at anytime to place the graphics for an object into a display list that gets added to the global DisplayList array. This feature is very helpful for debugging. Object Draw() methods can be placed within an algorithm to see how it modifies geometry as it progresses. The function my_gfx_loop() can be called at anytime to render the DisplayLists and to temporarily switch the flow of control to the ui_test interface so that the displayed graphics can be rotated, zoomed, and panned before continuing with the algorithm under study.
Standard object display is managed through a set of global lists named:

  • UserBreps contains all Breps to render
  • UserPoints contains all Points to render
  • UserCurves contains all Curves to render
  • UserSurfaces contains all Surfaces to render
  • UserPolyBreps contains all PolyBreps to render
  • UserBooleanTrees contains all BooleanTrees to render
  • UserGaps contains all ObjGaps to render

The user object lists are managed by the following functions:

  • my_Delete_All_Objects() Clear all user object lists.
  • my_Draw_All_Objects() Rebuild global DisplayList array with DisplayLists for every user object list item.
  • my_Dump_All_Objects() Dump a pretty printed description of every user object list item to standard output.
  • UserTest::Draw(IwBrep *) Add Brep copy to UserBreps
  • UserTest::Draw(IwPolyBrep *) Add PolyBrep copy to UserPolyBreps
  • UserTest::Draw(IwCurve *) Add Curve copy to UserCurves
  • UserTest::Draw(IwEdge *) Add Edge->Curve copy to UserCurves
  • UserTest::Draw(IwSurface *) Add Surface copy to UserSurfaces
  • UserTest::Draw(IwFace *) Add Face->Surface copy to UserSurfaces
  • UserTest::Draw(IwVertex *) Add Vertex->Point copy to UserPoints
  • UserTest::Draw(IwPoint3d *) Add Point copy to UserPoints
  • UserTest::Draw(IwPoint3d *, IwPoint3d *) Add Line to UserCurves
  • UserTest::Draw(IwTArray(IwPoint3d)) Add point copies to UserPoints

Functions within the ui_test interface and explicit calls in user programs add and remove objects from the user object lists. A call to my_Draw_All_Objects() clears the old global display list array and replaces it with a new array of display lists generated by calling the Draw() method on every user object list item.
The following picture summarizes the ui_test graphics flow of control:

GraphicsChart.jpg

Figure 1: ui_test graphics flow of control

View Parameters

The parameters that control the size, orientation, and positioning of the view box in model coordinates are stored in s_View, an IwViewParameters object. Currently this object is also used to store global interface state data.

Display Parameters

The parameters that control the output behavior of the Object Draw() functions are stored in s_Disp, an IwDisplayParameters object. Actions by the user change these values that will change the output graphics the next time that the global DisplayList array members are regenerated and redisplayed. The first set of values in this object is a set of draw bits and values that specify how to draw Breps, Faces, and Edges. These draw bits, their default values, and their associated behaviors include:

  • m_bHiddenCurve [FALSE] TRUE = Draw hidden line image
  • m_bDrawWireFrame [TRUE ] TRUE = Draw all Edges and Vertices
  • m_bDrawFacets [FALSE] TRUE = Generate/Output polygon facets
  • m_bDoShading [TRUE ] TRUE = display polygon facets as shaded image
    FALSE= display polygon facets as wires.
  • m_eTessellator [IW_DM_NLIB] Specify Nlib/openGL/NMTLib tessellator
  • m_bUseEdgeTypeColors [TRUE ] TRUE = Select EdgeColor based on type (manifold, lamina, wire)
    FALSE= Select EdgeColor based on attribute or owner color
  • m_bDrawCrossHatch [FALSE] TRUE = Draw UV IsoParameterCurve crosshatch lines
  • m_bDrawKnots [FALSE] TRUE = Draw edge knotPoints (place surface xhatch lines on knot boundaries)
  • m_bDrawPolygon [FALSE] TRUE = Draw surface->ControlNet and curve->ControlPolygon
  • m_bDrawNormals [FALSE] TRUE = Draw Surface midPoint Normal vector
  • m_bDrawCurvature [FALSE] TRUE = Draw Curve Curvature combs
  • m_bDrawDerivatives [FALSE] TRUE = Draw Curve 1st Derivative combs
  • m_bDrawPinCushion [FALSE] TRUE = Draw surface vector fields
  • m_eLastMode Last value stored by iwgfx_SetDrawingMode. note: this value does not determine the draw state - rather when it was set all the drawing bits were set to a specific state, since that time, any draw bit might have been altered. This is saved to be kind of backward compatible with the previous mode based interface style.

The following sets of values within the s_Disp object contain parameters that specify the size and look of the graphical output. These values, their default values, and their behaviors are as follows:
Shading

    • m_bFlatShading // [FALSE] TRUE = When shading use flat shading, not Gouraud shading
    • m_bHIddenPolygon // [FALSE] TRUE = When shading skip back facing polygons

Cross Hatch

    • m_lCrossHatchUCount // [4] How many curves between the Knots in the U cross hatch
    • m_lCrossHatchVCount // [4] How many curves between the Knots in the V cross hatch
    • m_dCrossHatchLineWidth // [1] Line width for the non-knot cross hatch curves
    • m_dCrossHatchKnotLineWidth // [2] Line width for the cross hatch curves on knot values

Hidden Curve

    • m_lHiddenCurveDash // [1] 0 = visible, 1 - dash invisible, 2 - silhouette only
    • m_vHCRView // [Origin=(0,0,0), XAxis=(1,0,0), YAxis=(0,1,0)]
    • m_bPerspective // [FALSE] TRUE = Use Projection View not OrthoGraphic

Tessellation

    • m_dCrvTessAngle [10.0] Angular tolerance used in tessellation in degrees
    • m_dSrfTessAngle [25.0] Angular tolerance used in tessellation in degrees
    • m_dChordHeight [ 0.1] Tolerance that controls curve and polygon tessellation using chord height measurements. If using NURBS this is a pixel space distance (i.e. 25.0). If using Tessellation it is a model space distance.
    • m_dMax3DEdge [-4.0] Expected max EdgeLength to tessellate. When negative, compute dMax3DEdge based on box object BoundingBox size as compute dMax3DEdge based on box object
    • BoundingBox size as dMax3DEdge = -0.025 * BBox.Size * dMax3DEdge
    • m_dMaxAspectRatio [ 0.0] When negative, use viewBased surface tessellation, else use view independent surface tessellation.
    • m_ePolygonOutputType [IW_PO_TRIANGLES] Specify how NMTLib tessellator
    • m_bViewBasedTess [FALSE] TRUE = NOT USED YETCurvature and Derivative Combs and PinCushions
    • m_lSamplePointCount [25] Number of curve sample points
    • m_lUPointCount [10] Number of surface U Sample Points
    • m_lVPointCount [10] Number of surface V Sample Points
    • m_ePinCushionType [IW_DM_UNIT_NORMAL] Select various vector fields
    • m_dPinCushionScale [10.0] Scale applied to PinCushion Vectors
    • m_dCurvatureScale [-20.0] Scale applied to curvature combs

Line and Point Size

    • m_dLineWidth [2.0] number of pixels used to draw lines
    • m_dPointSize [4.0] number of pixels used to draw point icons
    • m_bDashedLines [FALSE] TRUE = draw crosshatched curves dashed

Application Colors

    • m_DefaultColor [0,0,0] normal object default color
    • m_DefaultShadingColor [.7,.7,.7] normal object default color used for Shaded Breps
    • m_InterruptColor [IW_BIG_DOUBLE..] when set to anything but IW_BIG_DOUBLE, overrides normal object color selection

Object Color, Line Width, and PointSize Management

When a display list is played back into the OpenGL pipeline its graphics are output using the current color, line width, and point size values up until the time when the display list itself sets these values. The NMTLib rule for building display lists is that color is always set prior to outputting geometry objects but that line width and point size are only set when a special size is needed. Whenever a line width or point size is set within a display list it is restored to its initial value when that line width or point size value is no longer needed.

Controlling Object Color in an Object DisplayList

The ui_test interface stores a default object color in s_Disp that is used to render all objects without a color specification. The default color can be changed at run time with the iwgfx_SetDefaultColor() function.

At run time, an object?s color is specified by a color attribute attached to the object or to the object?s owning Brep or PolyBrep. Attach a color attribute to an object or its containing Brep or PolyBrep with the calls,

  • IwVector3dAttribute *pColor = new(*s_pContext) IwVector3dAttribute( IW_AI_COLOR, s_VColor);
  • pObject->AddAttribute(pColor);

For debugging purposes, an object?s specified color can be overridden by setting an interrupt color stored in the s_Disp object with the commands,

  • iwgfx_SetColor()
  • iwgfx_ChangeColor(), and
  • iwgfx_ClearColor().

Sometimes drawn objects are rendered using a special application color such as vectors drawn for curvature combs and surface normals, or curves belonging to wire or lamina edges. Under these circumstances, when there is no interrupt or attribute color rather than using the default color for the object, use an application color instead.
The rule for determining which object color to use while building an object DisplayList is executed while building a DisplayList for an object and encoded into the function iwgfx_OutputObjectColor(IwColorRuleType eColorRule).

The rule is

  1. When s_Disp has a set interrupt color ? use it, else
  2. When the object or the object?s Brep or PolyBrep owner has a color attribute ? use it, else
  3. When eColorRule != IW_CR_STANDARD ? use appropriate application color, else
  4. Use the DefaultColor stored in the s_Disp object.

Note the difference between modifying the default color and the interrupt color. Objects are drawn with the interrupt color even when the object has a color attribute or is drawn with an application color rule. Objects are only drawn with the default color when the object does not have a color attribute and the object is not being drawn with an application color rule. Also note shaded objects do not use the default color, they use the default shaded color.
The iwgfx_OutputObjectColor() function outputs the selected object color to OpenGL with a glColor() call which causes the color change to be compiled into the currently open DisplayList so that the color change will execute at the appropriate time when the DisplayList is finally played back by a call to iwgfx_DrawLists(). Only call the iwgfx_OutputObjectColor() function from within object Draw() and OutputGraphics() methods.

Implementing Object Draw() and OutputGraphics() Methods

All Draw() methods open and add a new DisplayList to the global DisplayList array to contain all the draw commands used to render the object. The behavior of many Draw() functions depend on the values stored within s_Disp. Most Draw() methods only use the s_Disp values while others overwrite some of those values as they draw. Two functions exist to fetch the s_Disp values depending on if the parameters are only to be used or if they are to be overwritten. These functions are:

  • iwgfx_RefGlobalDisplayParameters Get s_Disp reference when using parameters without modifications.
  • iwgfx_GetGlobalDisplayParameters Get copy of s_Disp parameters, needed when the called Draw() method overwrites any of the s_Disp parameter values.

A typical Draw() method
void Object::Draw()

const

{

// locals: global display parameters

const IwDisplayParameters &rDisp = iwgfx_RefGlobalDisplayParameters();

// use following instead when view parameter modification is required

// IwDisplayParameters sDisp ;

// Iwgfx_GetGlobalDisplayParameters(sDisp) ;

// start new DisplayList (unless one is already open)

iwgfx_Open() ;

// set Object Color

IwVector3D sColor = iwgfx_OutputObjectColor

(this, rDisp.GetShadedColorRule()) ;

// output Object graphics

OutputGraphics(rDisp) ;

// restore color state and end new DisplayList

iwgfx_OutputColor(sColor) ;

iwgfx_Close() ;

} // end Object::Draw

Writing OutputGraphics() methods

The stream of color, line width, point size, and draw functions that render the object are placed within the OutputGraphics() method. Because a DisplayList is open, these calls do not get output to the graphics pipeline. Instead they get compiled into the open DisplayList to be played back into the graphics pipeline at some future time. Some objects with simple graphics don?t bother to isolate the render functions into a separate OutputGraphics() method choosing to place the draw function calls directly into the Draw() method.

Outputting the color, line width, and point size values is not done with the iwgfx_SetCalls() but with iwgfx_OutputCalls(). The iwgfx_SetCalls() modify the global values stored within s_Disp while the iwgfx_OutputCalls() push a value out to the OpenGL stream. The set of iwgfx_OutputCalls() includes:

  • iwgfx_OutputObjectColor()
  • iwgfx_OutputColor()
  • iwgfx_ChangeOutputColor()
  • iwgfx_OutputPointSize()
  • iwgfx_OutputLineWidth()
  • iwgfx_OutputDashedLines()

Once the color, line width, and point size parameters are set as desired output actual geometry shapes using the iwgfx_DrawCalls() including:

  • iwgfx_DrawPoint(),
  • iwgfx_DrawLine(),
  • iwgfx_DrawPolyLine(),
  • iwgfx_DrawTriangle(), and for drawing meshes
  • iwgfx_NewNurbsRenderer(),
  • iwgfx_NurbsSurface(), and
  • iwgfx_NurbsCurve().

Draw() and OutputGraphics() Architectural Rules

The NMTLib architecture rules for implementing Draw() and OutputGraphics() methods include:

  1. Never use the iwgfx_SetCalls() from within a Draw() or OutputGraphics() method. Abiding by this rule prevents one draw routine from modifying the behavior of other draw routines.
  2. The generalization of rule 1 is: never make a change to the global display parameters stored in s_Disp from a Draw() or OutputGraphics() method. Some OutputGraphics() methods want to override s_Disp global values. Always send such methods a copy of the s_Disp values retrieved with the iwgfx_GetGlobalDisplayParameters() function so that any overrides cannot have side effects on other draw routines.
  3. Avoid calling a Draw() method from a Draw() or OutputGraphics() method, instead make sure the desired object graphics is divided up into a Draw() and OutputGraphics() function and call the OutputGraphics() function directly. This rule is not required as long as all Draw() functions make sure that they make no permanent change to the color, line width, and point size state of the OpenGL pipeline and the values stored in s_Disp.
  4. Whenever the point size or line width is changed it must be changed back to its current value when the need for the new size is done. This allows draw functions to call other Draw() or OutputGraphics() methods without side effects.

DisplayLists in OpenGL, NMTLib, and ui_test

An openGL displayList is a compiled set of openGL commands that can be run repeatedly very quickly. One creates a displayList by

  • opening a new displayList
  • executing any set of openGL rendering commands
  • close displayList

All the executed openGL commands get compiled into a openGL secret format. Applications access the display through an ID, which is implemented as an integer value. The display is rendered by sending the displayList ID to the openGl call, glCallList(DisplayListID).
NMTLib rendering is designed to build and store displaylists while keeping color, line width, point size, and dashed line pattern information outside of the list so that an application can vary those without having to rebuild the displaylist. This makes it possible for NMTlib rendering to support highlighting and the like at interactive rates. One makes iwgfx_Calls() which encapsulate the openGl calls.

NMTLib does this by creating and managing its own display lists in objects called IwDisplayList. The IwDisplayList stores the openGL DisplayList Id and color and draw size parameters. NMTLib stores all the the displayLists in a global array and uses the function iwgfx_DrawLists() to render those. Prior to asking openGL to render the openGL display list the iwgfx_DrawLists() funtion makes openGL calls to set color and draw size as

  • iwgfx_OutputPointSize(rDisplayList.GetPointSize()) ;
  • iwgfx_OutputLineWidth(rDisplayList.GetLineWidth()) ;
  • iwgfx_OutputDashedLines(rDisplayList.GetDashedLine()) ;
  • iwgfx_OutputColor(rDisplayList.GetColor()) ;
  • // output DisplayList
  • GLuint lDisplayListId = rDisplayList.GetDisplayListId() ;
  • glCallList(lDisplayListId); IW_GL_DUMP("glCallList", lDisplayListId);

All one needs to do to change a color or draw size is to modify the rDisplayList member value and leave it to iwgfx_DrawLists() to get that information out at display time.
NMTLib's list of IwDisplayLists is stored in an instance of the class IwViewParameters. This instance should be created and stored by the ui-test application as part of the IwView object. However, due to evolutionary reasons, NMTlib creates one global IwViewParameters object called s_View.

Applications need to control which color and draw sizes are stored in the IwDisplayList objects when the objects are created and when an application wants to change those values.

IwDisplayLists are created and stored in the global list by the call iwgfx_Open(). the current behavior for iwgfx_Open() is to initialize the stored color and draw sizes from values fetched from a passed in color value and the calls,

  • iwgfx_GetLineWidth() and
  • iwgfx_GetPointSize().

The values fetched by iwgfx_GetLineWidth() and iwgfx_GetPointSize() come from a second global graphics value called s_Disp, an instance of IwDisplayParameters. The IwDisplayParameters class is a long list of all the object rendering values that are displayed in ui_test. Values in s_Disp can be set with iwgfx_calls. A typical calling sequence to store the desired color and draw size parameters in an IwDisplayList are:

  • // set pointsize = 1, linesize = 2, color = 0.0, 0.0, 1.0 in global s_Disp object.
  • iwgfx_SetLook(1, 2, 0,0,1) ;
  • // create an IwDisplayList using values fetched from s_Disp and store it in the displayLists array within the global s_View object.
  • iwgfx_Open() ;
  • . . . gl Draw commands get compiled into the drawlist . . .
  • perhaps: pBrep->Draw() ;
  • // closes the openGl display list
  • iwgfx_Close();

New behaviors

The iwgfx_Open() method has been modified to take optional color, line width, and point size arguments. If used, these are stored in the IwDisplayList created and placed on the global displayList array instead of using the ones stored in s_Disp. This allows a special draw function to use special colors and draw sizes rather than the general ones. I used this feature when creating a drawList for the new IwGap object. An example call might look like

  • // start new displayList (unless one is already open)
  • // use gap color and draw sizes rather than default ones
  • double dLineWidth = iwgfx_GetGapLineWidth() ;
  • double dPointSize = iwgfx_GetGapPointSize() ;
  • IwVector3d &rGapColor = iwgfx_GetRuleColor(NULL, IW_CR_GAP) ;
  • iwgfx_Open(rGapColor, &dLineWidth, &dPointSize);

Later when iwgfx_DrawLists() is called, the drawList created by the above call will be rendered after setting the color, line width and Point size values as specified above.
For highlight purposes one will want to fetch the IwDisplayList object stored in the global array and modify the stored colors and draw sizes. This is a little tricky only because the drawlists on the global list array are stored by value, not by reference.

The new function iwgfx_GetDisplayList() returns a pointer to the IwDisplayList taking a GLuint Id value. So to support highlighting in application one has to manage the following challenges:

  1. Some kind of I/O that fetches the IwDisplayList Id value.
  2. IwDisplayList *pDisplayList = iwgfx_GetDisplayList(lDisplayListId) ;
  3. Some combination of pDisplayList->SetColor(), pDisplayList->SetPointSize(), pDisplayList->SetLineWidth() calls.
  4. Redisplay all lists with iwgfx_DrawLists().
Last updated on Apr 29, 2024.