5.2. Complex Shapes

Complex shapes, such as triangle strip sets and face sets, require at least a set of coordinates. If the lighting is set to PHONG, complex shapes also require a set of surface normals, as shown in Figure 5.2, “ Nodes Used to Create a Simple Indexed Face Set. Coordinates and normals are defined by separate nodes in the scene graph so that this information can be shared by other nodes.

Examples of complex shapes include the following:

An SoCoordinate3 SoCoordinate3 SoCoordinate3 node sets the current coordinates in the rendering state to the specified points. This node contains one field (point), which is of type SoMFVec3f SoMFVec3f SoMFVec3f . For example:

SbVec3f 				verts[6];
SoCoordinate3			 *coord = new SoCoordinate3;
// ...Initialize vertices array ...
coord->point.setValues(0, 6, verts);
  
SbVec3f[] verts = new SbVec3f[6];
SoCoordinate3	coord = new SoCoordinate3();
// ...Initialize vertices array ...
coord.point.SetValues(0, verts);
  
SbVec3f[] verts = new SbVec3f[6];
SoCoordinate3	coord = new SoCoordinate3();
// ...Initialize vertices array ...
coord.point.setValues(0, verts);
  

An SoNormal SoNormal SoNormal node sets the current surface normals in the rendering state to the specified vectors. This node contains one field, vector, of type SoMFVec3f SoMFVec3f SoMFVec3f .

[Tip]

Tip: Normals can also be generated automatically by Inventor, in which case you do not need an SoNormal SoNormal SoNormal node. See the section called “Generating Normals Automatically” for further information.

Nodes Used to Create a Simple Indexed Face Set

Figure 5.2.  Nodes Used to Create a Simple Indexed Face Set


An SoFaceSet SoFaceSet SoFaceSet is a shape node that represents a polygonal object formed by constructing faces out of the current coordinates, current normals, current materials, and current textures. It uses the values within each node in the order they are given. (To use coordinates, normals, and materials in a different order, use the SoIndexedFaceSet SoIndexedFaceSet SoIndexedFaceSet node, described in the next section.)

Example 5.1, “ Creating a Face Set creates an obelisk using a face set composed of eight faces. The scene graph for this example is shown in Figure 5.3, “ Scene Graph for Face Set Example. Ignore the normal binding node for now. This node is explained in Section 5.4, “Binding Nodes”Figure 5.4, “ Face-Set Example”shows the image created by this example.



Example 5.1.  Creating a Face Set

//  Eight polygons. The first four are triangles 
//  The second four are quadrilaterals for the sides.
static float vertices[28][3] =
{
   { 0, 30, 0}, {-2,27, 2}, { 2,27, 2},            //front tri
   { 0, 30, 0}, {-2,27,-2}, {-2,27, 2},            //left  tri
   { 0, 30, 0}, { 2,27,-2}, {-2,27,-2},            //rear  tri
   { 0, 30, 0}, { 2,27, 2}, { 2,27,-2},            //right tri
   {-2, 27, 2}, {-4,0, 4}, { 4,0, 4}, { 2,27, 2},  //front quad
   {-2, 27,-2}, {-4,0,-4}, {-4,0, 4}, {-2,27, 2},  //left  quad
   { 2, 27,-2}, { 4,0,-4}, {-4,0,-4}, {-2,27,-2},  //rear  quad
   { 2, 27, 2}, { 4,0, 4}, { 4,0,-4}, { 2,27,-2}   //right quad
};

// Number of vertices in each polygon:
static long numvertices[8] = {3, 3, 3, 3, 4, 4, 4, 4};

// Normals for each polygon:
static float norms[8][3] =
{ 
   {0, .555,  .832}, {-.832, .555, 0}, //front, left tris
   {0, .555, -.832}, { .832, .555, 0}, //rear, right tris
   {0, .0739,  .9973}, {-.9972, .0739, 0},//front, left quads
   {0, .0739, -.9973}, { .9972, .0739, 0},//rear, right quads
};

SoSeparator *
makeObeliskFaceSet()
{
   SoSeparator *obelisk = new SoSeparator();
   obelisk->ref();

   // Define the normals used:
   SoNormal *myNormals = new SoNormal;
   myNormals->vector.setValues(0, 8, norms);
   obelisk->addChild(myNormals);
   SoNormalBinding *myNormalBinding = new SoNormalBinding;
   myNormalBinding->value = SoNormalBinding::PER_FACE;
   obelisk->addChild(myNormalBinding);

   // Define material for obelisk
   SoMaterial *myMaterial = new SoMaterial;
   myMaterial->diffuseColor.setValue(.4, .4, .4);
   obelisk->addChild(myMaterial);

   // Define coordinates for vertices
   SoCoordinate3 *myCoords = new SoCoordinate3;
   myCoords->point.setValues(0, 28, vertices);
   obelisk->addChild(myCoords);

   // Define the FaceSet
   SoFaceSet *myFaceSet = new SoFaceSet;
   myFaceSet->numVertices.setValues(0, 8, numvertices);
   obelisk->addChild(myFaceSet);

   obelisk->unrefNoDelete();
   return obelisk;
}
      
//  Eight polygons. The first four are triangles 
//  The second four are quadrilaterals for the sides.
float[,]
vertices = new float[28, 3] {
    { 0.0f, 30.0f, 0.0f}, {-2.0f,27.0f, 2.0f}, { 2.0f,27.0f, 2.0f}, // front tri
    { 0.0f, 30.0f, 0.0f}, {-2.0f,27.0f,-2.0f}, {-2.0f,27.0f, 2.0f}, // left  tri
    { 0.0f, 30.0f, 0.0f}, { 2.0f,27.0f,-2.0f}, {-2.0f,27.0f,-2.0f}, // rear  tri
    { 0.0f, 30.0f, 0.0f}, { 2.0f,27.0f, 2.0f}, { 2.0f,27.0f,-2.0f}, // right tri
    {-2.0f, 27.0f, 2.0f}, {-4.0f, 0.0f, 4.0f}, 
    { 4.0f,  0.0f, 4.0f}, { 2.0f,27.0f, 2.0f}, // front quad
    {-2.0f, 27.0f,-2.0f}, {-4.0f, 0.0f,-4.0f},
    {-4.0f,  0.0f, 4.0f}, {-2.0f,27.0f, 2.0f}, // left  quad
    { 2.0f, 27.0f,-2.0f}, { 4.0f, 0.0f,-4.0f},
    {-4.0f,  0.0f,-4.0f}, {-2.0f,27.0f,-2.0f}, // rear  quad
    { 2.0f, 27.0f, 2.0f}, { 4.0f, 0.0f, 4.0f},
    { 4.0f,  0.0f,-4.0f}, { 2.0f,27.0f,-2.0f}  // right quad
};

// Number of vertices in each polygon:
Int32[] numvertices = new Int32[8] { 3, 3, 3, 3, 4, 4, 4, 4 };

// Normals for each polygon:
float[,] norms = new float[8, 3] { 
    {0.0f, 0.555f, 0.832f}, {-0.832f, 0.555f, 0.0f}, // front, left tris
    {0.0f, 0.555f,-0.832f}, { 0.832f, 0.555f, 0.0f}, // rear, right tris

    {0.0f, 0.0739f, 0.9973f}, {-0.9972f, 0.0739f, 0.0f}, // front, left quads
    {0.0f, 0.0739f,-0.9973f}, { 0.9972f, 0.0739f, 0.0f}, // rear, right quads
};

