1 
2 #include <stdlib.h>
3 #include <osg/Geode>
4 #include <osgUtil/TriStripVisitor>
5 
6 #include "VBSPGeometry.h"
7 
8 
9 using namespace osg;
10 using namespace bsp;
11 
12 
VBSPGeometry(VBSPData * bspData)13 VBSPGeometry::VBSPGeometry(VBSPData * bspData)
14 {
15     // Keep track of the bsp data, as it has all of the data lists that we
16     // need
17     bsp_data = bspData;
18 
19     // Create arrays for the vertex attributes
20     vertex_array = new Vec3Array();
21     normal_array = new Vec3Array();
22     texcoord_array = new Vec2Array();
23 
24     // Create a primitive set for drawing variable length primitives (VBSP
25     // primitives are only guaranteed to be convex polygons)
26     primitive_set = new DrawArrayLengths(PrimitiveSet::POLYGON);
27 
28     // Create a second set of arrays for displacement surfaces
29     disp_vertex_array = new Vec3Array();
30     disp_normal_array = new Vec3Array();
31     disp_texcoord_array = new Vec2Array();
32     disp_vertex_attr_array = new Vec4Array();
33 
34     // Create a second primitive set for drawing indexed triangles, which is
35     // the quickest method for drawing the displacement surfaces
36     disp_primitive_set = new DrawElementsUInt(PrimitiveSet::TRIANGLES);
37 }
38 
39 
~VBSPGeometry()40 VBSPGeometry::~VBSPGeometry()
41 {
42 }
43 
44 
doesEdgeExist(int row,int col,int direction,int vertsPerEdge)45 bool VBSPGeometry::doesEdgeExist(int row, int col, int direction,
46                                  int vertsPerEdge)
47 {
48     // See if there is an edge on the displacement surface from the given
49     // vertex in the given direction (we only need to know the vertices
50     // indices, because all displacement surfaces are tessellated in the
51     // same way)
52     switch (direction)
53     {
54         case 0:
55             // False if we're on the left edge, otherwise true
56             if ((row - 1) < 0)
57                 return false;
58             else
59                 return true;
60 
61         case 1:
62             // False if we're on the top edge, otherwise true
63             if ((col + 1) >= vertsPerEdge)
64                 return false;
65             else
66                 return true;
67 
68         case 2:
69             // False if we're on the right edge, otherwise true
70             if ((row + 1) >= vertsPerEdge)
71                 return false;
72             else
73                 return true;
74 
75         case 3:
76             // False if we're on the bottom edge, otherwise true
77             if ((col - 1) < 0)
78                 return false;
79             else
80                 return true;
81 
82         default:
83             return false;
84     }
85 }
86 
87 
getNormalFromEdges(int row,int col,unsigned char edgeBits,int firstVertex,int vertsPerEdge)88 osg::Vec3 VBSPGeometry::getNormalFromEdges(int row, int col,
89                                            unsigned char edgeBits,
90                                            int firstVertex, int vertsPerEdge)
91 {
92     osg::Vec3 *   vertexData;
93     osg::Vec3 *   surfaceVerts;
94     osg::Vec3     finalNormal;
95     osg::Vec3     v1, v2, v3;
96     osg::Vec3     e1, e2;
97     osg::Vec3     tempNormal;
98     int           normalCount;
99 
100     // Constants for direction.  If the bit is set in the edgeBits, then
101     // there is an edge connected to the current vertex in that direction
102     const unsigned char   NEG_X = 1 << 0;
103     const unsigned char   POS_Y = 1 << 1;
104     const unsigned char   POS_X = 1 << 2;
105     const unsigned char   NEG_Y = 1 << 3;
106 
107     // Constants for quadrants.  If both bits are set, then there are
108     // exactly two triangles in that quadrant
109     const unsigned char   QUAD_1 = POS_X | POS_Y;
110     const unsigned char   QUAD_2 = NEG_X | POS_Y;
111     const unsigned char   QUAD_3 = NEG_X | NEG_Y;
112     const unsigned char   QUAD_4 = POS_X | NEG_Y;
113 
114 
115     // Grab the vertex data from the displaced vertex array (if there's a
116     // better way to randomly access the data in this array, I'm all ears)
117     vertexData = (osg::Vec3 *)disp_vertex_array->getDataPointer();
118 
119     // Move to the surface we're interested in, and start counting vertices
120     // from there
121     surfaceVerts = &vertexData[firstVertex];
122 
123     // Start with no normals computed
124     finalNormal.set(0.0, 0.0, 0.0);
125     normalCount = 0;
126 
127     // The process is fairly simple.  For all four quadrants surrounding
128     // the vertex, check each quadrant to see if there are triangles there.
129     // If so, calculate the normals of the two triangles in that quadrant, and
130     // add them to the final normal.  When fininshed, scale the final normal
131     // based on the number of contributing triangle normals
132 
133     // Check quadrant 1 (+X,+Y)
134     if ((edgeBits & QUAD_1) == QUAD_1)
135     {
136         // First triangle
137         v1 = surfaceVerts[(col+1) * vertsPerEdge + row];
138         v2 = surfaceVerts[col * vertsPerEdge + row];
139         v3 = surfaceVerts[col * vertsPerEdge + (row+1)];
140         e1 = v1 - v2;
141         e2 = v3 - v2;
142         tempNormal = e2 ^ e1;
143         tempNormal.normalize();
144         finalNormal += tempNormal;
145         normalCount++;
146 
147         // Second triangle
148         v1 = surfaceVerts[(col+1) * vertsPerEdge + row];
149         v2 = surfaceVerts[col * vertsPerEdge + (row+1)];
150         v3 = surfaceVerts[(col+1) * vertsPerEdge + (row+1)];
151         e1 = v1 - v2;
152         e2 = v3 - v2;
153         tempNormal = e2 ^ e1;
154         tempNormal.normalize();
155         finalNormal += tempNormal;
156         normalCount++;
157     }
158 
159     // Check quadrant 2 (-X,+Y)
160     if ((edgeBits & QUAD_2) == QUAD_2)
161     {
162         // First triangle
163         v1 = surfaceVerts[(col+1) * vertsPerEdge + (row-1)];
164         v2 = surfaceVerts[col * vertsPerEdge + (row-1)];
165         v3 = surfaceVerts[col * vertsPerEdge + row];
166         e1 = v1 - v2;
167         e2 = v3 - v2;
168         tempNormal = e2 ^ e1;
169         tempNormal.normalize();
170         finalNormal += tempNormal;
171         normalCount++;
172 
173         // Second triangle
174         v1 = surfaceVerts[(col+1) * vertsPerEdge + (row-1)];
175         v2 = surfaceVerts[col * vertsPerEdge + row];
176         v3 = surfaceVerts[(col+1) * vertsPerEdge + row];
177         e1 = v1 - v2;
178         e2 = v3 - v2;
179         tempNormal = e2 ^ e1;
180         tempNormal.normalize();
181         finalNormal += tempNormal;
182         normalCount++;
183     }
184 
185     // Check quadrant 3 (-X,-Y)
186     if ((edgeBits & QUAD_3) == QUAD_3)
187     {
188         // First triangle
189         v1 = surfaceVerts[col * vertsPerEdge + (row-1)];
190         v2 = surfaceVerts[(col-1) * vertsPerEdge + (row-1)];
191         v3 = surfaceVerts[(col-1) * vertsPerEdge + row];
192         e1 = v1 - v2;
193         e2 = v3 - v2;
194         tempNormal = e2 ^ e1;
195         tempNormal.normalize();
196         finalNormal += tempNormal;
197         normalCount++;
198 
199         // Second triangle
200         v1 = surfaceVerts[col * vertsPerEdge + (row-1)];
201         v2 = surfaceVerts[(col-1) * vertsPerEdge + row];
202         v3 = surfaceVerts[col * vertsPerEdge + row];
203         e1 = v1 - v2;
204         e2 = v3 - v2;
205         tempNormal = e2 ^ e1;
206         tempNormal.normalize();
207         finalNormal += tempNormal;
208         normalCount++;
209     }
210 
211     // Check quadrant 4 (+X,-Y)
212     if ((edgeBits & QUAD_4) == QUAD_4)
213     {
214         // First triangle
215         v1 = surfaceVerts[col * vertsPerEdge + row];
216         v2 = surfaceVerts[(col-1) * vertsPerEdge + row];
217         v3 = surfaceVerts[(col-1) * vertsPerEdge + (row+1)];
218         e1 = v1 - v2;
219         e2 = v3 - v2;
220         tempNormal = e2 ^ e1;
221         tempNormal.normalize();
222         finalNormal += tempNormal;
223         normalCount++;
224 
225         // Second triangle
226         v1 = surfaceVerts[col * vertsPerEdge + row];
227         v2 = surfaceVerts[(col-1) * vertsPerEdge + (row+1)];
228         v3 = surfaceVerts[col * vertsPerEdge + (row+1)];
229         e1 = v1 - v2;
230         e2 = v3 - v2;
231         tempNormal = e2 ^ e1;
232         tempNormal.normalize();
233         finalNormal += tempNormal;
234         normalCount++;
235     }
236 
237     // Scale the final normal according to how many triangle normals are
238     // contributing
239     finalNormal *= (1.0f / (float)normalCount);
240 
241     return finalNormal;
242 }
243 
244 
createDispSurface(Face & face,DisplaceInfo & dispInfo)245 void VBSPGeometry::createDispSurface(Face & face, DisplaceInfo & dispInfo)
246 {
247     TexInfo           currentTexInfo;
248     TexData           currentTexData;
249     Vec3              texU;
250     float             texUOffset;
251     float             texUScale;
252     Vec3              texV;
253     float             texVOffset;
254     float             texVScale;
255     int               i, j;
256     unsigned int      k;
257     double            dist, minDist;
258     int               minIndex = 0;
259     osg::Vec3         temp;
260     int               edgeIndex;
261     int               currentSurfEdge;
262     Edge              currentEdge;
263     osg::Vec3         currentVertex;
264     osg::Vec3         vertices[4];
265     unsigned int      firstVertex;
266     int               numEdgeVertices;
267     double            subdivideScale;
268     osg::Vec3         leftEdge, rightEdge;
269     osg::Vec3         leftEdgeStep, rightEdgeStep;
270     osg::Vec3         leftEnd, rightEnd;
271     osg::Vec3         leftRightSeg, leftRightStep;
272     unsigned int      dispVertIndex;
273     DisplacedVertex   dispVertInfo;
274     osg::Vec3         flatVertex, dispVertex;
275     unsigned int      index;
276     osg::Vec3         normal;
277     float             u, v;
278     osg::Vec2         texCoord;
279     float             alphaBlend;
280     unsigned char     edgeBits;
281 
282 
283     // Get the texture info for this face
284     currentTexInfo = bsp_data->getTexInfo(face.texinfo_index);
285     currentTexData = bsp_data->getTexData(currentTexInfo.texdata_index);
286 
287     // Get the texture vectors and offsets.  These are used to calculate
288     // texture coordinates
289     texU.set(currentTexInfo.texture_vecs[0][0],
290              currentTexInfo.texture_vecs[0][1],
291              currentTexInfo.texture_vecs[0][2]);
292     texUOffset = currentTexInfo.texture_vecs[0][3];
293     texV.set(currentTexInfo.texture_vecs[1][0],
294              currentTexInfo.texture_vecs[1][1],
295              currentTexInfo.texture_vecs[1][2]);
296     texVOffset = currentTexInfo.texture_vecs[1][3];
297 
298     // Scale the texture vectors from inches to meters
299     texU *= 39.37f;
300     texV *= 39.37f;
301 
302     // Get the size of the texture involved, as the planar texture projection
303     // assumes non-normalized texture coordinates
304     texUScale = 1.0f / (float)currentTexData.texture_width;
305     texVScale = 1.0f / (float)currentTexData.texture_height;
306 
307     // Get the first edge index
308     edgeIndex = face.first_edge;
309 
310     // Get the base vertices for this face
311     for (i = 0; i < face.num_edges; i++)
312     {
313         // Look up the edge specified by the surface edge index, the
314         // index might be negative (see below), so take the absolute
315         // value
316         currentSurfEdge = bsp_data->getSurfaceEdge(edgeIndex);
317         currentEdge = bsp_data->getEdge(abs(currentSurfEdge));
318 
319         // The sign of the surface edge index specifies which vertex is
320         // "first" for this face.  A negative index means the edge should
321         // be flipped, and the second vertex treated as the first
322         if (currentSurfEdge < 0)
323             currentVertex = bsp_data->getVertex(currentEdge.vertex[1]);
324         else
325             currentVertex = bsp_data->getVertex(currentEdge.vertex[0]);
326 
327         // Add the vertex to the array
328         vertices[i] = currentVertex;
329 
330         // Move on to the next vertex
331         edgeIndex++;
332     }
333 
334     // Rotate the base coordinates for the surface until the first vertex
335     // matches the start position
336     minDist = 1.0e9;
337     for (i = 0; i < 4; i++)
338     {
339        // Calculate the distance of the start position from this vertex
340        dist = (vertices[i] - dispInfo.start_position * 0.0254f).length();
341 
342        // If this is the smallest distance we've seen, remember it
343        if (dist < minDist)
344        {
345           minDist = dist;
346           minIndex = i;
347        }
348     }
349 
350     // Rotate the displacement surface quad until we get the starting vertex
351     // in the 0th position
352     for (i = 0; i < minIndex; i++)
353     {
354         temp = vertices[0];
355         vertices[0] = vertices[1];
356         vertices[1] = vertices[2];
357         vertices[2] = vertices[3];
358         vertices[3] = temp;
359     }
360 
361     // Calculate the vectors for the left and right edges of the surface
362     // (remembering that the surface is wound clockwise)
363     leftEdge = vertices[1] - vertices[0];
364     rightEdge = vertices[2] - vertices[3];
365 
366     // Calculate the number of vertices along each edge of the surface
367     numEdgeVertices = (1 << dispInfo.power) + 1;
368 
369     // Calculate the subdivide scale, which will tell us how far apart to
370     // put each vertex (relative to the length of the surface's edges)
371     subdivideScale = 1.0 / (double)(numEdgeVertices - 1);
372 
373     // Calculate the step size between vertices on the left and right edges
374     leftEdgeStep = leftEdge * subdivideScale;
375     rightEdgeStep = rightEdge * subdivideScale;
376 
377     // Remember the first vertex index in the vertex array
378     firstVertex = disp_vertex_array->size();
379 
380     // Generate the displaced vertices (this technique comes from the
381     // Source SDK)
382     for (i = 0; i < numEdgeVertices; i++)
383     {
384         // Calculate the two endpoints for this section of the surface
385         leftEnd = leftEdgeStep * (double) i;
386         leftEnd += vertices[0];
387         rightEnd = rightEdgeStep * (double) i;
388         rightEnd += vertices[3];
389 
390         // Now, get the vector from left to right, and subdivide it as well
391         leftRightSeg = rightEnd - leftEnd;
392         leftRightStep = leftRightSeg * subdivideScale;
393 
394         // Generate the vertices for this section
395         for (j = 0; j < numEdgeVertices; j++)
396         {
397             // Get the displacement info for this vertex
398             dispVertIndex = dispInfo.disp_vert_start;
399             dispVertIndex += i * numEdgeVertices + j;
400             dispVertInfo = bsp_data->getDispVertex(dispVertIndex);
401 
402             // Calculate the flat vertex
403             flatVertex = leftEnd + (leftRightStep * (double) j);
404 
405             // Calculate the displaced vertex
406             dispVertex =
407                 dispVertInfo.displace_vec *
408                 (dispVertInfo.displace_dist * 0.0254);
409             dispVertex += flatVertex;
410 
411             // Add the vertex to the displaced vertex array
412             disp_vertex_array->push_back(dispVertex);
413 
414             // Calculate the texture coordinates for this vertex.  Texture
415             // coordinates are calculated using a planar projection, so we need
416             // to use the non-displaced vertex position here
417             u = texU * flatVertex + texUOffset;
418             u *= texUScale;
419             v = texV * flatVertex + texVOffset;
420             v *= texVScale;
421             texCoord.set(u, v);
422 
423             // Add the texture coordinate to the array
424             disp_texcoord_array->push_back(texCoord);
425 
426             // Get the texture blend parameter for this vertex as well, and
427             // assign it as the alpha channel for the primary vertex color.
428             // We'll use a combiner operation to do the texture blending
429             alphaBlend = dispVertInfo.alpha_blend / 255.0;
430             disp_vertex_attr_array->push_back(
431                 osg::Vec4f(1.0, 1.0, 1.0, 1.0 - alphaBlend));
432         }
433     }
434 
435     // Calculate normals at each vertex (this is adapted from the Source SDK,
436     // including the two helper functions)
437     for (i = 0; i < numEdgeVertices; i++)
438     {
439         for (j = 0; j < numEdgeVertices; j++)
440         {
441             // See which of the 4 possible edges (left, up, right, or down) are
442             // incident on this vertex
443             edgeBits = 0;
444             for (k = 0; k < 4; k++)
445             {
446                 if (doesEdgeExist(j, i, k, numEdgeVertices))
447                     edgeBits |= 1 << k;
448             }
449 
450             // Calculate the normal based on the adjacent edges
451             normal = getNormalFromEdges(j, i, edgeBits, firstVertex,
452                                         numEdgeVertices);
453 
454             // Add the normal to the normal array
455             disp_normal_array->push_back(normal);
456         }
457     }
458 
459     // Now, triangulate the surface (this technique comes from the Source SDK)
460     for (i = 0; i < numEdgeVertices-1; i++)
461     {
462         for (j = 0; j < numEdgeVertices-1; j++)
463         {
464             // Get the current vertex index (local to this surface)
465             index = i * numEdgeVertices + j;
466 
467             // See if this index is odd
468             if ((index % 2) == 1)
469             {
470                 // Add the vertex offset (so we reference this surface's
471                 // vertices in the array)
472                 index += firstVertex;
473 
474                 // Create two triangles on this vertex from top-left to
475                 // bottom-right
476                 disp_primitive_set->push_back(index);
477                 disp_primitive_set->push_back(index + 1);
478                 disp_primitive_set->push_back(index + numEdgeVertices);
479                 disp_primitive_set->push_back(index + 1);
480                 disp_primitive_set->push_back(index + numEdgeVertices + 1);
481                 disp_primitive_set->push_back(index + numEdgeVertices);
482             }
483             else
484             {
485                 // Add the vertex offset (so we reference this surface's
486                 // vertices in the array)
487                 index += firstVertex;
488 
489                 // Create two triangles on this vertex from bottom-left to
490                 // top-right
491                 disp_primitive_set->push_back(index);
492                 disp_primitive_set->push_back(index + numEdgeVertices + 1);
493                 disp_primitive_set->push_back(index + numEdgeVertices);
494                 disp_primitive_set->push_back(index);
495                 disp_primitive_set->push_back(index + 1);
496                 disp_primitive_set->push_back(index + numEdgeVertices + 1);
497             }
498         }
499     }
500 }
501 
502 
addFace(int faceIndex)503 void VBSPGeometry::addFace(int faceIndex)
504 {
505     Face                  currentFace;
506     Edge                  currentEdge;
507     DisplaceInfo          currentDispInfo;
508     TexInfo               currentTexInfo;
509     TexData               currentTexData;
510     Vec3                  normal;
511     int                   edgeIndex;
512     int                   i;
513     int                   currentSurfEdge;
514     Vec3                  currentVertex;
515     Vec3                  texU;
516     float                 texUOffset;
517     float                 texUScale;
518     Vec3                  texV;
519     float                 texVOffset;
520     float                 texVScale;
521     float                 u, v;
522     Vec2f                 texCoord;
523 
524     // Make sure this face is not "on node" (an internal node of the BSP tree).
525     // These faces are not used for visible geometry
526     currentFace = bsp_data->getFace(faceIndex);
527 
528     // See if this is a displacement surface
529     if (currentFace.dispinfo_index != -1)
530     {
531         // Get the displacement info
532         currentDispInfo =
533             bsp_data->getDispInfo(currentFace.dispinfo_index);
534 
535         // Generate the displacement surface
536         createDispSurface(currentFace, currentDispInfo);
537     }
538     else
539     {
540         // Get the face normal, using the plane information
541         normal = bsp_data->getPlane(currentFace.plane_index).plane_normal;
542         if (currentFace.plane_side != 0)
543             normal = -normal;
544 
545         // Get the texture info and data structures
546         currentTexInfo = bsp_data->getTexInfo(currentFace.texinfo_index);
547         currentTexData = bsp_data->getTexData(currentTexInfo.texdata_index);
548 
549         // Get the texture vectors and offsets.  These are used to calculate
550         // texture coordinates
551         texU.set(currentTexInfo.texture_vecs[0][0],
552                  currentTexInfo.texture_vecs[0][1],
553                  currentTexInfo.texture_vecs[0][2]);
554         texUOffset = currentTexInfo.texture_vecs[0][3];
555         texV.set(currentTexInfo.texture_vecs[1][0],
556                  currentTexInfo.texture_vecs[1][1],
557                  currentTexInfo.texture_vecs[1][2]);
558         texVOffset = currentTexInfo.texture_vecs[1][3];
559 
560         // Scale the texture vectors from inches to meters
561         texU *= 39.37f;
562         texV *= 39.37f;
563 
564         // Get the texture size, as the planar texture projection results in
565         // non-normalized texture coordinates
566         texUScale = 1.0f / (float)currentTexData.texture_width;
567         texVScale = 1.0f / (float)currentTexData.texture_height;
568 
569         // Start with the last edge index, because we need to switch from
570         // clockwise winding (DirectX) to counter-clockwise winding (OpenGL)
571         edgeIndex = currentFace.first_edge + currentFace.num_edges - 1;
572 
573         // Set the length of this primitive on the primitive set
574         primitive_set->push_back(currentFace.num_edges);
575 
576         // Iterate over the edges in this face, and extract the vertex data
577         for (i = 0; i < currentFace.num_edges; i++)
578         {
579             // Look up the edge specified by the surface edge index, the
580             // index might be negative (see below), so take the absolute
581             // value
582             currentSurfEdge = bsp_data->getSurfaceEdge(edgeIndex);
583             currentEdge = bsp_data->getEdge(abs(currentSurfEdge));
584 
585             // The sign of the surface edge index specifies which vertex is
586             // "first" for this face.  A negative index means the edge should
587             // be flipped, and the second vertex treated as the first
588             if (currentSurfEdge < 0)
589                 currentVertex = bsp_data->getVertex(currentEdge.vertex[1]);
590             else
591                 currentVertex = bsp_data->getVertex(currentEdge.vertex[0]);
592 
593             // Add the vertex to the array
594             vertex_array->push_back(currentVertex);
595 
596             // Set the normal
597             normal_array->push_back(normal);
598 
599             // Calculate the texture coordinates for this vertex
600             u = texU * currentVertex + texUOffset;
601             u *= texUScale;
602             v = texV * currentVertex + texVOffset;
603             v *= texVScale;
604             texCoord.set(u, v);
605 
606             // Add the texture coordinate to the array
607             texcoord_array->push_back(texCoord);
608 
609             // Move on to the next (previous?) vertex
610             edgeIndex--;
611         }
612     }
613 }
614 
615 
createGeometry()616 ref_ptr<Group> VBSPGeometry::createGeometry()
617 {
618     ref_ptr<Group>       rootGroup;
619     ref_ptr<Geode>       geode;
620     ref_ptr<Geometry>    geometry;
621     Vec4f                color;
622     ref_ptr<Vec4Array>   colorArray;
623 
624     // Create the root group (we'll attach everything to this group and
625     // return it)
626     rootGroup = new Group();
627 
628     // Create a geode for the geometries
629     geode = new Geode();
630     rootGroup->addChild(geode.get());
631 
632     // See if there are any regular (non-displaced) faces to render
633     if (primitive_set->size() > 0)
634     {
635         // Create a geometry object for the regular surfaces
636         geometry = new Geometry();
637 
638         // Add the vertex attributes
639         geometry->setVertexArray(vertex_array.get());
640         geometry->setNormalArray(normal_array.get(), Array::BIND_PER_VERTEX);
641         geometry->setTexCoordArray(0, texcoord_array.get());
642 
643         // Add an overall color
644         color.set(1.0, 1.0, 1.0, 1.0);
645         colorArray = new Vec4Array(1, &color);
646         geometry->setColorArray(colorArray.get(), Array::BIND_OVERALL);
647 
648         // Add our primitive set to the geometry
649         geometry->addPrimitiveSet(primitive_set.get());
650 
651         // Add the geometry to the geode
652         geode->addDrawable(geometry.get());
653 
654         // Now, stripify the geode to convert the POLYGON primitives to
655         // triangle strips
656         osgUtil::TriStripVisitor tsv;
657         geode->accept(tsv);
658         tsv.stripify();
659     }
660 
661     // Now do the same for the displacement surfaces (if any)
662     if (disp_primitive_set->size() > 0)
663     {
664         // Create a geometry object for the regular surfaces
665         geometry = new Geometry();
666 
667         // Add the vertex attributes
668         geometry->setVertexArray(disp_vertex_array.get());
669         geometry->setNormalArray(disp_normal_array.get(), Array::BIND_PER_VERTEX);
670         geometry->setColorArray(disp_vertex_attr_array.get(), Array::BIND_PER_VERTEX);
671         geometry->setTexCoordArray(0, disp_texcoord_array.get());
672         geometry->setTexCoordArray(1, disp_texcoord_array.get());
673 
674         // Add our primitive set to the geometry
675         geometry->addPrimitiveSet(disp_primitive_set.get());
676 
677         // Add the geometry to the geode
678         geode->addDrawable(geometry.get());
679     }
680 
681     // Return the root group
682     return rootGroup;
683 }
684 
685