1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 #ifdef HAVE_CONFIG_H
34 #include <config.h>
35 #endif // HAVE_CONFIG_H
36 
37 #ifdef HAVE_VRML97
38 
39 /*!
40   \class SoVRMLExtrusion SoVRMLExtrusion.h Inventor/VRMLnodes/SoVRMLExtrusion.h
41   \brief The SoVRMLExtrusion class is a a geometry node for extruding a cross section along a spine.
42 
43   \ingroup VRMLnodes
44 
45   \WEB3DCOPYRIGHT
46 
47   \verbatim
48   Extrusion {
49     eventIn MFVec2f    set_crossSection
50     eventIn MFRotation set_orientation
51     eventIn MFVec2f    set_scale
52     eventIn MFVec3f    set_spine
53     field   SFBool     beginCap         TRUE
54     field   SFBool     ccw              TRUE
55     field   SFBool     convex           TRUE
56     field   SFFloat    creaseAngle      0                # [0,inf)
57     field   MFVec2f    crossSection     [ 1 1, 1 -1, -1 -1, -1 1, 1  1 ]    # (-inf,inf)
58     field   SFBool     endCap           TRUE
59     field   MFRotation orientation      0 0 1 0          # [-1,1],(-inf,inf)
60     field   MFVec2f    scale            1 1              # (0,inf)
61     field   SFBool     solid            TRUE
62     field   MFVec3f    spine            [ 0 0 0, 0 1 0 ] # (-inf,inf)
63   }
64   \endverbatim
65 
66   \e Introduction
67 
68   The Extrusion node specifies geometric shapes based on a two
69   dimensional cross-section extruded along a three dimensional spine
70   in the local coordinate system. The cross-section can be scaled and
71   rotated at each spine point to produce a wide variety of shapes.  An
72   Extrusion node is defined by:
73 
74   \li a 2D crossSection piecewise linear curve (described as a series
75   of connected vertices);
76 
77   \li a 3D spine piecewise linear curve (also described as a series
78   of connected vertices);
79 
80   \li a list of 2D scale parameters;
81 
82   \li a list of 3D orientation parameters.
83 
84   \e Algorithmic \e description
85 
86   Shapes are constructed as follows. The cross-section curve, which
87   starts as a curve in the Y=0 plane, is first scaled about the origin
88   by the first scale parameter (first value scales in X, second value
89   scales in Z). It is then translated by the first spine point and
90   oriented using the first orientation parameter (as explained
91   later). The same procedure is followed to place a cross- section at
92   the second spine point, using the second scale and orientation
93   values. Corresponding vertices of the first and second
94   cross-sections are then connected, forming a quadrilateral polygon
95   between each pair of vertices. This same procedure is then repeated
96   for the rest of the spine points, resulting in a surface extrusion
97   along the spine.
98 
99   The final orientation of each cross-section is computed by first
100   orienting it relative to the spine segments on either side of point
101   at which the cross-section is placed. This is known as the
102   spine-aligned cross-section plane (SCP), and is designed to provide
103   a smooth transition from one spine segment to the next (see Figure
104   6.6). The SCP is then rotated by the corresponding orientation
105   value. This rotation is performed relative to the SCP. For example,
106   to impart twist in the cross- section, a rotation about the Y-axis
107   (0 1 0) would be used. Other orientations are valid and rotate the
108   cross-section out of the SCP.
109 
110   <center>
111   <img src="http://www.web3d.org/documents/specifications/14772/V2.0/Images/Extrusion.gif">
112   Figure 6.6
113   </center>
114 
115   The SCP is computed by first computing its Y-axis and Z-axis, then
116   taking the cross product of these to determine the X-axis. These
117   three axes are then used to determine the rotation value needed to
118   rotate the Y=0 plane to the SCP. This results in a plane that is the
119   approximate tangent of the spine at each point, as shown in Figure
120   6.6. First the Y-axis is determined, as follows:
121 
122   Let n be the number of spines and let i be the index variable
123   satisfying 0 <= i < n:
124 
125   \li For all points other than the first or last: The Y-axis for
126   spine[i] is found by normalizing the vector defined by (spine[i+1]
127   - spine[i-1]).
128 
129   \li If the spine curve is closed: The SCP for the first and last
130   points is the same and is found using (spine[1] - spine[n-2])
131   to compute the Y-axis.
132 
133   \li If the spine curve is not closed: The Y-axis used for the
134   first point is the vector from spine[0] to spine[1], and for the
135   last it is the vector from spine[n-2] to spine[n-1].
136 
137   The Z-axis is determined as follows:
138 
139   \li For all points other than the first or last: Take the following
140   cross-product:
141 
142   \verbatim
143   Z = (spine[i+1] - spine[i]) � (spine[i-1] - spine[i])
144   \endverbatim
145 
146   \li If the spine curve is closed: The SCP for the first and last
147   points is the same and is found by taking the following cross- product:
148 
149   \verbatim
150   Z = (spine[1] - spine[0]) � (spine[n-2] - spine[0])
151   \endverbatim
152 
153   \li If the spine curve is not closed: The Z-axis used for the first
154   spine point is the same as the Z-axis for spine[1]. The Z- axis used for
155   the last spine point is the same as the Z-axis for spine[n-2].
156 
157   \li After determining the Z-axis, its dot product with the Z-axis of the
158   previous spine point is computed. If this value is negative, the
159   Z-axis is flipped (multiplied by -1). In most cases, this prevents
160   small changes in the spine segment angles from flipping the
161   cross-section 180 degrees.
162 
163   Once the Y- and Z-axes have been computed, the X-axis can be
164   calculated as their cross-product.
165 
166   \e Special \e Cases
167 
168   If the number of scale or orientation values is greater than the
169   number of spine points, the excess values are ignored. If they
170   contain one value, it is applied at all spine points. The results
171   are undefined if the number of scale or orientation values is
172   greater than one but less than the number of spine points. The scale
173   values shall be positive.
174 
175   If the three points used in computing the Z-axis are collinear, the
176   cross-product is zero so the value from the previous point is used
177   instead.  If the Z-axis of the first point is undefined (because the
178   spine is not closed and the first two spine segments are collinear)
179   then the Z-axis for the first spine point with a defined Z-axis is
180   used.
181 
182   If the entire spine is collinear, the SCP is computed by finding the
183   rotation of a vector along the positive Y-axis (v1) to the vector
184   formed by the spine points (v2). The Y=0 plane is then rotated by
185   this value.  If two points are coincident, they both have the same
186   SCP. If each point has a different orientation value, then the
187   surface is constructed by connecting edges of the cross-sections as
188   normal. This is useful in creating revolved surfaces.
189 
190   Note: combining coincident and non-coincident spine segments, as
191   well as other combinations, can lead to interpenetrating surfaces
192   which the extrusion algorithm makes no attempt to avoid.
193 
194   \e Common \e Cases
195 
196   The following common cases are among the effects which are supported
197   by the Extrusion node:
198 
199   \li Surfaces of revolution: If the cross-section is an approximation
200   of a circle and the spine is straight, the Extrusion is equivalent
201   to a surface of revolution, where the scale parameters define the
202   size of the cross-section along the spine.
203 
204   \li Uniform extrusions: If the scale is (1, 1) and the spine is
205   straight, the cross-section is extruded uniformly without twisting
206   or scaling along the spine. The result is a cylindrical shape with a
207   uniform cross section.
208 
209   \li Bend/twist/taper objects: These shapes are the result of using
210   all fields. The spine curve bends the extruded shape defined by the
211   cross-section, the orientation parameters (given as rotations about
212   the Y-axis) twist it around the spine, and the scale parameters
213   taper it (by scaling about the spine).
214 
215   \e Other \e Fields
216 
217   Extrusion has three parts: the sides, the beginCap (the
218   surface at the initial end of the spine) and the endCap (the surface
219   at the final end of the spine). The caps have an associated SFBool field
220   that indicates whether each exists (TRUE) or doesn't exist (FALSE).
221 
222   When the beginCap or endCap fields are specified as TRUE, planar cap
223   surfaces will be generated regardless of whether the crossSection is
224   a closed curve. If crossSection is not a closed curve, the caps are
225   generated by adding a final point to crossSection that is equal to
226   the initial point. An open surface can still have a cap, resulting
227   (for a simple case) in a shape analogous to a soda can sliced in
228   half vertically.  These surfaces are generated even if spine is also
229   a closed curve.  If a field value is FALSE, the corresponding cap is
230   not generated.
231 
232   Texture coordinates are automatically generated by Extrusion
233   nodes. Textures are mapped so that the coordinates range in the U
234   direction from 0 to 1 along the crossSection curve (with 0
235   corresponding to the first point in crossSection and 1 to the last)
236   and in the V direction from 0 to 1 along the spine curve (with 0
237   corresponding to the first listed spine point and 1 to the last). If
238   either the endCap or beginCap exists, the crossSection curve is
239   uniformly scaled and translated so that the larger dimension of the
240   cross-section (X or Z) produces texture coordinates that range from
241   0.0 to 1.0. The beginCap and endCap textures' S and T directions
242   correspond to the X and Z directions in which the crossSection
243   coordinates are defined.
244 
245   The browser shall automatically generate normals for the Extrusion
246   node,using the creaseAngle field to determine if and how normals are
247   smoothed across the surface. Normals for the caps are generated
248   along the Y-axis of the SCP, with the ordering determined by viewing
249   the cross-section from above (looking along the negative Y-axis of
250   the SCP). By default, a beginCap with a counterclockwise ordering
251   shall have a normal along the negative Y-axis. An endCap with a
252   counterclockwise ordering shall have a normal along the positive
253   Y-axis.
254 
255   Each quadrilateral making up the sides of the extrusion are ordered
256   from the bottom cross-section (the one at the earlier spine point)
257   to the top.  So, one quadrilateral has the points:
258 
259   \verbatim
260   spine[0](crossSection[0], crossSection[1])
261   spine[1](crossSection[1], crossSection[0])
262   \endverbatim
263 
264   in that order. By default, normals for the sides are generated as
265   described in 4.6.3, Shapes and geometry
266   (<http://www.web3d.org/documents/specifications/14772/V2.0/part1/concepts.html#4.6.3>).
267 
268   For instance, a circular crossSection with counter-clockwise
269   ordering and the default spine form a cylinder. With solid TRUE and
270   ccw TRUE, the cylinder is visible from the outside. Changing ccw to
271   FALSE makes it visible from the inside.  The ccw, solid, convex, and
272   creaseAngle fields are described in 4.6.3, Shapes and geometry
273   (<http://www.web3d.org/documents/specifications/14772/V2.0/part1/concepts.html#4.6.3>).
274 
275 */
276 
277 /*!
278   SoSFBool SoVRMLExtrusion::beginCap
279   Used to enable/disable begin cap. Default value is TRUE.
280 */
281 
282 /*!
283   SoSFBool SoVRMLExtrusion::ccw
284   Specifies counterclockwise vertex ordering. Default value is TRUE.
285 */
286 
287 /*!
288   SoSFBool SoVRMLExtrusion::convex
289   Specifies if cross sections is convex. Default value is TRUE.
290 */
291 
292 /*!
293   SoSFFloat SoVRMLExtrusion::creaseAngle
294   Specifies the crease angle for the generated normals. Default value is 0.0.
295 */
296 
297 /*!
298   SoMFVec2f SoVRMLExtrusion::crossSection
299   The cross section.
300 */
301 
302 /*!
303   SoSFBool SoVRMLExtrusion::endCap
304   Used to enable/disable end cap. Default value is TRUE.
305 
306 */
307 
308 /*!
309   SoMFRotation SoVRMLExtrusion::orientation
310   Orientation for the cross section at each spine point.
311 */
312 
313 /*!
314   SoMFVec2f SoVRMLExtrusion::scale
315   Scaling for the cross section at each spine point.
316 */
317 
318 /*!
319   SoSFBool SoVRMLExtrusion::solid
320   When TRUE, backface culling will be enabled. Default value is TRUE.
321 */
322 
323 /*!
324   SoMFVec3f SoVRMLExtrusion::spine
325   The spine points.
326 */
327 
328 #include <Inventor/VRMLnodes/SoVRMLExtrusion.h>
329 
330 #include <cfloat>
331 #include <cmath>
332 #include <cstring>
333 
334 #include <Inventor/VRMLnodes/SoVRMLMacros.h>
335 #include <Inventor/lists/SbList.h>
336 #include <Inventor/misc/SoNormalGenerator.h>
337 #include <Inventor/bundles/SoMaterialBundle.h>
338 #include <Inventor/bundles/SoTextureCoordinateBundle.h>
339 #include <Inventor/bundles/SoVertexAttributeBundle.h>
340 #include <Inventor/elements/SoCoordinateElement.h>
341 #include <Inventor/elements/SoVertexAttributeBindingElement.h>
342 #include <Inventor/elements/SoGLCacheContextElement.h>
343 #include <Inventor/elements/SoShapeHintsElement.h>
344 #include <Inventor/elements/SoCacheElement.h>
345 #include <Inventor/SbTesselator.h>
346 #include <Inventor/actions/SoGLRenderAction.h>
347 #include <Inventor/actions/SoGetPrimitiveCountAction.h>
348 #include <Inventor/misc/SoState.h>
349 #include <Inventor/misc/SoGLDriverDatabase.h>
350 #include <Inventor/nodes/SoIndexedFaceSet.h>
351 #include <Inventor/SoPrimitiveVertex.h>
352 #include <Inventor/errors/SoDebugError.h>
353 #include <Inventor/elements/SoMultiTextureEnabledElement.h>
354 #include <Inventor/elements/SoMultiTextureCoordinateElement.h>
355 #include <Inventor/SbBox2f.h>
356 #ifdef HAVE_THREADS
357 #include <Inventor/threads/SbRWMutex.h>
358 #endif // HAVE_THREADS
359 
360 #include "nodes/SoSubNodeP.h"
361 #include "rendering/SoVBO.h"
362 #include "rendering/SoVertexArrayIndexer.h"
363 #include "rendering/SoGL.h"
364 #include "misc/SbHash.h"
365 #include "caches/SoVBOCache.h"
366 
367 // *************************************************************************
368 
369 //
370 // needed to avoid warnings generated by SbVec3f::normalize
371 //
372 static float
my_normalize(SbVec3f & vec)373 my_normalize(SbVec3f & vec)
374 {
375   float len = vec.length();
376   if (len > FLT_EPSILON) {
377     vec /= len;
378   }
379   return len;
380 }
381 
382 // set this to TRUE to create triangles, even if convex == TRUE just
383 // testing this feature. Will consider an environment variable or
384 // something later. pederb, 2005-01-25
385 static const SbBool ALWAYS_CREATE_TRIANGLES = FALSE;
386 
387 class SoVRMLExtrusionVertex {
388 public:
389   SbVec3f coord;
390   SbVec3f normal;
391   SbVec2f texcoord;
392 
393   // needed for SbHash
operator unsigned long(void) const394   operator unsigned long(void) const {
395     unsigned long key = 0;
396     // create an xor key based on coordinates, normal and texcoords
397     const unsigned char * ptr = (const unsigned char *) this;
398     const ptrdiff_t size = sizeof(SoVRMLExtrusionVertex);
399 
400     for (int i = 0; i < size; i++) {
401       int shift = (i%4) * 8;
402       key ^= (ptr[i]<<shift);
403     }
404     return key;
405   }
406   // needed, since if we don't add this the unsigned long operator
407   // will be used when comparing two vertices.
operator ==(const SoVRMLExtrusionVertex & v)408   int operator==(const SoVRMLExtrusionVertex & v) {
409     return
410       (this->coord == v.coord) &&
411       (this->normal == v.normal) &&
412       (this->texcoord == v.texcoord);
413   }
414 };
415 
416 class SoVRMLExtrusionP {
417 public:
418 
SoVRMLExtrusionP(SoVRMLExtrusion * master)419   SoVRMLExtrusionP(SoVRMLExtrusion * master)
420     :master(master),
421      coord(32),
422      tcoord(32),
423      idx(32),
424      gen(TRUE),
425      dirty(TRUE),
426      vbocache(NULL)
427 #ifdef COIN_THREADSAFE
428      , rwmutex(SbRWMutex::READ_PRECEDENCE)
429 #endif // COIN_THREADSAFE
430   {
431     this->tess.setCallback(tess_callback, this);
432   }
~SoVRMLExtrusionP()433   ~SoVRMLExtrusionP() {
434     if (this->vbocache) this->vbocache->unref();
435   }
436 
437   SoVRMLExtrusion * master;
438   SbList <SbVec3f> coord;
439   SbList <SbVec2f> tcoord;
440   SbList <int32_t> idx;
441   SoNormalGenerator gen;
442   SbTesselator tess;
443   static void tess_callback(void *, void *, void *, void *);
444   void generateCoords(void);
445   void generateNormals(void);
446   SbBool dirty;
447   SoVBOCache * vbocache;
448 
449   SbHash<SoVRMLExtrusionVertex, int32_t> vbohash;
450 
451   SbList <SbVec3f> vbocoord;
452   SbList <SbVec3f> vbonormal;
453   SbList <SbVec2f> vbotexcoord;
454 
455   void updateVBO(SoAction * action);
456   void generateVBO(SoAction * action, SoTextureCoordinateBundle & tb);
457 
458 #ifdef COIN_THREADSAFE
459   SbRWMutex rwmutex;
readLock(void)460   void readLock(void) { this->rwmutex.readLock(); }
readUnlock(void)461   void readUnlock(void) { this->rwmutex.readUnlock(); }
writeLock(void)462   void writeLock(void) { this->rwmutex.writeLock(); }
writeUnlock(void)463   void writeUnlock(void) { this->rwmutex.writeUnlock(); }
464 #else // !COIN_THREADSAFE
readLock(void)465   void readLock(void) { }
readUnlock(void)466   void readUnlock(void) { }
writeLock(void)467   void writeLock(void) { }
writeUnlock(void)468   void writeUnlock(void) { }
469 #endif // !COIN_THREADSAFE
470 };
471 
472 #define PRIVATE(obj) (obj)->pimpl
473 #define PUBLIC(obj) obj->master
474 
475 // *************************************************************************
476 
477 SO_NODE_SOURCE(SoVRMLExtrusion);
478 
479 // *************************************************************************
480 
481 // Doc in parent
482 void
initClass(void)483 SoVRMLExtrusion::initClass(void) // static
484 {
485   SO_NODE_INTERNAL_INIT_CLASS(SoVRMLExtrusion, SO_VRML97_NODE_TYPE);
486 }
487 
488 /*!
489   Constructor.
490 */
SoVRMLExtrusion(void)491 SoVRMLExtrusion::SoVRMLExtrusion(void)
492 {
493   PRIVATE(this) = new SoVRMLExtrusionP(this);
494 
495   SO_VRMLNODE_INTERNAL_CONSTRUCTOR(SoVRMLExtrusion);
496 
497   SO_VRMLNODE_ADD_FIELD(beginCap, (TRUE));
498   SO_VRMLNODE_ADD_FIELD(endCap, (TRUE));
499   SO_VRMLNODE_ADD_FIELD(solid, (TRUE));
500   SO_VRMLNODE_ADD_FIELD(ccw, (TRUE));
501   SO_VRMLNODE_ADD_FIELD(convex, (TRUE));
502   SO_VRMLNODE_ADD_FIELD(creaseAngle, (0.0f));
503 
504   SO_NODE_ADD_FIELD(crossSection, (0.0f, 0.0f));
505   this->crossSection.setNum(5);
506   SbVec2f * cs = this->crossSection.startEditing();
507   cs[0] = SbVec2f(1.0f, 1.0f);
508   cs[1] = SbVec2f(1.0f, -1.0f);
509   cs[2] = SbVec2f(-1.0f, -1.0f);
510   cs[3] = SbVec2f(-1.0f, 1.0f);
511   cs[4] = SbVec2f(1.0f, 1.0f);
512   this->crossSection.finishEditing();
513   this->crossSection.setDefault(TRUE);
514 
515   SO_NODE_ADD_FIELD(orientation, (SbRotation::identity()));
516   SO_NODE_ADD_FIELD(scale, (1.0f, 1.0f));
517 
518   SO_NODE_ADD_FIELD(spine, (0.0f, 0.0f, 0.0f));
519   this->spine.setNum(2);
520   this->spine.set1Value(1, 0.0f, 1.0f, 0.0f);
521   this->spine.setDefault(TRUE);
522 }
523 
524 /*!
525   Destructor.
526 */
~SoVRMLExtrusion()527 SoVRMLExtrusion::~SoVRMLExtrusion()
528 {
529   delete PRIVATE(this);
530 }
531 
532 
533 // Doc in parent
534 void
GLRender(SoGLRenderAction * action)535 SoVRMLExtrusion::GLRender(SoGLRenderAction * action)
536 {
537   if (!this->shouldGLRender(action)) return;
538 
539   SoState * state = action->getState();
540   state->push();
541 
542   this->setupShapeHints(state, this->ccw.getValue(), this->solid.getValue());
543 
544   PRIVATE(this)->readLock();
545 
546   this->updateCache();
547 
548   if ((SoMultiTextureCoordinateElement::getType(state) !=
549        SoMultiTextureCoordinateElement::FUNCTION) &&
550       (SoMultiTextureCoordinateElement::getType(state) !=
551        SoMultiTextureCoordinateElement::TEXGEN)) {
552     SoGLMultiTextureCoordinateElement::setTexGen(state, this, NULL);
553     SoMultiTextureCoordinateElement::set2(state, this, PRIVATE(this)->tcoord.getLength(),
554                                           PRIVATE(this)->tcoord.getArrayPtr());
555   }
556   const uint32_t contextid = SoGLCacheContextElement::get(state);
557   const cc_glglue * glue = cc_glglue_instance(contextid);
558   SbBool vbo = SoVBO::shouldCreateVBO(state, contextid, PRIVATE(this)->coord.getLength());
559 
560   if (vbo) PRIVATE(this)->updateVBO(action);
561 
562   SoMaterialBundle mb(action);
563   mb.sendFirst();
564 
565   SbBool doTextures = SoMultiTextureEnabledElement::get(state);
566 
567   if (vbo) {
568     if (!SoGLDriverDatabase::isSupported(glue, SO_GL_VBO_IN_DISPLAYLIST)) {
569       SoCacheElement::invalidate(state);
570       SoGLCacheContextElement::shouldAutoCache(state,
571                                                SoGLCacheContextElement::DONT_AUTO_CACHE);
572     }
573     int i;
574     int lastenabled = -1;
575     const SbBool * enabled = SoMultiTextureEnabledElement::getEnabledUnits(state, lastenabled);
576 
577     if (doTextures) {
578       PRIVATE(this)->vbocache->getTexCoordVBO(0)->bindBuffer(contextid);
579       cc_glglue_glTexCoordPointer(glue, 2, GL_FLOAT, 0, NULL);
580       cc_glglue_glEnableClientState(glue, GL_TEXTURE_COORD_ARRAY);
581 
582       for (i = 1; i <= lastenabled; i++) {
583         if (enabled[i]) {
584           cc_glglue_glClientActiveTexture(glue, GL_TEXTURE0 + i);
585           cc_glglue_glTexCoordPointer(glue, 2, GL_FLOAT, 0, NULL);
586           cc_glglue_glEnableClientState(glue, GL_TEXTURE_COORD_ARRAY);
587         }
588       }
589       cc_glglue_glClientActiveTexture(glue, GL_TEXTURE0);
590     }
591 
592     PRIVATE(this)->vbocache->getNormalVBO()->bindBuffer(contextid);
593     cc_glglue_glNormalPointer(glue, GL_FLOAT, 0, NULL);
594     cc_glglue_glEnableClientState(glue, GL_NORMAL_ARRAY);
595 
596     PRIVATE(this)->vbocache->getCoordVBO()->bindBuffer(contextid);
597     cc_glglue_glVertexPointer(glue, 3, GL_FLOAT, 0, NULL);
598     cc_glglue_glEnableClientState(glue, GL_VERTEX_ARRAY);
599 
600     SoGLVertexAttributeElement::getInstance(state)->enableVBO(action);
601 
602     PRIVATE(this)->vbocache->getVertexArrayIndexer()->render(glue, TRUE, contextid);
603 
604     cc_glglue_glBindBuffer(glue, GL_ARRAY_BUFFER, 0); // Reset VBO binding
605     cc_glglue_glDisableClientState(glue, GL_NORMAL_ARRAY);
606     cc_glglue_glDisableClientState(glue, GL_VERTEX_ARRAY);
607 
608     SoGLVertexAttributeElement::getInstance(state)->disableVBO(action);
609 
610     if (doTextures) {
611       for (i = 1; i <= lastenabled; i++) {
612         if (enabled[i]) {
613           cc_glglue_glClientActiveTexture(glue, GL_TEXTURE0 + i);
614           cc_glglue_glDisableClientState(glue, GL_TEXTURE_COORD_ARRAY);
615         }
616       }
617       cc_glglue_glClientActiveTexture(glue, GL_TEXTURE0);
618       cc_glglue_glDisableClientState(glue, GL_TEXTURE_COORD_ARRAY);
619     }
620   }
621   else {
622     const SbVec3f * normals = PRIVATE(this)->gen.getNormals();
623 
624     SoCoordinateElement::set3(state, this, PRIVATE(this)->coord.getLength(), PRIVATE(this)->coord.getArrayPtr());
625     const SoCoordinateElement * coords = SoCoordinateElement::getInstance(state);
626 
627     if (doTextures) {
628       int lastenabled = -1;
629       const SbBool * enabled = SoMultiTextureEnabledElement::getEnabledUnits(state, lastenabled);
630       for (int i = 1; i <= lastenabled; i++) {
631         if (enabled[i] && (SoMultiTextureCoordinateElement::getType(state, i) !=
632                            SoMultiTextureCoordinateElement::FUNCTION)) {
633           SoMultiTextureCoordinateElement::set2(state, this, i,
634                                                 PRIVATE(this)->tcoord.getLength(),
635                                                 PRIVATE(this)->tcoord.getArrayPtr());
636         }
637       }
638     }
639 
640     SoTextureCoordinateBundle tb(action, TRUE, FALSE);
641     doTextures = tb.needCoordinates();
642 
643     SoVertexAttributeBundle vab(action, TRUE);
644     SbBool doattribs = vab.doAttributes();
645 
646     SoVertexAttributeBindingElement::Binding attribbind =
647       SoVertexAttributeBindingElement::get(state);
648 
649     if (!doattribs) {
650       // for overall attribute binding we check for doattribs before
651       // sending anything in SoGL::FaceSet::GLRender
652       attribbind = SoVertexAttributeBindingElement::OVERALL;
653     }
654 
655     sogl_render_faceset((SoGLCoordinateElement *) coords,
656                         PRIVATE(this)->idx.getArrayPtr(),
657                         PRIVATE(this)->idx.getLength(),
658                         normals,
659                         NULL,
660                         &mb,
661                         NULL,
662                         &tb,
663                         PRIVATE(this)->idx.getArrayPtr(),
664                         &vab,
665                         3, /* SoIndexedFaceSet::PER_VERTEX */
666                         0,
667                         (int) attribbind,
668                         doTextures ? 1 : 0,
669                         doattribs ? 1 : 0);
670 
671   }
672   PRIVATE(this)->readUnlock();
673 
674   state->pop();
675 
676   // send approx number of triangles for autocache handling
677   sogl_autocache_update(state, PRIVATE(this)->idx.getLength() / 4,
678                         vbo);
679 }
680 
681 // Doc in parent
682 void
getPrimitiveCount(SoGetPrimitiveCountAction * action)683 SoVRMLExtrusion::getPrimitiveCount(SoGetPrimitiveCountAction * action)
684 {
685   PRIVATE(this)->readLock();
686   this->updateCache();
687   action->addNumTriangles(PRIVATE(this)->idx.getLength() / 4);
688   PRIVATE(this)->readUnlock();
689 }
690 
691 // Doc in parent
692 void
computeBBox(SoAction * COIN_UNUSED_ARG (action),SbBox3f & box,SbVec3f & center)693 SoVRMLExtrusion::computeBBox(SoAction * COIN_UNUSED_ARG(action),
694                              SbBox3f & box,
695                              SbVec3f & center)
696 {
697   PRIVATE(this)->readLock();
698 
699   this->updateCache();
700 
701   int num = PRIVATE(this)->coord.getLength();
702   const SbVec3f * coords = PRIVATE(this)->coord.getArrayPtr();
703 
704   box.makeEmpty();
705   while (num--) {
706     box.extendBy(*coords++);
707   }
708   if (!box.isEmpty()) center = box.getCenter();
709   PRIVATE(this)->readUnlock();
710 }
711 
712 // Doc in parent
713 void
generatePrimitives(SoAction * action)714 SoVRMLExtrusion::generatePrimitives(SoAction * action)
715 {
716   PRIVATE(this)->readLock();
717   this->updateCache();
718 
719   const SbVec3f * normals = PRIVATE(this)->gen.getNormals();
720   const SbVec2f * tcoords = PRIVATE(this)->tcoord.getArrayPtr();
721   const SbVec3f * coords = PRIVATE(this)->coord.getArrayPtr();
722   const int32_t * iptr = PRIVATE(this)->idx.getArrayPtr();
723   const int32_t * endptr = iptr + PRIVATE(this)->idx.getLength();
724 
725   SoState * state = action->getState();
726   state->push();
727 
728   int lastenabled = -1;
729   const SbBool * enabled = SoMultiTextureEnabledElement::getEnabledUnits(state, lastenabled);
730   for (int i = 1; i <= lastenabled; i++) {
731     if (enabled[i] && (SoMultiTextureCoordinateElement::getType(state, i) !=
732                        SoMultiTextureCoordinateElement::FUNCTION)) {
733       SoMultiTextureCoordinateElement::set2(state, this, i,
734                                             PRIVATE(this)->tcoord.getLength(),
735                                             PRIVATE(this)->tcoord.getArrayPtr());
736     }
737   }
738   SoShapeHintsElement::set(state, this,
739                            this->ccw.getValue() ?
740                            SoShapeHintsElement::COUNTERCLOCKWISE :
741                            SoShapeHintsElement::CLOCKWISE,
742                            this->solid.getValue() ?
743                            SoShapeHintsElement::SOLID :
744                            SoShapeHintsElement::UNKNOWN_SHAPE_TYPE,
745                            this->convex.getValue() ?
746                            SoShapeHintsElement::CONVEX :
747                            SoShapeHintsElement::UNKNOWN_FACE_TYPE);
748 
749   SoTextureCoordinateBundle tb(action, FALSE, FALSE);
750   SbBool istexfunc = tb.isFunction();
751   SoPrimitiveVertex vertex;
752 
753   this->beginShape(action, TRIANGLES);
754   TriangleShape shapetype = LINES; // set it to some impossible value
755 
756   int idx;
757   while (iptr < endptr) {
758 
759     // we generate either triangles or quads, so this test is safe
760     SbBool isquad = iptr[3] >= 0;
761     if (isquad && (shapetype != QUADS)) {
762       if (shapetype == TRIANGLES) this->endShape();
763       this->beginShape(action, QUADS);
764       shapetype = QUADS;
765     }
766     if (!isquad && (shapetype != TRIANGLES)) {
767       if (shapetype == QUADS) this->endShape();
768       this->beginShape(action, TRIANGLES);
769       shapetype = TRIANGLES;
770     }
771     idx = *iptr++;
772     while (idx >= 0) {
773       vertex.setNormal(*normals);
774       vertex.setPoint(coords[idx]);
775       if (istexfunc) {
776         vertex.setTextureCoords(tb.get(coords[idx], *normals));
777       }
778       else {
779         vertex.setTextureCoords(tcoords[idx]);
780       }
781       this->shapeVertex(&vertex);
782       idx = *iptr++;
783       normals++;
784     }
785   }
786   if ((shapetype == TRIANGLES) || (shapetype == QUADS)) this->endShape();
787 
788   state->pop();
789   PRIVATE(this)->readUnlock();
790 }
791 
792 // private method that updates the coordinate and normal cache.
793 // cache must be read-locked when entering here!
794 void
updateCache(void)795 SoVRMLExtrusion::updateCache(void)
796 {
797   if (PRIVATE(this)->dirty) {
798     PRIVATE(this)->readUnlock();
799     PRIVATE(this)->writeLock();
800     PRIVATE(this)->generateCoords();
801     PRIVATE(this)->generateNormals();
802     PRIVATE(this)->dirty = FALSE;
803     PRIVATE(this)->writeUnlock();
804     PRIVATE(this)->readLock();
805   }
806 }
807 
808 void
updateVBO(SoAction * action)809 SoVRMLExtrusionP::updateVBO(SoAction * action)
810 {
811   if (this->vbocache == NULL || !this->vbocache->isValid(action->getState())) {
812     this->readUnlock();
813     SoTextureCoordinateBundle tb(action, FALSE, FALSE);
814     SbBool istexfunc = tb.isFunction();
815     if (istexfunc) {
816       // trigger a texture coordinate function callback to update (for
817       // instance) bounding box caches in texture function nodes. It's
818       // important that this is done before we writeLock() the node.
819       (void) tb.get(SbVec3f(0.0f, 0.0f, 0.0f), SbVec3f(0.0f, 0.0f, 1.0f));
820     }
821     this->writeLock();
822     this->generateVBO(action, tb);
823     this->writeUnlock();
824     this->readLock();
825   }
826 }
827 
828 void
generateVBO(SoAction * action,SoTextureCoordinateBundle & tb)829 SoVRMLExtrusionP::generateVBO(SoAction * action, SoTextureCoordinateBundle & tb)
830 {
831   SbBool storedinvalid = SoCacheElement::setInvalid(FALSE);
832 
833   SoState * state = action->getState();
834 
835   state->push();
836 
837   if (this->vbocache) {
838     this->vbocache->unref();
839   }
840   this->vbocache = new SoVBOCache(state);
841   this->vbocache->ref();
842 
843   // set active cache to record cache dependencies
844   SoCacheElement::set(state, this->vbocache);
845 
846   // create a dependency on the texture coordinate element
847   (void) SoMultiTextureCoordinateElement::getType(state);
848 
849   SbBool istexfunc = tb.isFunction();
850 
851   const SbVec3f * normals = this->gen.getNormals();
852   const SbVec2f * tcoords = this->tcoord.getArrayPtr();
853   const SbVec3f * coords = this->coord.getArrayPtr();
854   const int32_t * iptr = this->idx.getArrayPtr();
855   const int32_t * endptr = iptr + this->idx.getLength();
856 
857   this->vbohash.clear();
858   this->vbocoord.truncate(0);
859   this->vbonormal.truncate(0);
860   this->vbotexcoord.truncate(0);
861 
862   SoVRMLExtrusionVertex v;
863   int32_t vidx[4];
864   int curridx = 0;
865 
866   SoVertexArrayIndexer * vboindexer = this->vbocache->getVertexArrayIndexer(TRUE);
867 
868   while (iptr < endptr) {
869     // we generate either triangles or quads, so this test is safe
870     SbBool isquad = iptr[3] >= 0;
871 
872     for (int i = 0; i < (isquad ? 4 : 3); i++) {
873       int idx = *iptr++;
874       v.normal = *normals;
875       if (istexfunc) {
876         SbVec4f tmp = tb.get(coords[idx], *normals);
877         v.texcoord = SbVec2f(tmp[0]/tmp[3], tmp[1]/tmp[3]);
878       }
879       else {
880         v.texcoord = tcoords[idx];
881       }
882       v.coord = coords[idx];
883       normals++;
884 
885       if (!this->vbohash.get(v, vidx[i])) {
886         vidx[i] = curridx++;
887         this->vbohash.put(v, vidx[i]);
888         this->vbocoord.append(v.coord);
889         this->vbonormal.append(v.normal);
890         this->vbotexcoord.append(v.texcoord);
891       }
892     }
893     iptr++;
894     if (isquad) {
895       vboindexer->addQuad(vidx[0], vidx[1], vidx[2], vidx[3]);
896     }
897     else {
898       vboindexer->addTriangle(vidx[0], vidx[1], vidx[2]);
899     }
900   }
901   state->pop();
902   SoCacheElement::setInvalid(storedinvalid);
903 
904   this->vbohash.clear();
905   vboindexer->close();
906 
907   this->vbocache->getCoordVBO()->setBufferData(this->vbocoord.getArrayPtr(),
908                                                this->vbocoord.getLength()*sizeof(SbVec3f), 1);
909 
910   this->vbocache->getNormalVBO()->setBufferData(this->vbonormal.getArrayPtr(),
911                                                 this->vbonormal.getLength()*sizeof(SbVec3f), 1);
912   this->vbocache->getTexCoordVBO(0)->setBufferData(this->vbotexcoord.getArrayPtr(),
913                                                    this->vbotexcoord.getLength()*sizeof(SbVec2f), 1);
914 }
915 
916 
917 // Doc in parent
918 void
notify(SoNotList * list)919 SoVRMLExtrusion::notify(SoNotList * list)
920 {
921   if (PRIVATE(this)->vbocache) PRIVATE(this)->vbocache->invalidate();
922   PRIVATE(this)->dirty = TRUE;
923   inherited::notify(list);
924 }
925 
926 
927 // Doc in parent
928 SoDetail *
createTriangleDetail(SoRayPickAction * COIN_UNUSED_ARG (action),const SoPrimitiveVertex * COIN_UNUSED_ARG (v1),const SoPrimitiveVertex * COIN_UNUSED_ARG (v2),const SoPrimitiveVertex * COIN_UNUSED_ARG (v3),SoPickedPoint * COIN_UNUSED_ARG (pp))929 SoVRMLExtrusion::createTriangleDetail(SoRayPickAction * COIN_UNUSED_ARG(action),
930                                       const SoPrimitiveVertex * COIN_UNUSED_ARG(v1),
931                                       const SoPrimitiveVertex * COIN_UNUSED_ARG(v2),
932                                       const SoPrimitiveVertex * COIN_UNUSED_ARG(v3),
933                                       SoPickedPoint * COIN_UNUSED_ARG(pp))
934 {
935   // no triangle detail for Extrusion
936   return NULL;
937 }
938 
939 static SbVec3f
calculate_y_axis(const SbVec3f * spine,const int i,const int numspine,const SbBool closed)940 calculate_y_axis(const SbVec3f * spine, const int i,
941                  const int numspine, const SbBool closed)
942 {
943   SbVec3f Y;
944   if (closed) {
945     if (i > 0) {
946       if (i == numspine-1) {
947         Y = spine[1] - spine[i-1];
948       }
949       else {
950         Y = spine[i+1] - spine[i-1];
951       }
952     }
953     else {
954       // use numspine-2, since for closed spines, the last spine point == the first point
955       Y = spine[1] - spine[numspine >= 2 ? numspine-2 : numspine-1];
956     }
957   }
958   else {
959     if (i == 0) Y = spine[1] - spine[0];
960     else if (i == numspine-1) Y = spine[numspine-1] - spine[numspine-2];
961     else Y = spine[i+1] - spine[i-1];
962   }
963   my_normalize(Y);
964   return Y;
965 }
966 
967 static SbVec3f
calculate_z_axis(const SbVec3f * spine,const int i,const int numspine,const SbBool closed)968 calculate_z_axis(const SbVec3f * spine, const int i,
969                  const int numspine, const SbBool closed)
970 {
971   SbVec3f z0, z1;
972 
973   if (closed) {
974     if (i > 0) {
975       if (i == numspine-1) {
976         z0 = spine[1] - spine[i];
977         z1 = spine[i-1] - spine[i];
978       }
979       else {
980         z0 = spine[i+1] - spine[i];
981         z1 = spine[i-1] - spine[i];
982       }
983     }
984     else {
985       z0 = spine[1] - spine[0];
986       z1 = spine[numspine >= 2 ? numspine-2 : numspine-1] - spine[0];
987     }
988   }
989   else {
990     if (numspine == 2) return SbVec3f(0.0f, 0.0f, 0.0f);
991     else if (i == 0) {
992       z0 = spine[2] - spine[1];
993       z1 = spine[0] - spine[1];
994     }
995     else if (i == numspine-1) {
996       z0 = spine[numspine-1] - spine[numspine-2];
997       z1 = spine[numspine-3] - spine[numspine-2];
998     }
999     else {
1000       z0 = spine[i+1] - spine[i];
1001       z1 = spine[i-1] - spine[i];
1002     }
1003   }
1004 
1005   my_normalize(z0);
1006   my_normalize(z1);
1007 
1008   // test if spine segments are collinear. If they are, the cross
1009   // product will not be reliable, and we should just use the previous
1010   // Z-axis instead.
1011   if (SbAbs(z0.dot(z1)) > 0.999f) {
1012     return SbVec3f(0.0f, 0.0f, 0.0f);
1013   }
1014   SbVec3f tmp = z0.cross(z1);
1015   if (my_normalize(tmp) == 0.0f) {
1016     return SbVec3f(0.0f, 0.0f, 0.0f);
1017   }
1018   return tmp;
1019 }
1020 
1021 //
1022 // generates extruded coordinates
1023 //
1024 void
generateCoords(void)1025 SoVRMLExtrusionP::generateCoords(void)
1026 {
1027   this->coord.truncate(0);
1028   this->tcoord.truncate(0);
1029   this->idx.truncate(0);
1030 
1031   if (PUBLIC(this)->crossSection.getNum() == 0 ||
1032       PUBLIC(this)->spine.getNum() == 0) return;
1033 
1034   SbMatrix matrix = SbMatrix::identity();
1035 
1036   SbBox2f crossbox;
1037   crossbox.makeEmpty();
1038 
1039   int i, j, numcross;
1040   SbBool connected = FALSE;   // is cross section closed
1041   SbBool closed = FALSE;      // is spine closed
1042   numcross = PUBLIC(this)->crossSection.getNum();
1043   const SbVec2f * cross = PUBLIC(this)->crossSection.getValues(0);
1044   if (cross[0] == cross[numcross-1]) {
1045     connected = TRUE;
1046   }
1047 
1048   int numspine = PUBLIC(this)->spine.getNum();
1049   const SbVec3f * spine = PUBLIC(this)->spine.getValues(0);
1050   if (spine[0] == spine[numspine-1]) {
1051     closed = TRUE;
1052   }
1053 
1054   // calculate the length of the spine and cross section. Needed for
1055   // texture coordinates.
1056   float spinelen = 0.0f;
1057   float crosslen = 0.0f;
1058 
1059   for (i = 0; i < numspine-1; i++) {
1060     spinelen += (spine[i+1]-spine[i]).length();
1061   }
1062   if (spinelen == 0.0f) spinelen = 1.0f;
1063 
1064   for (i = 0; i < numcross-1; i++) {
1065     crosslen += (cross[i+1]-cross[i]).length();
1066   }
1067   if (crosslen == 0.0f) crosslen = 1.0f;
1068 
1069   SbVec3f prevY(0.0f, 0.0f, 0.0f);
1070   SbVec3f prevZ(0.0f, 0.0f, 0.0f);
1071   const SbVec3f empty(0.0f, 0.0f, 0.0f);
1072 
1073   SbBool colinear = FALSE;
1074   SbVec3f X, Y, Z;
1075 
1076   // find first non-collinear spine segments and calculate the first
1077   // valid Y and Z axis
1078   for (i = 0; i < numspine && (prevY == empty || prevZ == empty); i++) {
1079     if (prevY == empty) {
1080       Y = calculate_y_axis(spine, i, numspine, closed);
1081       if (Y != empty) prevY = Y;
1082     }
1083     if (prevZ == empty) {
1084       Z = calculate_z_axis(spine, i, numspine, closed);
1085       if (Z != empty) prevZ = Z;
1086     }
1087   }
1088 
1089   if (prevY == empty) prevY = SbVec3f(0.0f, 1.0f, 0.0f);
1090   if (prevZ == empty) { // all spine segments are colinear, calculate constant Z axis
1091     prevZ = SbVec3f(0.0f, 0.0f, 1.0f);
1092     if (prevY != SbVec3f(0.0f, 1.0f, 0.0f)) {
1093       SbRotation rot(SbVec3f(0.0f, 1.0f, 0.0f), prevY);
1094       rot.multVec(prevZ, prevZ);
1095     }
1096     colinear = TRUE;
1097   }
1098 
1099   int numorient = PUBLIC(this)->orientation.getNum();
1100   const SbRotation * orient = PUBLIC(this)->orientation.getValues(0);
1101 
1102   int numscale = PUBLIC(this)->scale.getNum();
1103   const SbVec2f * scale = PUBLIC(this)->scale.getValues(0);
1104 
1105   // calculate cross section bbox
1106   for (j = 0; j < numcross; j++) {
1107     crossbox.extendBy(cross[j]);
1108   }
1109 
1110   float currentspinelen = 0.0f; // for texcoords
1111 
1112   // loop through all spines
1113   for (i = 0; i < numspine; i++) {
1114     if (colinear) {
1115       Y = prevY;
1116       Z = prevZ;
1117     }
1118     else {
1119       Y = calculate_y_axis(spine, i, numspine, closed);
1120       Z = calculate_z_axis(spine, i, numspine, closed);
1121       if (Y == empty) Y = prevY;
1122       if (Z == empty) Z = prevZ;
1123       if (Z.dot(prevZ) < 0) Z = -Z;
1124     }
1125 
1126     X = Y.cross(Z);
1127     my_normalize(X);
1128 
1129     prevY = Y;
1130     prevZ = Z;
1131 
1132     matrix[0][0] = X[0];
1133     matrix[0][1] = X[1];
1134     matrix[0][2] = X[2];
1135     matrix[0][3] = 0.0f;
1136 
1137     matrix[1][0] = Y[0];
1138     matrix[1][1] = Y[1];
1139     matrix[1][2] = Y[2];
1140     matrix[1][3] = 0.0f;
1141 
1142     matrix[2][0] = Z[0];
1143     matrix[2][1] = Z[1];
1144     matrix[2][2] = Z[2];
1145     matrix[2][3] = 0.0f;
1146 
1147     matrix[3][0] = spine[i][0];
1148     matrix[3][1] = spine[i][1];
1149     matrix[3][2] = spine[i][2];
1150     matrix[3][3] = 1.0f;
1151 
1152     if (numorient) {
1153       SbMatrix rmat;
1154       orient[SbMin(i, numorient-1)].getValue(rmat);
1155       matrix.multLeft(rmat);
1156     }
1157 
1158     if (numscale) {
1159       SbMatrix smat = SbMatrix::identity();
1160       SbVec2f s = scale[SbMin(i, numscale-1)];
1161       smat[0][0] = s[0];
1162       smat[2][2] = s[1];
1163       matrix.multLeft(smat);
1164     }
1165 
1166     float currentcrosslen = 0.0f; // for texcoords
1167     for (j = 0; j < numcross; j++) {
1168       SbVec3f c;
1169       SbVec2f tc;
1170       c[0] = cross[j][0];
1171       c[1] = 0.0f;
1172       c[2] = cross[j][1];
1173 
1174       matrix.multVecMatrix(c, c);
1175       this->coord.append(c);
1176       tc[0] = currentcrosslen / crosslen;
1177       tc[1] = currentspinelen / spinelen;
1178       this->tcoord.append(tc);
1179 
1180       if (j < numcross-1) {
1181         currentcrosslen += (cross[j+1]-cross[j]).length();
1182       }
1183     }
1184     if (i < numspine-1) {
1185       currentspinelen += (spine[i+1]-spine[i]).length();
1186     }
1187   }
1188 
1189 #define ADD_POINT(i0, j0) \
1190   do { \
1191     this->idx.append((i0)*numcross+(j0)); \
1192   } while (0)
1193 
1194   // this macro makes the code below more readable
1195 #define ADD_TRIANGLE(i0, j0, i1, j1, i2, j2) \
1196   do { \
1197     this->idx.append((i0)*numcross+(j0)); \
1198     this->idx.append((i2)*numcross+(j2)); \
1199     this->idx.append((i1)*numcross+(j1)); \
1200     this->idx.append(-1); \
1201   } while (0)
1202 
1203 #define ADD_QUAD(i0, j0, i1, j1, i2, j2, i3, j3)   \
1204   do { \
1205     this->idx.append((i0)*numcross+(j0)); \
1206     this->idx.append((i3)*numcross+(j3)); \
1207     this->idx.append((i2)*numcross+(j2)); \
1208     this->idx.append((i1)*numcross+(j1)); \
1209     this->idx.append(-1); \
1210   } while (0)
1211 
1212   // create walls
1213   for (i = 0; i < numspine-1; i++) {
1214     for (j = 0; j < numcross-1; j++) {
1215       if (PUBLIC(this)->convex.getValue() && !ALWAYS_CREATE_TRIANGLES) {
1216         ADD_QUAD(i, j, i+1, j, i+1, j+1, i, j+1);
1217       }
1218       else {
1219         ADD_TRIANGLE(i, j, i+1, j, i+1, j+1);
1220         ADD_TRIANGLE(i, j, i+1, j+1, i, j+1);
1221       }
1222     }
1223   }
1224 
1225   SbVec2f crossboxsize = crossbox.getMax() - crossbox.getMin();
1226 
1227   // create beginCap polygon
1228   if (PUBLIC(this)->beginCap.getValue() && !closed) {
1229     // create texcoords
1230     for (i = 0; i < numcross; i++) {
1231       SbVec2f c = cross[i];
1232       c -= crossbox.getMin();
1233       c[0] /= crossboxsize[0];
1234       c[1] /= crossboxsize[1];
1235       this->tcoord.append(c);
1236     }
1237     // just duplicated begincap coords to simplify texture coordinate handling
1238     for (i = 0; i < numcross; i++) {
1239       this->coord.append(coord[i]);
1240     }
1241 
1242     if (PUBLIC(this)->convex.getValue()) {
1243       for (i = 1; i < (connected ? numcross-2 : numcross-1); i++) {
1244         ADD_TRIANGLE(numspine, 0, numspine, i, numspine, i+1);
1245       }
1246     }
1247     else {
1248       // let the tesselator create triangles
1249       this->tess.beginPolygon(FALSE);
1250       for (i = (connected ? numcross-2 : numcross-1); i >= 0; i--) {
1251         int theidx = numcross*numspine + i;
1252         SbVec3f tc;
1253         tc.setValue(cross[i][0],
1254                     cross[i][1],
1255                     0.0f);
1256         this->tess.addVertex(tc, (void*) ((uintptr_t) theidx));
1257       }
1258       this->tess.endPolygon();
1259     }
1260   }
1261 
1262   // create endCap polygon
1263   if (PUBLIC(this)->endCap.getValue() && !closed) {
1264     // just duplicate endcap coords to simplify texture coordinate handling
1265     for (i = 0; i < numcross; i++) {
1266       this->coord.append(coord[(numspine-1)*numcross+i]);
1267     }
1268     // create texcoords
1269     for (i = 0; i < numcross; i++) {
1270       SbVec2f c = cross[i];
1271       c -= crossbox.getMin();
1272       c[0] /= crossboxsize[0];
1273       c[1] /= crossboxsize[1];
1274       // the endCap texcoords should be flipped in the T dimension
1275       c[1] = 1.0f - c[1];
1276       this->tcoord.append(c);
1277     }
1278 
1279 	int offset = PUBLIC(this)->beginCap.getValue() ? 1 : 0;
1280 
1281 	if (PUBLIC(this)->convex.getValue()) {
1282       for (i = 1; i < (connected ? numcross-2 : numcross-1); i++) {
1283         ADD_TRIANGLE(numspine+offset, numcross-1,
1284                      numspine+offset, numcross-1-i,
1285                      numspine+offset, numcross-2-i);
1286       }
1287     }
1288     else {
1289       // let the tesselator create triangles
1290       this->tess.beginPolygon(FALSE);
1291       for (i = (connected ? numcross-2 : numcross-1); i >= 0; i--) {
1292         int theidx = (numspine+offset)*numcross + numcross - 1 - i;
1293         SbVec3f tc;
1294         tc.setValue(cross[(numcross-1)-i][0],
1295                     cross[(numcross-1)-i][1],
1296                     0.0f);
1297         this->tess.addVertex(tc, (void*) ((uintptr_t) theidx));
1298       }
1299       this->tess.endPolygon();
1300     }
1301   }
1302 #undef ADD_TRIANGLE
1303 #undef ADD_QUAD
1304 #undef ADD_POINT
1305 }
1306 
1307 //
1308 // generates per-verex normals for the extrusion.
1309 //
1310 void
generateNormals(void)1311 SoVRMLExtrusionP::generateNormals(void)
1312 {
1313   this->gen.reset(PUBLIC(this)->ccw.getValue());
1314   const SbVec3f * c = this->coord.getArrayPtr();
1315   const int32_t * iptr = this->idx.getArrayPtr();
1316   const int32_t * endptr = iptr + this->idx.getLength();
1317 
1318   while (iptr < endptr) {
1319     this->gen.beginPolygon();
1320     int32_t theidx = *iptr++;
1321     while (theidx >= 0) {
1322       this->gen.polygonVertex(c[theidx]);
1323       theidx = *iptr++;
1324     }
1325     this->gen.endPolygon();
1326   }
1327   this->gen.generate(PUBLIC(this)->creaseAngle.getValue());
1328 }
1329 
1330 //
1331 // callback from the polygon tessellator
1332 //
1333 void
tess_callback(void * v0,void * v1,void * v2,void * data)1334 SoVRMLExtrusionP::tess_callback(void * v0, void * v1, void * v2, void * data)
1335 {
1336   SoVRMLExtrusionP * thisp = (SoVRMLExtrusionP*) data;
1337   thisp->idx.append((int32_t)((uintptr_t)v0));
1338   thisp->idx.append((int32_t)((uintptr_t)v1));
1339   thisp->idx.append((int32_t)((uintptr_t)v2));
1340   thisp->idx.append(-1);
1341 }
1342 
1343 #undef PUBLIC
1344 #undef PRIVATE
1345 
1346 #endif // HAVE_VRML97
1347