SoSeparator MakeObeliskFaceSet()
{
    SoSeparator obelisk = new SoSeparator();

    // Using the new SoVertexProperty node is more efficient
    SoVertexProperty myVertexProperty = new SoVertexProperty();

    // Define the normals used:
    myVertexProperty.normal.SetValues(0, 8, norms);
    myVertexProperty.normalBinding.Value = SoNormalBinding.Bindings.PER_FACE;

    // Define material for obelisk
    myVertexProperty.orderedRGBA.SetValue(new SbColor(0.4f, 0.4f, 0.4f).GetPackedValue());

    // Define coordinates for vertices
    myVertexProperty.vertex.SetValues(0, 28, vertices);

    // Define the FaceSet
    SoFaceSet myFaceSet = new SoFaceSet();
    myFaceSet.numVertices.SetValues(0, numvertices);

    myFaceSet.vertexProperty.Value = myVertexProperty;
    obelisk.AddChild(myFaceSet);

    return obelisk;
}
    
//Eight polygons. The first four are triangles 
//The second four are quadrilaterals for the sides.
float[][] vertices = new float[][] {
  { 0.0f, 30.0f, 0.0f}, {-2.0f,27.0f, 2.0f}, { 2.0f,27.0f, 2.0f}, // front tri
  { 0.0f, 30.0f, 0.0f}, {-2.0f,27.0f,-2.0f}, {-2.0f,27.0f, 2.0f}, // left  tri
  { 0.0f, 30.0f, 0.0f}, { 2.0f,27.0f,-2.0f}, {-2.0f,27.0f,-2.0f}, // rear  tri
  { 0.0f, 30.0f, 0.0f}, { 2.0f,27.0f, 2.0f}, { 2.0f,27.0f,-2.0f}, // right tri
  {-2.0f, 27.0f, 2.0f}, {-4.0f, 0.0f, 4.0f}, 
  { 4.0f,  0.0f, 4.0f}, { 2.0f,27.0f, 2.0f}, // front quad
  {-2.0f, 27.0f,-2.0f}, {-4.0f, 0.0f,-4.0f},
  {-4.0f,  0.0f, 4.0f}, {-2.0f,27.0f, 2.0f}, // left  quad
  { 2.0f, 27.0f,-2.0f}, { 4.0f, 0.0f,-4.0f},
  {-4.0f,  0.0f,-4.0f}, {-2.0f,27.0f,-2.0f}, // rear  quad
  { 2.0f, 27.0f, 2.0f}, { 4.0f, 0.0f, 4.0f},
  { 4.0f,  0.0f,-4.0f}, { 2.0f,27.0f,-2.0f}  // right quad
};

//Number of vertices in each polygon:
int[] numvertices = new int[] { 3, 3, 3, 3, 4, 4, 4, 4 };

//Normals for each polygon:
float[][] norms = new float[][] { 
  {0.0f, 0.555f, 0.832f}, {-0.832f, 0.555f, 0.0f}, // front, left tris
  {0.0f, 0.555f,-0.832f}, { 0.832f, 0.555f, 0.0f}, // rear, right tris

  {0.0f, 0.0739f, 0.9973f}, {-0.9972f, 0.0739f, 0.0f}, // front, left quads
  {0.0f, 0.0739f,-0.9973f}, { 0.9972f, 0.0739f, 0.0f}, // rear, right quads
};

SoSeparator MakeObeliskFaceSet()
{
  SoSeparator obelisk = new SoSeparator();

  // Using the new SoVertexProperty node is more efficient
  SoVertexProperty myVertexProperty = new SoVertexProperty();

  // Define the normals used:
  myVertexProperty.normal.setValues(0, norms);
  myVertexProperty.normalBinding.setValue(SoNormalBinding.Bindings.PER_FACE);

  // Define material for obelisk
  myVertexProperty.orderedRGBA.setValue(new SbColor(0.4f, 0.4f, 0.4f).getPackedValue());

  // Define coordinates for vertices
  myVertexProperty.vertex.setValues(0, vertices);

  // Define the FaceSet
  SoFaceSet myFaceSet = new SoFaceSet();
  myFaceSet.numVertices.setValues(0, numvertices);

  myFaceSet.vertexProperty.setValue(myVertexProperty);
  obelisk.addChild(myFaceSet);

  return obelisk;
}
    

[Tip]

Tip: When you construct a scene graph, be sure that you have used as few nodes as possible to accomplish your goals. For example, to create a multifaceted polygonal shape, it's best to put all the coordinates for the shape into one SoCoordinate node and put the description of all the face sets into a single SoFaceSet SoFaceSet SoFaceSet (or SoIndexedFaceSet SoIndexedFaceSet SoIndexedFaceSet ) node rather than using multiple nodes for each face.

An SoIndexedFaceSet SoIndexedFaceSet SoIndexedFaceSet node is a shape node that represents a polygonal object formed by constructing faces out of the current coordinates, using the current surface normals, current materials, and current texture. In contrast to the SoFaceSet SoFaceSet SoFaceSet node, this node can use those values in any order. This node class contains four fields with indices that specify the ordering:

Be sure that the indices contained in the indexed face set can actually be found in the coordinates and normals lists, or errors will occur.

Example 5.2, “ Creating an Indexed Face Set creates the first stellation of the dodecahedron from an indexed face set. Each of the twelve intersecting faces is a pentagon. The scene graph diagram for this example is shown in Figure 5.5, “ Scene Graph for Indexed Face-Set Example. Figure 5.6, “ Indexed Face-Set Example” shows the image created by this example.


Example 5.2.  Creating an Indexed Face Set

// Positions of all of the vertices:
static float vertexPositions[12][3] =
{
   { 0.0000,  1.2142,  0.7453},  // top

   { 0.0000,  1.2142, -0.7453},  // points surrounding top
   {-1.2142,  0.7453,  0.0000},
   {-0.7453,  0.0000,  1.2142}, 
   { 0.7453,  0.0000,  1.2142}, 
   { 1.2142,  0.7453,  0.0000},

   { 0.0000, -1.2142,  0.7453},  // points surrounding bottom
   {-1.2142, -0.7453,  0.0000}, 
   {-0.7453,  0.0000, -1.2142},
   { 0.7453,  0.0000, -1.2142}, 
   { 1.2142, -0.7453,  0.0000}, 

   { 0.0000, -1.2142, -0.7453}, // bottom
};
        
// Positions of all of the vertices:
float[,] vertexPositions = new float[12, 3] {
    { 0.0000f,  1.2142f,  0.7453f},  // top

    { 0.0000f,  1.2142f, -0.7453f},  // points surrounding top
    {-1.2142f,  0.7453f,  0.0000f},
    {-0.7453f,  0.0000f,  1.2142f}, 
    { 0.7453f,  0.0000f,  1.2142f}, 
    { 1.2142f,  0.7453f,  0.0000f},

    { 0.0000f, -1.2142f,  0.7453f},  // points surrounding bottom
    {-1.2142f, -0.7453f,  0.0000f}, 
    {-0.7453f,  0.0000f, -1.2142f},
    { 0.7453f,  0.0000f, -1.2142f}, 
    { 1.2142f, -0.7453f,  0.0000f}, 

    { 0.0000f, -1.2142f, -0.7453f}, // bottom
};
      
