Computer Graphics Workshop '97 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 root (new-SoSeparator))
(send root 'ref)

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

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

(set-mfield-values! (send 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! (send tri-strip-set 'numVertices) 0
		    '(4 4 4))

(define viewer (examiner 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))
(send root 'ref)
(define coords (new-SoCoordinate3))
(send root 'addChild coords)
(set-mfield-values! (send 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))
(send root 'addChild line-set)
(set-mfield-values! (send 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

(send (send 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. Here's an example of building a ship (four polygons) out of an indexed face set:

;; Ship geometry must have a bounding cube of
;; ((* *ship-geometry-bbox* -1.0)..*ship-geometry-bbox*)
;; in every dimension
(define *ship-geometry-bbox* 1.0)
(define *ship-geometry* (new-SoSeparator))
(send *ship-geometry* 'ref)

(let* ((coords (new-SoCoordinate3))
       (sf (new-SoScale))
       (hints (new-SoShapeHints))
       (color (new-SoBaseColor))
       (mbind (new-SoMaterialBinding))
       (ifs (new-SoIndexedFaceSet))

       (ship-top (new-SbVec3f '#(0 0.6 0.8)))
       (ship-width 0.8)
       (ship-left (new-SbVec3f `#(,(* -1.0 ship-width)
       (ship-right (new-SbVec3f `#(,ship-width
       (ship-front (new-SbVec3f 0 0 -1)))
  ;; 4 triangles
  (set-mfield-values! (send coords 'point) 0
  (set-mfield-values! (send ifs 'coordIndex) 0
		      `(0 1 3 ,SO_END_FACE_INDEX
			0 2 1 ,SO_END_FACE_INDEX))
  (set-mfield-values! (send ifs 'materialIndex) 0
		      `(0 1 0 2))
  (set-mfield-values! (send color 'rgb) 0
		      '(#(0.2 0.2 0.8)
			#(0.8 0.2 0.2)
			#(0.6 0.6 0.6)))
  (send (send sf 'scaleFactor) 
	'setValue *ship-geometry-bbox*
  (send (send hints 'vertexOrdering)
	'setValue SoShapeHints::COUNTERCLOCKWISE)
  (send (send hints 'shapeType)
	'setValue SoShapeHints::SOLID)
  (send (send mbind 'value) 'setValue
  (addChildren *ship-geometry* sf hints coords mbind color ifs))

(define v (examiner *ship-geometry*))
Some useful Inventor Mentor examples which illustrate the creation of shapes using the various geometry nodes are 05.1.FaceSet, 05.2.IndexedFaceSet, 05.3.TriangleStripSet, 05.4.QuadMesh, and 05.5.Binding.

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))
(send (send 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
(send (send coords 'point) 'set1Value 0 '#(2 3 4))
(send (send 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 (send (send 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, (send 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))
(send (send 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 (send (send coords 'point) 'operator-brackets 10))
(send my-vec 'getValue)
There are situations in which you would want the number of accessed values in a field to decrease rather than increase; for example, if you decrease the resolution of the torus in problem set 3, you will compute fewer points than before, but the point field of the Coordinate3 node will still contain the old (large) number of points (again, because MFields automatically expand but do not shrink). To change the size of an MField manually, use the setNum method:
(send (send coords 'point) 'setNum 3)
This tells the point field that there are three valid values in it, with indices 0, 1, and 2.

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 '97 home page

$Id: index.html,v 1.16 1997/01/16 01:50:04 kbrussel Exp $