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