//Positions of all of the vertices:
float[][] vertexPositions = new float[][] {
    { 0.0000f,  1.2142f,  0.7453f},  // top

    { 0.0000f,  1.2142f, -0.7453f},  // points surrounding top
    {-1.2142f,  0.7453f,  0.0000f},
    {-0.7453f,  0.0000f,  1.2142f}, 
    { 0.7453f,  0.0000f,  1.2142f}, 
    { 1.2142f,  0.7453f,  0.0000f},

    { 0.0000f, -1.2142f,  0.7453f},  // points surrounding bottom
    {-1.2142f, -0.7453f,  0.0000f}, 
    {-0.7453f,  0.0000f, -1.2142f},
    { 0.7453f,  0.0000f, -1.2142f}, 
    { 1.2142f, -0.7453f,  0.0000f}, 

    { 0.0000f, -1.2142f, -0.7453f}, // bottom
};
      


// Connectivity, information; 12 faces with 5 vertices each },
// (plus the end-of-face indicator for each face):

static long indices[72] =
{
    1, 2,  3,  4, 5, SO_END_FACE_INDEX, // top face

    0, 1,  8,  7, 3, SO_END_FACE_INDEX, // 5 faces about top
    0, 2,  7,  6, 4, SO_END_FACE_INDEX,
    0, 3,  6, 10, 5, SO_END_FACE_INDEX,
    0, 4, 10,  9, 1, SO_END_FACE_INDEX,
    0, 5,  9,  8, 2, SO_END_FACE_INDEX, 

    9,  5, 4, 6, 11, SO_END_FACE_INDEX, // 5 faces about bottom
   10,  4, 3, 7, 11, SO_END_FACE_INDEX,
    6,  3, 2, 8, 11, SO_END_FACE_INDEX,
    7,  2, 1, 9, 11, SO_END_FACE_INDEX,
    8,  1, 5,10, 11, SO_END_FACE_INDEX,

    6,  7, 8, 9, 10, SO_END_FACE_INDEX, // bottom face
};
 
// Colors for the 12 faces
static float colors[12][3] =
{
   {1.0, .0, 0}, { .0,  .0, 1.0}, {0, .7,  .7}, { .0, 1.0,  0},
   { .7, .7, 0}, { .7,  .0,  .7}, {0, .0, 1.0}, { .7,  .0, .7},
   { .7, .7, 0}, { .0, 1.0,  .0}, {0, .7,  .7}, {1.0,  .0,  0}
};

// Routine to create a scene graph representing a dodecahedron
SoSeparator *
makeStellatedDodecahedron()
{
   SoSeparator *result = new SoSeparator;
   result->ref();

   // Define colors for the faces
   SoMaterial *myMaterials = new SoMaterial;
   myMaterials->diffuseColor.setValues(0, 12, colors);
   result->addChild(myMaterials);
   SoMaterialBinding *myMaterialBinding = new SoMaterialBinding;
   myMaterialBinding->value = SoMaterialBinding::PER_FACE;
   result->addChild(myMaterialBinding);

   // Define coordinates for vertices

   // Define coordinates for vertices
   SoCoordinate3 *myCoords = new SoCoordinate3;
   myCoords->point.setValues(0, 12, vertexPositions);
   result->addChild(myCoords);

   // Define the IndexedFaceSet, with indices into the vertices:
   SoIndexedFaceSet *myFaceSet = new SoIndexedFaceSet;
   myFaceSet->coordIndex.setValues(0, 72, indices);
   result->addChild(myFaceSet);

   result->unrefNoDelete();
   return result;
}
      
// Connectivity, information; 12 faces with 5 vertices each },
// (plus the end-of-face indicator for each face):

Int32[] indices = new Int32[72]
{
    1, 2, 3, 4, 5, -1, // top face

    0, 1, 8, 7, 3, -1, // 5 faces about top
    0, 2, 7, 6, 4, -1,
    0, 3, 6,10, 5, -1,
    0, 4,10, 9, 1, -1,
    0, 5, 9, 8, 2, -1, 

    9, 5, 4, 6,11, -1, // 5 faces about bottom
   10, 4, 3, 7,11, -1,
    6, 3, 2, 8,11, -1,
    7, 2, 1, 9,11, -1,
    8, 1, 5,10,11, -1,

    6, 7, 8, 9,10, -1, // bottom face
};

