Computer Graphics Workshop '96 Lecture Notes


Today's topics
Vertex based shapes - completion

Last lecture we observed how to construct a face set out of a Coordinate3 node plus a FaceSet node. This lecture we will example some of the other types of vertex based shapes.

Recall that a face set node fundamentally only tells Inventor how to "bind together" the coordinates povided by another node. A face set can represent multiple planar polygons, either convex or concave. (If you give Inventor non-planar vertices and tell it to construct a polygon for them using a face set, the result will be an arbitrary triangular tesselation of those vertices.)

A triangle strip set is another shape which has more constraints than a face set. As the name implies, it creates a strip of triangles between points alternating between the top and bottom of the strip. Here's an example of how one of these is created:

    |    /|    /|
    |   / |   / |
    |  /  |  /  |
    | /   | /   |
    |/    |/    |
Vertices 0 through 5 would be specified inside a Coordinate3 node. The triangle strip set would then have "6" as the sole value in its numVertices field.

Triangle strip sets have the advantage of rendering faster than face sets; however, they can not represent geometry as general as face sets can.

The final type of polygonal shape in Inventor is the quad mesh; this node constructs quadrilaterals out of the provided coordinates by considering the vertices to be points along the rows of the mesh. While a triangle strip set can represent all the shapes a quad mesh can, a quad mesh can be convenient when you can easily represent your shape as some distortion of a flat sheet of points.

Here's a simple example of a triangle strip set:

(define viewer (new-SoXtExaminerViewer))
(-> viewer 'show)

(define root (new-SoSeparator))
(-> root 'ref)

(define coords (new-SoCoordinate3))
(-> root 'addChild coords)

(define tri-strip-set (new-SoTriangleStripSet))
(-> root 'addChild tri-strip-set)

(set-mfield-values! (-> coords 'point) 0
		    '(#(0 1 0)
		      #(0 0 0)
		      #(1 1 0)
		      #(1 0 0)
		      #(2 1 0)
		      #(2 0 0)
		      #(3 1 0)
		      #(3 0 0)
		      #(4 1 0)
		      #(4 0 0)
		      #(5 1 0)
		      #(5 0 0)
		      #(6 1 0)
		      #(6 0 0)
		      #(7 1 0)
		      #(7 0 0)
		      #(8 1 0)
		      #(8 0 0)
		      #(9 1 0)
		      #(9 0 0)
		      #(10 1 0)
		      #(10 0 0)
		      #(11 1 0)
		      #(11 0 0)
		      #(12 1 0)
		      #(12 0 0)
		      #(13 1 0)
		      #(13 0 0)))

(set-mfield-values! (-> tri-strip-set 'numVertices) 0
		    '(4 4 4))

(-> viewer 'setSceneGraph root)
Inventor can represent 3D points and lines as well as filled polygons. An SoLineSet constructs polylines out of the given coordinates. For example:
(define root (new-SoSeparator))
(-> root 'ref)
(define coords (new-SoCoordinate3))
(-> root 'addChild coords)
(set-mfield-values! (-> coords 'point) 0
  '(#(0 0 0)
    #(0 1 0)
    #(1 0 0)
    #(1 1 0)
    #(2 0 0)
    #(2 1 0)))
(define line-set (new-SoLineSet))
(-> root 'addChild line-set)
(set-mfield-values! (-> line-set 'numVertices) 0
  '(4 2))
Note that the first four points are connected in a polyline, while the second two points are disconnected from them.

The SoPointSet works in a similar way to all the above shapes, but has a single-valued numPoints field instead of a numVertices field since points do not have to be grouped together.

The SoMaterial node affects the coloring for line sets and point sets as well as for other vertex-based and primitive shapes. However, there are additional parameters for lines and points: for example, how thick should the lines be? The line thickness for a point set can be specified by adding an SoDrawStyle node to the scene graph. This node has floating-point fields for setting the point size (pointSize) and the line width (lineWidth); these values are measured in "printer's points" (1 inch = 72.27 printer's points). If this node were inserted in front of the line-set node in the above example, we could make the lines thicker by writing

(-> (-> draw-style 'lineWidth) 'setValue 2.0)
This node also contains a field, style, which changes the drawing style for all shapes, not just vertex-based shapes. By setting this field's value to SoDrawStyle::LINES, for example, you could create a wireframe view of a cone.

All of the previously described vertex-based shapes have been "non-indexed"; that is, all of the coordinates of the desired shape must appear in order in the Coordinate3 node. In some cases, this can lead to repetition in the specification of vertices. Face sets, line sets, and triangle strip sets have indexed analogues (SoIndexedFaceSet, SoIndexedLineSet, SoIndexedTriangleStripSet) in which the values specified in these nodes are indices into the array of coordinates, material properties, etc. rather than numbers of vertices per part. However, since the same results can be obtained by using the non-indexed versions of these nodes, we leave the description of the usage of these nodes to the Inventor Mentor.

Fields - reprise

Single vs. multiple-valued fields

As we discussed in the second lecture, fields can either contain one value of a certain type (prefix "SoSF") or multiple values of that type ("SoMF"). The access functions for the two types of fields differ. Single valued fields can have their values changed by the setValue method; in Scheme, multiple valued fields can only have one of their values changed at a time by using the set1Value method. An example of this is
(define coords (new-SoCoordinate3))
(-> (-> coords 'point) 'set1Value 0 2 3 4)
The set1Value method takes an index (starting at 0) and the new value for that slot in the array; some of the field types (like SoMFVec3f, the type of the point field in the SoCoordinate3 node) have additional convenience functions to allow the new value to be expressed in several different ways. For example, we could have written the last line above as
(-> (-> coords 'point) 'set1Value 0 '#(2 3 4))
(-> (-> coords 'point) 'set1Value 0 (new-SbVec3f 2 3 4))
Because single-valued fields can only store one value, they all use the getValue method to return the value they contain. Multiple-valued fields do not have a "get1Value" method defined; instead, the C++ syntax for getting one value out of an mfield uses the brackets operator:
const SbVec3f &myVec = coords->point[3];
The conversion of this syntax to Scheme is the following: (define my-vec (-> (-> coords 'point) 'operator-brackets 3)) In other words, the brackets operator is converted into a method of all multiple-valued fields.

We have defined a convenience function, set-mfield-values! (defined in InventorInit.scm), which sets multiple values in an mfield for you. It takes as arguments, in order, the following:

  • the field to be changed (for example, (-> coords 'point))
  • the starting index of the values to follow (usually 0 - the first index is 0)
  • a list of values. Each element in the list must be a valid value for a slot in the new field. For example, for an SoMFVec3f this list must contain either Scheme vectors of length 3 or SbVec3fs.

Number of elements in an mfield

The set1Value method in mfields automatically takes care of memory allocation; that is, the space within the field is expanded to (at least) the largest accessed index.
(define coords (new-SoCoordinate3))
(-> (-> coords 'point) 'set1Value 10 '#(3 4 5))
The point field now has 11 slots in it (remember, in Inventor, as in C, all indices start at 0). The slots numbered 0-9 have undefined values stored in them; the one with index 10 has the vector (3,4,5) stored in it:
(define my-vec (-> (-> coords 'point) 'operator-brackets 10))
(-> my-vec 'getValue)
There are situations in which you would want the number of accessed values in a field to decrease rather than increase; perhaps your application culls a large portion of geometry before manipulating the on-screen geometry. In this case your application might use the set1Value method to copy the new geometry into a Coordinate3 node; however, the node would still consider the largest-accessed index to be the largest valid index. To change this value, use the setNum method:
(-> (-> coords 'point) 'setNum 3)
This tells the point field that there are three valid values in it, with indices 0, 1, and 2.

Open Inventor keeps track of which nodes or fields have changed recently through a process known as notification. It then performs appropriate actions, such as re-rendering the scene graph. This is why when you add shapes to the scene graph using Scheme, the picture in the viewer updates automatically.

Unfortunately, when many changes are made to a single field which is contained within a node that is part of the active scene graph, the notification process can become slow. We therefore will demonstrate how to disable and reenable this process. Note that you should only use these functions in the following situations:

  • the field you are changing is part of a node that is being viewed in a viewer
  • you are changing many values in this field
Let's say that we have 500 coordinates stored in a Coordinate3 node that our application is changing in some way. Then we might do the following. First, disable notification by using the enableNotify method of the point field:
(-> (-> coords 'point) 'enableNotify 0)
The enableNotify method takes an SbBool as argument; recall that SbBools are represented in Scheme as integer values of 0 and 1 for FALSE and TRUE, respectively. Once notification is disabled, we can change the values in the field. When we have finished changing the values, we must re-enable notification:
(-> (-> coords 'point) 'enableNotify 1)
If this were the only node we were modifying, we might now want to schedule a render of the scene graph; since notification was disabled during the modification we just made, Inventor would still be using the old values of the field. We therefore tell Inventor that new values are in the field:
(-> (-> coords 'point) 'touch)
Note again that especially in Scheme there is so much other overhead that the notification process will not be the limiting factor in the performance of your applications. However, when you are writing interactive Inventor applications in C++ which deal with a lot of data, disabling and reenabling the notification process can provide more control over the behavior of your Inventor application.

Next lecture

Next time we will begin to discuss the tools and programming structures necessary for creating a fully interactive Inventor application.

Back to the CGW '96 home page

$Id: index.html,v 1.5 1996/01/16 21:23:16 kbrussel Exp $