// Colors for the 12 faces
float[,] colors = new float[12, 3]
{
    { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, 
    { 0.0f, 0.7f, 0.7f}, { 0.0f, 1.0f, 0.0f},
    { 0.7f, 0.7f, 0.0f}, { 0.7f, 0.0f, 0.7f}, 
    { 0.0f, 0.0f, 1.0f}, { 0.7f, 0.0f, 0.7f},
    { 0.7f, 0.7f, 0.0f}, { 0.0f, 1.0f, 0.0f}, 
    { 0.0f, 0.7f, 0.7f}, { 1.0f, 0.0f, 0.0f}
};

// Routine to create a scene graph representing a dodecahedron
SoSeparator makeStellatedDodecahedron()
{
    SoSeparator result = new SoSeparator();

    // Using the new SoVertexProperty node is more efficient
    SoVertexProperty myVertexProperty = new SoVertexProperty();

    // Define colors for the faces
    for (int i = 0; i < 12; i++)
        myVertexProperty.orderedRGBA[i] = 
          new SbColor(colors[i, 0], colors[i, 1], colors[i, 2]).GetPackedValue();
    myVertexProperty.materialBinding.Value = SoMaterialBinding.Bindings.PER_FACE;

    // Define coordinates for vertices
    myVertexProperty.vertex.SetValues(0, 12, vertexPositions);

    // Define the IndexedFaceSet, with indices into
    // the vertices:
    SoIndexedFaceSet myFaceSet = new SoIndexedFaceSet();
    myFaceSet.coordIndex.SetValues(0, indices);

    myFaceSet.vertexProperty.Value = myVertexProperty;
    result.AddChild(myFaceSet);

    return result;
}
    
//Connectivity, information; 12 faces with 5 vertices each },
//(plus the end-of-face indicator for each face):

int[] indices = new int[]
{
   1, 2, 3, 4, 5, -1, // top face

   0, 1, 8, 7, 3, -1, // 5 faces about top
   0, 2, 7, 6, 4, -1,
   0, 3, 6,10, 5, -1,
   0, 4,10, 9, 1, -1,
   0, 5, 9, 8, 2, -1, 

   9, 5, 4, 6,11, -1, // 5 faces about bottom
  10, 4, 3, 7,11, -1,
   6, 3, 2, 8,11, -1,
   7, 2, 1, 9,11, -1,
   8, 1, 5,10,11, -1,

   6, 7, 8, 9,10, -1, // bottom face
};

//Colors for the 12 faces
float[][] colors = new float[][]
{
   { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, 
   { 0.0f, 0.7f, 0.7f}, { 0.0f, 1.0f, 0.0f},
   { 0.7f, 0.7f, 0.0f}, { 0.7f, 0.0f, 0.7f}, 
   { 0.0f, 0.0f, 1.0f}, { 0.7f, 0.0f, 0.7f},
   { 0.7f, 0.7f, 0.0f}, { 0.0f, 1.0f, 0.0f}, 
   { 0.0f, 0.7f, 0.7f}, { 1.0f, 0.0f, 0.0f}
};

//Routine to create a scene graph representing a dodecahedron
SoSeparator makeStellatedDodecahedron()
{
   SoSeparator result = new SoSeparator();

   // Using the new SoVertexProperty node is more efficient
   SoVertexProperty myVertexProperty = new SoVertexProperty();

   // Define colors for the faces
   for (int i = 0; i < 12; i++)
       myVertexProperty.orderedRGBA.set1Value(i, 
             new SbColor(colors[i][0], colors[i][1], colors[i][2]).getPackedValue());
   myVertexProperty.materialBinding.setValue(SoMaterialBinding.Bindings.PER_FACE);

   // Define coordinates for vertices
   myVertexProperty.vertex.setValues(0, 12, vertexPositions);

   // Define the IndexedFaceSet, with indices into
   // the vertices:
   SoIndexedFaceSet myFaceSet = new SoIndexedFaceSet();
   myFaceSet.coordIndex.setValues(0, indices);

   myFaceSet.vertexProperty.setValue(myVertexProperty);
   result.addChild(myFaceSet);

   return result;
}
    

Polygons with holes can be described efficiently as a set of “contours” (boundary edges) in SoFaceSet SoFaceSet SoFaceSet and SoIndexedFaceSet SoIndexedFaceSet SoIndexedFaceSet . The SoShapeHints SoShapeHints SoShapeHints windingType field allows you to specify how the interior and exterior regions of a polygon are determined. These classes expose the full power of the GLU tessellator.


In the previous picture, two contours are specified. A winding type was chosen such that contour 2 defines a hole.

There are some basic concepts that you must know before creating holes with SoIndexedFaceSet SoIndexedFaceSet SoIndexedFaceSet or SoFaceSet SoFaceSet SoFaceSet shapes. Most important, to create a hole in a surface, you must insert an SoShapeHints SoShapeHints SoShapeHints node before the SoIndexedFaceSet SoIndexedFaceSet SoIndexedFaceSet or the SoFaceSet SoFaceSet SoFaceSet node.

[Warning]

The windingType field of SoShapeHints SoShapeHints SoShapeHints was introduced in Open Inventor 4.0. If you specify a non-default value for the windingType field, when this node is written to an Inventor file, the file will contain this new field. Older versions of Open Inventor (before version 4.0) will not be able to read this file and will generate an Inventor read error (unknown field).

  1. Create a list of coordinates for the contours you will use. (See SoIndexedFaceSet SoIndexedFaceSet SoIndexedFaceSet in the Reference Manual.)

    Example:

    static const float
    vertexPositions[7][3] =
    {
      {0, 0, 0}, { 1, 1, 0 } , { 1, 0, 0 } , { 1,-1, 0 },
      {0.3f, 0.2f, 0}, { 0.8f, 0, 0 } , { 0.3f, -0.2f, 0 } 
    };
          
    static const float[,] vertexPositions = new float[7,3]
    {
      {0, 0, 0}, { 1, 1, 0 } , { 1, 0, 0 } , { 1,-1, 0 },
      {0.3f, 0.2f, 0}, { 0.8f, 0, 0 } , { 0.3f, -0.2f, 0 } 
    };
        
    static float[][] vertexPositions = new float[][]
    {
      {0, 0, 0}, { 1, 1, 0 } , { 1, 0, 0 } , { 1,-1, 0 },
      {0.3f, 0.2f, 0}, { 0.8f, 0, 0 } , { 0.3f, -0.2f, 0 } 
    };
        
  2. Now we define indices for the outer contour and the contour that will define the hole.

    Example:


    static int32_t indices[8] =
    {
      0, 3, 1, SO_END_CONTOUR_INDEX,    // Outer contour
      5, 6, 4, SO_END_CONTOUR_INDEX     // Hole contour
    };
          
    public static int[] indices= new int[8]
    {
      0, 3, 1, -1,    // Outer contour
      5, 6, 4, -1     // Hole contour
    };
    
    
    public static int[] indices= new int[]
    {
      0, 3, 1, -1,    // Outer contour
      5, 6, 4, -1     // Hole contour
    };
    
    

    Figure 5.10, “Winding numbers defining a hole” shows the winding numbers for the different regions of the polygon (see the section called “Winding Numbers”). The centermost region has a winding number of 0 because the first contour is counterclockwise, and the interior one is clockwise.

    As you can see, we have defined two contours. The first one is the outer contour and the second one is the definition of the hole.

  3. Now we have to decide what winding type we are going to use (see the section called “Winding type”):

    Here are results we will get in our example with the following winding types:

    To describe this in Open Inventor we must add the following code before the definition of the SoIndexedFaceSet SoIndexedFaceSet SoIndexedFaceSet :

    m_hints = new SoShapeHints;
    root->addChild(m_hints);
    // If we want to use an ODD winding type.
    m_hints->windingType=SoShapeHints::ODD_TYPE;
                         
    SoShapeHints m_hints = new SoShapeHints();
    root.AddChild(m_hints);
    // If we want to use an ODD winding type.
    m_hints.windingType.Value = SoShapeHints.WindingTypes.ODD_TYPE;
              
    SoShapeHints m_hints = new SoShapeHints();
    root.addChild(m_hints);
    // If we want to use an ODD winding type.
    m_hints.windingType.setValue(SoShapeHints.WindingTypes.ODD_TYPE);
    
    [Warning]

    If we forget the previous four lines, we will get two coplanar sufaces.

And now the final code will be:

SoSeparator *root = new SoSeparator;
root->ref();

// 1) Define the winding type to use. This code asks Open Inventor to
// use the following IndexedFaceSet as contours.

myShapeHints = new SoShapeHints;
root->addChild(myShapeHints);
// If we want to use an ODD winding type.
myShapeHints->windingType=SoShapeHints::ODD_TYPE;

// 2) Creation of the indexedFaceSet.
SoVertexProperty *myVertexProperty = new SoVertexProperty;
// Define coordinates for vertices
myVertexProperty->vertex.setValues(0, 7, vertexPositions);

// Define the IndexedFaceSet, with indices into the vertices:
SoIndexedFaceSet *myIndexedFaceSet = new SoIndexedFaceSet;
myIndexedFaceSet ->coordIndex.setValues(0, 8, indices);

myIndexedFaceSet ->vertexProperty.setValue(myVertexProperty);
root->addChild(myIndexedFaceSet);
  
SoSeparator root = new SoSeparator();

// 1) Define the winding type to use. This code asks Open Inventor to
// use the following IndexedFaceSet as contours.

SoShapeHints myShapeHints = new SoShapeHints();
root.AddChild(myShapeHints);
// If we want to use an ODD winding type.
myShapeHints.windingType.Value = SoShapeHints.WindingTypes.ODD_TYPE;

// 2) Creation of the indexedFaceSet.
SoVertexProperty myVertexProperty = new SoVertexProperty();
// Define coordinates for vertices
myVertexProperty.vertex.SetValues(0, vertexPositions);

// Define the IndexedFaceSet, with indices into the vertices:
SoIndexedFaceSet myIndexedFaceSet = new SoIndexedFaceSet();
myIndexedFaceSet.coordIndex.SetValues(0, indices);

myIndexedFaceSet.vertexProperty.Value = myVertexProperty;
root.AddChild(myIndexedFaceSet);
SoSeparator root = new SoSeparator();

// 1) Define the winding type to use. This code asks Open Inventor to
// use the following IndexedFaceSet as contours.

SoShapeHints myShapeHints = new SoShapeHints();
root.addChild(myShapeHints);
// If we want to use an ODD winding type.
myShapeHints.windingType.setValue(SoShapeHints.WindingTypes.ODD_TYPE);

// 2) Creation of the indexedFaceSet.
SoVertexProperty myVertexProperty = new SoVertexProperty();
// Define coordinates for vertices
myVertexProperty.vertex.setValues(0, vertexPositions);

// Define the IndexedFaceSet, with indices into the vertices:
SoIndexedFaceSet myIndexedFaceSet = new SoIndexedFaceSet();
myIndexedFaceSet.coordIndex.setValues(0, indices);

myIndexedFaceSet.vertexProperty.setValue(myVertexProperty);
root.addChild(myIndexedFaceSet);

With the SoFaceSet SoFaceSet SoFaceSet node, there are no indices. It uses the coordinates in order, starting with the first one. Each contour has a number of vertices specified by a value in the numVertices field.

Example:

static const float vertices[6][3] =
{
  // First contour. 
  {0, 0, 0}, 
  {1, -1, 0}, 
  {1, 1, 0}, 
  // Second contour.
  {0.8f, 0, 0},
  {0.3f, -0.2f, 0},
  {0.3f, 0.2f, 0}
};

// Number of vertices in each polygon:
static int32_t numvertices[2] = {3, 3};
  
static const float[,] vertices = new float[6,3]
{
  // First contour. 
  {0, 0, 0}, 
  {1, -1, 0}, 
  {1, 1, 0}, 
  // Second contour.
  {0.8f, 0, 0},
  {0.3f, -0.2f, 0},
  {0.3f, 0.2f, 0}
};

// Number of vertices in each polygon:
static int[] numvertices = new int[2]{3, 3};
  
static float[][] vertices = new float[][]
{
  // First contour. 
  {0, 0, 0}, 
  {1, -1, 0}, 
  {1, 1, 0}, 
  // Second contour.
  {0.8f, 0, 0},
  {0.3f, -0.2f, 0},
  {0.3f, 0.2f, 0}
};

// Number of vertices in each polygon:
static int[] numvertices = new int[]{3, 3};
  

Just like the previous example, the winding numbers are shown in Figure 5.10, “Winding numbers defining a hole”.

Now we must specify the winding type to use (See the section called “Winding type”), which is given by the following code:

m_hints = new SoShapeHints;
root->addChild(m_hints);
// If we want to use an ODD winding type.
m_hints->windingType=SoShapeHints::ODD_TYPE;
  
SoShapeHints m_hints = new SoShapeHints();
root.AddChild(m_hints);
// If we want to use an ODD winding type.
m_hints.windingType.Value = SoShapeHints.WindingTypes.ODD_TYPE;
  
SoShapeHints m_hints = new SoShapeHints();
root.addChild(m_hints);
// If we want to use an ODD winding type.
m_hints.windingType.setValue(SoShapeHints.WindingTypes.ODD_TYPE);
  
[Warning]

If we forget the previous four lines, we will get get two coplanar sufaces.

Here are results we will get in our example with following winding types:

The final code to create a hole with an SoFaceSet SoFaceSet SoFaceSet node is:

SoSeparator *root = new SoSeparator;
root->ref();

// 1) Define the winding type to use. This code asks Open Inventor to 
// use following IndexedFaceSet as contours.
myShapeHints = new SoShapeHints;
root->addChild(myShapeHints);
// If we want to use an ODD winding type.
myShapeHints->windingType=SoShapeHints::ODD_TYPE;

// 2) Create the SoFaceSet
// Using the new SoVertexProperty node is more efficient
SoVertexProperty *myVertexProperty = new SoVertexProperty;

// Define coordinates for vertices
myVertexProperty->vertex.setValues(0, 12, vertices);

// Define the FaceSet
SoFaceSet *myFaceSet = new SoFaceSet;
myFaceSet->numVertices.setValues(0, 4, numvertices);

myFaceSet->vertexProperty.setValue(myVertexProperty);
root->addChild(myFaceSet);
  
SoSeparator root = new SoSeparator();

// 1) Define the winding type to use. This code asks Open Inventor to 
// use following IndexedFaceSet as contours.
SoShapeHints myShapeHints = new SoShapeHints();
root.AddChild(myShapeHints);
// If we want to use an ODD winding type.
myShapeHints.windingType.Value = SoShapeHints.WindingTypes.ODD_TYPE;

// 2) Create the SoFaceSet
// Using the new SoVertexProperty node is more efficient
SoVertexProperty myVertexProperty = new SoVertexProperty();

// Define coordinates for vertices
myVertexProperty.vertex.SetValues(0, vertices);

// Define the FaceSet
SoFaceSet myFaceSet = new SoFaceSet();
myFaceSet.numVertices.SetValues(0, numvertices);

myFaceSet.vertexProperty.Value = myVertexProperty;
root.AddChild(myFaceSet);
SoSeparator root = new SoSeparator();

// 1) Define the winding type to use. This code asks Open Inventor to 
// use following IndexedFaceSet as contours.
SoShapeHints myShapeHints = new SoShapeHints();
root.addChild(myShapeHints);
// If we want to use an ODD winding type.
myShapeHints.windingType.setValue(SoShapeHints.WindingTypes.ODD_TYPE);

// 2) Create the SoFaceSet
// Using the new SoVertexProperty node is more efficient
SoVertexProperty myVertexProperty = new SoVertexProperty();

// Define coordinates for vertices
myVertexProperty.vertex.setValues(0, vertices);

// Define the FaceSet
SoFaceSet myFaceSet = new SoFaceSet();
myFaceSet.numVertices.setValues(0, numvertices);

myFaceSet.vertexProperty.setValue(myVertexProperty);
root.addChild(myFaceSet);

The SoTriangleStripSet SoTriangleStripSet SoTriangleStripSet node constructs triangle strips out of the vertices located at the current coordinates. It is one of the fastest ways to draw polygonal objects in Inventor. The triangle strip set uses the current coordinates, in order, starting at the index specified by the startIndex field. (If no index is specified, it starts at the first index.)

The numVertices field indicates the number of vertices to use for each triangle strip in the set. The triangle strip set is described as follows:

static long numVertices[2] =
{
   32, // flag
   8   // pole
};
SoTriangleStripSet *myStrips = new SoTriangleStripSet;
myStrips->numVertices.setValues(0, 2, numVertices);
      
static int[] numVertices = new int[2]
{
   32, // flag
   8   // pole
};
SoTriangleStripSet myStrips = new SoTriangleStripSet();
myStrips.numVertices.SetValues(0, numVertices);
     
static int[] numVertices = new int[]
{
  32, // flag
  8   // pole
};
SoTriangleStripSet myStrips = new SoTriangleStripSet();
myStrips.numVertices.setValues(0, numVertices);

Because the numVertices field contains an array with two values, two triangle strips are created. The first strip (the flag) is made from the first 32 coordinate values. The second strip (the flagpole) is made from the next 8 coordinates. Face 0 determines the vertex ordering—in this case, counterclockwise.

[Tip]

Tip: Triangle strip sets and quad meshes are generally faster to render than face sets.

Example 5.3, “ Creating a Triangle Strip Set shows the code for creating a pennant-shaped flag. Figure 5.12, “ Scene Graph for Triangle Strip Set Example shows the scene graph for this example. Figure 5.13, “ Triangle Strip Set Example” shows the resulting image.



Example 5.3.  Creating a Triangle Strip Set

// Positions of all of the vertices:
static float vertexPositions[40][3] =
{
   {  0,   12,    0 }, {   0,   15,    0},
   {2.1, 12.1,  -.2 }, { 2.1, 14.6,  -.2},
   {  4, 12.5,  -.7 }, {   4, 14.5,  -.7},
   {4.5, 12.6,  -.8 }, { 4.5, 14.4,  -.8},
   {  5, 12.7,   -1 }, {   5, 14.4,   -1},
   {4.5, 12.8, -1.4 }, { 4.5, 14.6, -1.4},
   {  4, 12.9, -1.6 }, {   4, 14.8, -1.6},
   {3.3, 12.9, -1.8 }, { 3.3, 14.9, -1.8},
   {  3,   13, -2.0 }, {   3, 14.9, -2.0}, 
   {3.3, 13.1, -2.2 }, { 3.3, 15.0, -2.2},
   {  4, 13.2, -2.5 }, {   4, 15.0, -2.5},
   {  6, 13.5, -2.2 }, {   6, 14.8, -2.2},
   {  8, 13.4,   -2 }, {   8, 14.6,   -2},
   { 10, 13.7, -1.8 }, {  10, 14.4, -1.8},
   { 12,   14, -1.3 }, {  12, 14.5, -1.3},
   { 15, 14.9, -1.2 }, {  15,   15, -1.2},

   {-.5, 15,   0 }, { -.5, 0,   0},   // the flagpole
   {  0, 15,  .5 }, {   0, 0,  .5},
   {  0, 15, -.5 }, {   0, 0, -.5},
   {-.5, 15,   0 }, { -.5, 0,   0}
};


// Number of vertices in each strip.
static long numVertices[2] =
{
   32, // flag
   8   // pole
};
 
// Colors for the 12 faces
static float colors[2][3] =
{
   { .5, .5,  1 }, // purple flag
   { .4, .4, .4 }, // grey flagpole
};

// Routine to create a scene graph representing a pennant.
SoSeparator *
makePennant()
{
  SoSeparator *result = new SoSeparator;
  result->ref();
  
  // A shape hints tells the ordering of polygons. 
  // This ensures double-sided lighting.
  SoShapeHints *myHints = new SoShapeHints;
  myHints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE;
  result->addChild(myHints);
  
  // Define colors for the strips
  SoMaterial *myMaterials = new SoMaterial;
  myMaterials->diffuseColor.setValues(0, 2, colors);
  result->addChild(myMaterials);
  SoMaterialBinding *myMaterialBinding = new SoMaterialBinding;
  myMaterialBinding->value = SoMaterialBinding::PER_PART;
  result->addChild(myMaterialBinding);
  
  // Define coordinates for vertices
  SoCoordinate3 *myCoords = new SoCoordinate3;
  myCoords->point.setValues(0, 40, vertexPositions);
  result->addChild(myCoords);
  
  // Define the TriangleStripSet, made of two strips.
  SoTriangleStripSet *myStrips = new SoTriangleStripSet;
  myStrips->numVertices.setValues(0, 2, numVertices);
  result->addChild(myStrips);
  
  result->unrefNoDelete();
  return result;
}
         
// Positions of all of the vertices:
float[,] vertexPositions = new float[40, 3]
{
  { 0.0f,12.0f, 0.0f }, { 0.0f,15.0f, 0.0f},
  { 2.1f,12.1f,-0.2f }, { 2.1f,14.6f,-0.2f},
  { 4.0f,12.5f,-0.7f }, { 4.0f,14.5f,-0.7f},
  { 4.5f,12.6f,-0.8f }, { 4.5f,14.4f,-0.8f},
  { 5.0f,12.7f,-1.0f }, { 5.0f,14.4f,-1.0f},
  { 4.5f,12.8f,-1.4f }, { 4.5f,14.6f,-1.4f},
  { 4.0f,12.9f,-1.6f }, { 4.0f,14.8f,-1.6f},
  { 3.3f,12.9f,-1.8f }, { 3.3f,14.9f,-1.8f},
  { 3.0f,13.0f,-2.0f }, { 3.0f,14.9f,-2.0f}, 
  { 3.3f,13.1f,-2.2f }, { 3.3f,15.0f,-2.2f},
  { 4.0f,13.2f,-2.5f }, { 4.0f,15.0f,-2.5f},
  { 6.0f,13.5f,-2.2f }, { 6.0f,14.8f,-2.2f},
  { 8.0f,13.4f,-2.0f }, { 8.0f,14.6f,-2.0f},
  {10.0f,13.7f,-1.8f }, {10.0f,14.4f,-1.8f},
  {12.0f,14.0f,-1.3f }, {12.0f,14.5f,-1.3f},
  {15.0f,14.9f,-1.2f }, {15.0f,15.0f,-1.2f},

  {-0.5f,15.0f, 0.0f }, {-0.5f, 0.0f, 0.0f},   // the flagpole
  { 0.0f,15.0f, 0.5f }, { 0.0f, 0.0f, 0.5f},
  { 0.0f,15.0f,-0.5f }, { 0.0f, 0.0f,-0.5f},
  {-0.5f,15.0f, 0.0f }, {-0.5f, 0.0f, 0.0f}
};

// Number of vertices in each strip.
Int32[] numVertices = new Int32[2]
{
  32, // flag
  8   // pole
};

// Colors for the 12 faces
float[,] colors = new float[2, 3]
{
  { 0.5f, 0.5f, 1.0f }, // purple flag
  { 0.4f, 0.4f, 0.4f }, // grey flagpole
};

// Routine to create a scene graph representing a pennant.
SoSeparator MakePennant()
{
  SoSeparator result = new SoSeparator();

  // A shape hints tells the ordering of polygons. 
  // This insures double sided lighting.
  SoShapeHints myHints = new SoShapeHints();
  myHints.vertexOrdering.Value = SoShapeHints.VertexOrderings.COUNTERCLOCKWISE;
  result.AddChild(myHints);

  // Using the new SoVertexProperty node is more efficient
  SoVertexProperty myVertexProperty = new SoVertexProperty();

  // Define colors for the strips
  for (int i = 0; i < 2; i++)
      myVertexProperty.orderedRGBA[i] = 
        new SbColor(colors[i, 0], colors[i, 1], colors[i, 2]).GetPackedValue();
  myVertexProperty.materialBinding.Value = SoMaterialBinding.Bindings.PER_PART;

  // Define coordinates for vertices
  myVertexProperty.vertex.SetValues(0, 40, vertexPositions);

  // Define the TriangleStripSet, made of two strips.
  SoTriangleStripSet myStrips = new SoTriangleStripSet();
  myStrips.numVertices.SetValues(0, numVertices);

  myStrips.vertexProperty.Value = myVertexProperty;
  result.AddChild(myStrips);

  return result;
}
        
// Positions of all of the vertices:
static float vertexPositions[][] =
{
  {  0F,   12F,    0F }, {   0F,   15F,    0F},
  {2.1F, 12.1F,  -.2F }, { 2.1F, 14.6F,  -.2F},
  {  4F, 12.5F,  -.7F }, {   4F, 14.5F,  -.7F},
  {4.5F, 12.6F,  -.8F }, { 4.5F, 14.4F,  -.8F},
  {  5F, 12.7F,   -1F }, {   5F, 14.4F,   -1F},
  {4.5F, 12.8F, -1.4F }, { 4.5F, 14.6F, -1.4F},
  {  4F, 12.9F, -1.6F }, {   4F, 14.8F, -1.6F},
  {3.3F, 12.9F, -1.8F }, { 3.3F, 14.9F, -1.8F},
  {  3F,   13F, -2.0F }, {   3F, 14.9F, -2.0F},
  {3.3F, 13.1F, -2.2F }, { 3.3F, 15.0F, -2.2F},
  {  4F, 13.2F, -2.5F }, {   4F, 15.0F, -2.5F},
  {  6F, 13.5F, -2.2F }, {   6F, 14.8F, -2.2F},
  {  8F, 13.4F,   -2F }, {   8F, 14.6F,   -2F},
  { 10F, 13.7F, -1.8F }, {  10F, 14.4F, -1.8F},
  { 12F,   14F, -1.3F }, {  12F, 14.5F, -1.3F},
  { 15F, 14.9F, -1.2F }, {  15F,   15F, -1.2F},

  {-.5F, 15F,   0F }, { -.5F, 0F,   0F},   // the flagpole
  {  0F, 15F,  .5F }, {   0F, 0F,  .5F},
  {  0F, 15F, -.5F }, {   0F, 0F, -.5F},
  {-.5F, 15F,   0F }, { -.5F, 0F,   0F}
};

// Number of vertices in each strip.
static int numVertices[] =
{
  32, // flag
  8   // pole
};

// Colors for the 12 faces
static float colors[][] =
{
  { .5F, .5F,  1F }, // purple flag
  { .4F, .4F, .4F } // grey flagpole
};

// Routine to create a scene graph representing a pennant.
SoSeparator makePennant()
{
  SoSeparator result = new SoSeparator();
  
  // A shape hints tells the ordering of polygons.
  // This insures double sided lighting.
  SoShapeHints myHints = new SoShapeHints();
  myHints.vertexOrdering.setValue(SoShapeHints.VertexOrderings.COUNTERCLOCKWISE);
  result.addChild(myHints);
  
  // This is the preferred code for Inventor 2.1
  // otherwise, see below the commented block
  
  // Using the new SoVertexProperty node is more efficient
  SoVertexProperty myVertexProperty = new SoVertexProperty();
  
  // Define colors for the strips
  for (int i = 0; i < 2; i++)
    myVertexProperty.orderedRGBA.set1Value(i, new SbColor(colors[i]).getPackedValue());
  myVertexProperty.materialBinding.setValue(SoMaterialBinding.Bindings.PER_PART);
  
  // Define coordinates for vertices
  myVertexProperty.vertex.setValues(0, vertexPositions);
  
  // Define the TriangleStripSet, made of two strips.
  SoTriangleStripSet myStrips = new SoTriangleStripSet();
  myStrips.numVertices.setValues(0, numVertices);
  
  myStrips.vertexProperty.setValue(myVertexProperty);
  result.addChild(myStrips);
  
  return result;
}

The SoQuadMesh SoQuadMesh SoQuadMesh node constructs quadrilaterals from the vertices located at the current coordinates. It uses the coordinates in order, starting at the index specified by the startIndex field. (If no index is specified, it starts at the first index.)

The verticesPerColumn and verticesPerRow fields indicate the number of vertices in the columns and rows of the mesh. Example 5.4, “ Creating a Quad Mesh ” creates a quad mesh as follows:

SoQuadMesh *myQuadMesh = new SoQuadMesh;
myQuadMesh->verticesPerRow = 12;
myQuadMesh->verticesPerColumn = 5;
     
SoQuadMesh myQuadMesh = new SoQuadMesh();
myQuadMesh.verticesPerRow.Value = 12;
myQuadMesh.verticesPerColumn.Value = 5;
     
SoQuadMesh myQuadMesh = new SoQuadMesh();
myQuadMesh.verticesPerRow.setValue(12);
myQuadMesh.verticesPerColumn.setValue(5);
     

Each row in this quad mesh contains 12 vertices. Each column contains 5 vertices. Figure 5.14, “ Scene Graph for Quad Mesh Example shows the scene graph for this example. Figure 5.15, “ Quad Mesh Example” shows the resulting image.



Example 5.4.  Creating a Quad Mesh

// Positions of all of the vertices:

static float vertexPositions[160][3] =
{  // 1st row
   {-13.0,  0.0, 1.5}, {-10.3, 13.7, 1.2}, { -7.6, 21.7, 1.0}, 
   { -5.0, 26.1, 0.8}, { -2.3, 28.2, 0.6}, { -0.3, 28.8, 0.5},
   {  0.3, 28.8, 0.5}, {  2.3, 28.2, 0.6}, {  5.0, 26.1, 0.8}, 
   {  7.6, 21.7, 1.0}, { 10.3, 13.7, 1.2}, { 13.0,  0.0, 1.5},
   // 2nd row
   {-10.0,  0.0, 1.5}, { -7.9, 13.2, 1.2}, { -5.8, 20.8, 1.0}, 
   { -3.8, 25.0, 0.8}, { -1.7, 27.1, 0.6}, { -0.2, 27.6, 0.5},
   {  0.2, 27.6, 0.5}, {  1.7, 27.1, 0.6}, {  3.8, 25.0, 0.8}, 
   {  5.8, 20.8, 1.0}, {  7.9, 13.2, 1.2}, { 10.0,  0.0, 1.5},
   // 3rd row
   {-10.0,  0.0,-1.5}, { -7.9, 13.2,-1.2}, { -5.8, 20.8,-1.0}, 
   { -3.8, 25.0,-0.8}, { -1.7, 27.1,-0.6}, { -0.2, 27.6,-0.5},
   {  0.2, 27.6,-0.5}, {  1.7, 27.1,-0.6}, {  3.8, 25.0,-0.8}, 
   {  5.8, 20.8,-1.0}, {  7.9, 13.2,-1.2}, { 10.0,  0.0,-1.5},
   // 4th row 
   {-13.0,  0.0,-1.5}, {-10.3, 13.7,-1.2}, { -7.6, 21.7,-1.0}, 
   { -5.0, 26.1,-0.8}, { -2.3, 28.2,-0.6}, { -0.3, 28.8,-0.5},
   {  0.3, 28.8,-0.5}, {  2.3, 28.2,-0.6}, {  5.0, 26.1,-0.8}, 
   {  7.6, 21.7,-1.0}, { 10.3, 13.7,-1.2}, { 13.0,  0.0,-1.5},
   // 5th row
   {-13.0,  0.0, 1.5}, {-10.3, 13.7, 1.2}, { -7.6, 21.7, 1.0}, 
   { -5.0, 26.1, 0.8}, { -2.3, 28.2, 0.6}, { -0.3, 28.8, 0.5},
   {  0.3, 28.8, 0.5}, {  2.3, 28.2, 0.6}, {  5.0, 26.1, 0.8}, 
   {  7.6, 21.7, 1.0}, { 10.3, 13.7, 1.2}, { 13.0,  0.0, 1.5}
};

// Routine to create a scene graph representing an arch.
SoSeparator *
makeArch()
{
   SoSeparator *result = new SoSeparator;
   result->ref();

   // Define the material
   SoMaterial *myMaterial = new SoMaterial;
   myMaterial->diffuseColor.setValue(.78, .57, .11);
   result->addChild(myMaterial);

   // Define coordinates for vertices
   SoCoordinate3 *myCoords = new SoCoordinate3;
   myCoords->point.setValues(0, 60, vertexPositions);
   result->addChild(myCoords);

   // Define the QuadMesh.
   SoQuadMesh *myQuadMesh = new SoQuadMesh;
   myQuadMesh->verticesPerRow = 12;

   myQuadMesh->verticesPerColumn = 5;
   result->addChild(myQuadMesh);

   result->unrefNoDelete();
   return result;
}
       
// Positions of all of the vertices:
float[,] vertexPositions = new float[60, 3]
{
  // 1st row
  {-13.0f,  0.0f, 1.5f}, {-10.3f, 13.7f, 1.2f}, { -7.6f, 21.7f, 1.0f}, 
  { -5.0f, 26.1f, 0.8f}, { -2.3f, 28.2f, 0.6f}, { -0.3f, 28.8f, 0.5f},
  {  0.3f, 28.8f, 0.5f}, {  2.3f, 28.2f, 0.6f}, {  5.0f, 26.1f, 0.8f}, 
  {  7.6f, 21.7f, 1.0f}, { 10.3f, 13.7f, 1.2f}, { 13.0f,  0.0f, 1.5f},
  // 2nd row
  {-10.0f,  0.0f, 1.5f}, { -7.9f, 13.2f, 1.2f}, { -5.8f, 20.8f, 1.0f}, 
  { -3.8f, 25.0f, 0.8f}, { -1.7f, 27.1f, 0.6f}, { -0.2f, 27.6f, 0.5f},
  {  0.2f, 27.6f, 0.5f}, {  1.7f, 27.1f, 0.6f}, {  3.8f, 25.0f, 0.8f}, 
  {  5.8f, 20.8f, 1.0f}, {  7.9f, 13.2f, 1.2f}, { 10.0f,  0.0f, 1.5f},
  // 3rd row
  {-10.0f,  0.0f,-1.5f}, { -7.9f, 13.2f,-1.2f}, { -5.8f, 20.8f,-1.0f}, 
  { -3.8f, 25.0f,-0.8f}, { -1.7f, 27.1f,-0.6f}, { -0.2f, 27.6f,-0.5f},
  {  0.2f, 27.6f,-0.5f}, {  1.7f, 27.1f,-0.6f}, {  3.8f, 25.0f,-0.8f}, 
  {  5.8f, 20.8f,-1.0f}, {  7.9f, 13.2f,-1.2f}, { 10.0f,  0.0f,-1.5f},
  // 4th row 
  {-13.0f,  0.0f,-1.5f}, {-10.3f, 13.7f,-1.2f}, { -7.6f, 21.7f,-1.0f}, 
  { -5.0f, 26.1f,-0.8f}, { -2.3f, 28.2f,-0.6f}, { -0.3f, 28.8f,-0.5f},
  {  0.3f, 28.8f,-0.5f}, {  2.3f, 28.2f,-0.6f}, {  5.0f, 26.1f,-0.8f}, 
  {  7.6f, 21.7f,-1.0f}, { 10.3f, 13.7f,-1.2f}, { 13.0f,  0.0f,-1.5f},
  // 5th row
  {-13.0f,  0.0f, 1.5f}, {-10.3f, 13.7f, 1.2f}, { -7.6f, 21.7f, 1.0f}, 
  { -5.0f, 26.1f, 0.8f}, { -2.3f, 28.2f, 0.6f}, { -0.3f, 28.8f, 0.5f},
  {  0.3f, 28.8f, 0.5f}, {  2.3f, 28.2f, 0.6f}, {  5.0f, 26.1f, 0.8f}, 
  {  7.6f, 21.7f, 1.0f}, { 10.3f, 13.7f, 1.2f}, { 13.0f,  0.0f, 1.5f}
};

//Routine to create a scene graph representing an arch.
SoSeparator
MakeArch()
{
  SoSeparator result = new SoSeparator();

  // Using the new SoVertexProperty node is more efficient
  SoVertexProperty myVertexProperty = new SoVertexProperty();

  // Define the material
  myVertexProperty.orderedRGBA.SetValue(new SbColor(0.78f, 0.57f, 0.11f).GetPackedValue());

  // Define coordinates for vertices
  myVertexProperty.vertex.SetValues(0, 60, vertexPositions);
  // Define the QuadMesh.
  SoQuadMesh myQuadMesh = new SoQuadMesh();
  myQuadMesh.verticesPerRow.Value = 12;
  myQuadMesh.verticesPerColumn.Value = 5;
  myQuadMesh.vertexProperty.Value = myVertexProperty;
  result.AddChild(myQuadMesh);

  return result;
}
     
// Positions of all of the vertices:
static float vertexPositions[][] =
{
  // 1st row
  {-13.0F,  0.0F, 1.5F}, {-10.3F, 13.7F, 1.2F}, { -7.6F, 21.7F, 1.0F},
  { -5.0F, 26.1F, 0.8F}, { -2.3F, 28.2F, 0.6F}, { -0.3F, 28.8F, 0.5F},
  {  0.3F, 28.8F, 0.5F}, {  2.3F, 28.2F, 0.6F}, {  5.0F, 26.1F, 0.8F},
  {  7.6F, 21.7F, 1.0F}, { 10.3F, 13.7F, 1.2F}, { 13.0F,  0.0F, 1.5F},
  // 2nd row
  {-10.0F,  0.0F, 1.5F}, { -7.9F, 13.2F, 1.2F}, { -5.8F, 20.8F, 1.0F},
  { -3.8F, 25.0F, 0.8F}, { -1.7F, 27.1F, 0.6F}, { -0.2F, 27.6F, 0.5F},
  {  0.2F, 27.6F, 0.5F}, {  1.7F, 27.1F, 0.6F}, {  3.8F, 25.0F, 0.8F},
  {  5.8F, 20.8F, 1.0F}, {  7.9F, 13.2F, 1.2F}, { 10.0F,  0.0F, 1.5F},
  // 3rd row
  {-10.0F,  0.0F,-1.5F}, { -7.9F, 13.2F,-1.2F}, { -5.8F, 20.8F,-1.0F},
  { -3.8F, 25.0F,-0.8F}, { -1.7F, 27.1F,-0.6F}, { -0.2F, 27.6F,-0.5F},
  {  0.2F, 27.6F,-0.5F}, {  1.7F, 27.1F,-0.6F}, {  3.8F, 25.0F,-0.8F},
  {  5.8F, 20.8F,-1.0F}, {  7.9F, 13.2F,-1.2F}, { 10.0F,  0.0F,-1.5F},
  // 4th row
  {-13.0F,  0.0F,-1.5F}, {-10.3F, 13.7F,-1.2F}, { -7.6F, 21.7F,-1.0F},
  { -5.0F, 26.1F,-0.8F}, { -2.3F, 28.2F,-0.6F}, { -0.3F, 28.8F,-0.5F},
  {  0.3F, 28.8F,-0.5F}, {  2.3F, 28.2F,-0.6F}, {  5.0F, 26.1F,-0.8F},
  {  7.6F, 21.7F,-1.0F}, { 10.3F, 13.7F,-1.2F}, { 13.0F,  0.0F,-1.5F},
  // 5th row
  {-13.0F,  0.0F, 1.5F}, {-10.3F, 13.7F, 1.2F}, { -7.6F, 21.7F, 1.0F},
  { -5.0F, 26.1F, 0.8F}, { -2.3F, 28.2F, 0.6F}, { -0.3F, 28.8F, 0.5F},
  {  0.3F, 28.8F, 0.5F}, {  2.3F, 28.2F, 0.6F}, {  5.0F, 26.1F, 0.8F},
  {  7.6F, 21.7F, 1.0F}, { 10.3F, 13.7F, 1.2F}, { 13.0F,  0.0F, 1.5F}
};

// Routine to create a scene graph representing an arch.
SoSeparator makeArch()
{
  // This is the preferred code for Inventor 2.1
  // otherwise, see below the commented block

  // Using the new SoVertexProperty node is more efficient
  SoVertexProperty myVertexProperty = new SoVertexProperty() ;

  // Define the material
  //    myVertexProperty.orderedRGBA.setValue(
                new SbColor(.78F, .57F, .11F).getPackedValue());
  int rgb = new SbColor(.78F, .57F, .11F).getPackedValue() ;
  myVertexProperty.orderedRGBA.setValue(rgb);

  // Define coordinates for vertices
  myVertexProperty.vertex.setValues(0, vertexPositions);

  // Define the QuadMesh.
  SoQuadMesh myQuadMesh = new SoQuadMesh() ;
  myQuadMesh.verticesPerRow.setValue(12) ;
  myQuadMesh.verticesPerColumn.setValue(5) ;
  myQuadMesh.vertexProperty.setValue(myVertexProperty) ;

  SoSeparator result = new SoSeparator();
  result.addChild(myQuadMesh);

  return result;
}