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 /*!
34 \class SoShape SoShape.h Inventor/nodes/SoShape.h
35 \brief The SoShape class is the superclass for geometry shapes.
36
37 \ingroup nodes
38
39 The node types which have actual geometry to render inherits this
40 class. For convenience, the SoShape class contains various common
41 code used by the subclasses.
42 */
43
44 // *************************************************************************
45
46 #include <Inventor/nodes/SoShape.h>
47
48 #include <cstring>
49 #include <cstdlib>
50
51 #ifdef HAVE_CONFIG_H
52 #include <config.h>
53 #endif // HAVE_CONFIG_H
54
55 #include <Inventor/C/glue/gl.h>
56 #include <Inventor/C/tidbits.h>
57 #include <Inventor/SbBox2f.h>
58 #include <Inventor/SbClip.h>
59 #include <Inventor/SbPlane.h>
60 #include <Inventor/SbTime.h>
61 #include <Inventor/SoPickedPoint.h>
62 #include <Inventor/SoPrimitiveVertex.h>
63 #include <Inventor/actions/SoCallbackAction.h>
64 #include <Inventor/actions/SoGLRenderAction.h>
65 #include <Inventor/actions/SoGetBoundingBoxAction.h>
66 #include <Inventor/actions/SoGetPrimitiveCountAction.h>
67 #include <Inventor/actions/SoRayPickAction.h>
68 #include <Inventor/annex/FXViz/elements/SoShadowStyleElement.h>
69 #include <Inventor/bundles/SoMaterialBundle.h>
70 #include <Inventor/caches/SoBoundingBoxCache.h>
71 #include <Inventor/caches/SoPrimitiveVertexCache.h>
72 #include <Inventor/details/SoFaceDetail.h>
73 #include <Inventor/details/SoLineDetail.h>
74 #include <Inventor/elements/SoBumpMapElement.h>
75 #include <Inventor/elements/SoCacheElement.h>
76 #include <Inventor/elements/SoComplexityElement.h>
77 #include <Inventor/elements/SoCoordinateElement.h>
78 #include <Inventor/elements/SoCullElement.h>
79 #include <Inventor/elements/SoGLCacheContextElement.h>
80 #include <Inventor/elements/SoGLLazyElement.h>
81 #include <Inventor/elements/SoGLMultiTextureEnabledElement.h>
82 #include <Inventor/elements/SoGLMultiTextureImageElement.h>
83 #include <Inventor/elements/SoGLShapeHintsElement.h>
84 #include <Inventor/elements/SoGLVBOElement.h>
85 #include <Inventor/elements/SoGLVertexAttributeElement.h>
86 #include <Inventor/elements/SoLightElement.h>
87 #include <Inventor/elements/SoLightModelElement.h>
88 #include <Inventor/elements/SoMaterialBindingElement.h>
89 #include <Inventor/elements/SoModelMatrixElement.h>
90 #include <Inventor/elements/SoMultiTextureCoordinateElement.h>
91 #include <Inventor/elements/SoMultiTextureEnabledElement.h>
92 #include <Inventor/elements/SoNormalElement.h>
93 #include <Inventor/elements/SoProjectionMatrixElement.h>
94 #include <Inventor/elements/SoShapeHintsElement.h>
95 #include <Inventor/elements/SoShapeStyleElement.h>
96 #include <Inventor/elements/SoTextureQualityElement.h>
97 #include <Inventor/elements/SoViewVolumeElement.h>
98 #include <Inventor/elements/SoViewingMatrixElement.h>
99 #include <Inventor/elements/SoViewportRegionElement.h>
100 #include <Inventor/errors/SoDebugError.h>
101 #include <Inventor/misc/SoGLBigImage.h>
102 #include <Inventor/misc/SoGLDriverDatabase.h>
103 #include <Inventor/misc/SoState.h>
104 #include <Inventor/nodes/SoLight.h>
105 #include <Inventor/nodes/SoVertexProperty.h>
106 #include <Inventor/nodes/SoVertexShape.h>
107 #include <Inventor/system/gl.h>
108 #include <Inventor/threads/SbMutex.h>
109 #include <Inventor/threads/SbStorage.h>
110
111 #ifdef HAVE_VRML97
112 #include <Inventor/VRMLnodes/SoVRMLIndexedFaceSet.h>
113 #include <Inventor/VRMLnodes/SoVRMLExtrusion.h>
114 #include <Inventor/VRMLnodes/SoVRMLElevationGrid.h>
115 #endif // HAVE_VRML97
116
117 #include "nodes/SoSubNodeP.h"
118 #include "rendering/SoGL.h"
119 #include "glue/glp.h"
120 #include "threads/threadsutilp.h"
121 #include "tidbitsp.h"
122 #include "rendering/SoVBO.h"
123 #include "coindefs.h" // COIN_OBSOLETED()
124
125 // SoShape.cpp grew too big, so I had to move some code into new
126 // files. pederb, 2001-07-18
127 #include "soshape_primdata.h"
128 #include "soshape_trianglesort.h"
129 #include "soshape_bigtexture.h"
130 #include "soshape_bumprender.h"
131
132 // *************************************************************************
133
134 /*!
135 \enum SoShape::TriangleShape
136 \COININTERNAL
137 */
138
139 /*!
140 \fn void SoShape::computeBBox(SoAction * action, SbBox3f & box, SbVec3f & center)
141
142 Implemented by SoShape subclasses to let the SoShape superclass know
143 the exact size and weighted center point of the shape's bounding box.
144
145 The bounding box and center point should be calculated and returned
146 in the local coordinate system.
147
148 The method implements action behavior for shape nodes for
149 SoGetBoundingBoxAction. It is invoked from
150 SoShape::getBoundingBox(). (Subclasses should \e not override
151 SoNode::getBoundingBox().)
152
153 The \a box parameter sent in is guaranteed to be an empty box, while
154 \a center is undefined upon function entry.
155 */
156
157 /*!
158 \fn void SoShape::generatePrimitives(SoAction * action)
159
160 The method implements action behavior for shape nodes for
161 SoCallbackAction. It is invoked from
162 SoShape::callback(). (Subclasses should \e not override
163 SoNode::callback().)
164
165 The subclass implementations uses the convenience methods
166 SoShape::beginShape(), SoShape::shapeVertex(), and
167 SoShape::endShape(), with SoDetail instances, to pass the primitives
168 making up the shape back to the caller.
169 */
170
171 // *************************************************************************
172
173 class SoShapeP {
174 public:
SoShapeP()175 SoShapeP() {
176 this->bboxcache = NULL;
177 this->pvcache = NULL;
178 this->bumprender = NULL;
179 this->rendercnt = 0;
180 this->flags = 0;
181 }
~SoShapeP()182 ~SoShapeP() {
183 if (this->bboxcache) { this->bboxcache->unref(); }
184 if (this->pvcache) { this->pvcache->unref(); }
185 delete this->bumprender;
186 }
187 enum {
188 RENDERCNT_BITS = 4, // bits needed to store rendercnt
189 FLAG_BITS = 4 // bits needed to store flags
190 };
191 enum Flags {
192 SHOULD_BBOX_CACHE = 0x1,
193 NEED_SETUP_SHAPE_HINTS = 0x2,
194 DISABLE_VERTEX_ARRAY_CACHE = 0x4,
195 };
196
197 static void calibrateBBoxCache(void);
198 static double bboxcachetimelimit;
199 SoBoundingBoxCache * bboxcache;
200 SoPrimitiveVertexCache * pvcache;
201 soshape_bumprender * bumprender;
202 uint32_t flags : FLAG_BITS;
203 // stores the number of frames rendered with no node changes
204 uint32_t rendercnt : RENDERCNT_BITS;
205
206 // needed since some VRML97 nodes change the GL state inside the node
testSetupShapeHints(SoShape * shape)207 void testSetupShapeHints(SoShape * shape) {
208 #ifdef HAVE_VRML97
209 if ((this->flags & SoShapeP::NEED_SETUP_SHAPE_HINTS) == 0) {
210 if (shape->isOfType(SoVRMLIndexedFaceSet::getClassTypeId()) ||
211 shape->isOfType(SoVRMLExtrusion::getClassTypeId()) ||
212 shape->isOfType(SoVRMLElevationGrid::getClassTypeId())) {
213 this->flags |= SoShapeP::NEED_SETUP_SHAPE_HINTS;
214 }
215 }
216 #endif // HAVE_VRML97
217 }
setupShapeHints(SoShape * shape,SoState * state)218 void setupShapeHints(SoShape * shape, SoState * state) {
219 #ifdef HAVE_VRML97
220 if (this->flags & SoShapeP::NEED_SETUP_SHAPE_HINTS) {
221 SbBool ccw = ((SoSFBool*)(shape->getField("ccw")))->getValue();
222 SbBool solid = ((SoSFBool*)(shape->getField("solid")))->getValue();
223 SoGLShapeHintsElement::forceSend(state, ccw, solid, !solid);
224 }
225 #endif // HAVE_VRML97
226 }
227
228 // we can use a per-instance mutex here instead of this class-wide
229 // one, but we go for the class-wide one since at least MSWindows
230 // might have a rather strict limit on the total amount of mutex
231 // resources a process / user can hold at any one time.
232 //
233 // i haven't looked too hard at the locked code regions, however --
234 // it might be that a class-wide lock can cause significantly less
235 // efficient execution in a multi-threaded environment. if so, we
236 // will have to come up with something smarter than this (a mutex
237 // pool or something, i suppose).
238 //
239 // -mortene.
240 static SbMutex * mutex;
241
242 #ifdef COIN_THREADSAFE
lock(void)243 void lock(void) { SoShapeP::mutex->lock(); }
unlock(void)244 void unlock(void) { SoShapeP::mutex->unlock(); }
245 #else // ! COIN_THREADSAFE
lock(void)246 void lock(void) { }
unlock(void)247 void unlock(void) { }
248 #endif // ! COIN_THREADSAFE
249
250 static void cleanup(void);
251 };
252
253 double SoShapeP::bboxcachetimelimit;
254
255 SbMutex * SoShapeP::mutex = NULL;
256
257 #undef PRIVATE
258 #define PRIVATE(p) ((p)->pimpl)
259
260 // *************************************************************************
261 // code/structures to handle static and/or thread safe data
262
263 enum SoShapeRenderMode {
264 NORMAL,
265 BIGTEXTURE,
266 SORTED_TRIANGLES,
267 PVCACHE
268 };
269
270 typedef struct {
271 soshape_primdata * primdata;
272 SbList <soshape_bigtexture*> * bigtexturelist;
273 SbList <uint32_t> * bigtexturecontext;
274 soshape_trianglesort * trianglesort;
275
276 soshape_bigtexture * currentbigtexture;
277 // used in generatePrimitives() callbacks to set correct material
278 SoMaterialBundle * currentbundle;
279
280 int rendermode;
281 } soshape_staticdata;
282
283 static soshape_bigtexture *
soshape_get_bigtexture(soshape_staticdata * data,uint32_t context)284 soshape_get_bigtexture(soshape_staticdata * data, uint32_t context)
285 {
286 for (int i = 0; i < data->bigtexturecontext->getLength(); i++) {
287 if ((*(data->bigtexturecontext))[i] == context) {
288 return (*(data->bigtexturelist))[i];
289 }
290 }
291 soshape_bigtexture * newtex = new soshape_bigtexture;
292 data->bigtexturelist->append(newtex);
293 data->bigtexturecontext->append(context);
294 return newtex;
295 }
296
297 static void
soshape_construct_staticdata(void * closure)298 soshape_construct_staticdata(void * closure)
299 {
300 soshape_staticdata * data = (soshape_staticdata*) closure;
301
302 data->bigtexturelist = new SbList <soshape_bigtexture*>;
303 data->bigtexturecontext = new SbList <uint32_t>;
304 data->primdata = new soshape_primdata();
305 data->trianglesort = new soshape_trianglesort();
306 data->rendermode = NORMAL;
307 }
308
309 static void
soshape_destruct_staticdata(void * closure)310 soshape_destruct_staticdata(void * closure)
311 {
312 soshape_staticdata * data = (soshape_staticdata*) closure;
313 for (int i = 0; i < data->bigtexturelist->getLength(); i++) {
314 delete (*(data->bigtexturelist))[i];
315 }
316 delete data->bigtexturelist;
317 delete data->bigtexturecontext;
318 delete data->primdata;
319 delete data->trianglesort;
320 }
321
322 static SbStorage * soshape_staticstorage;
323
324 static soshape_staticdata *
soshape_get_staticdata(void)325 soshape_get_staticdata(void)
326 {
327 return (soshape_staticdata*) soshape_staticstorage->get();
328 }
329
330 // called by atexit
331 void
cleanup(void)332 SoShapeP::cleanup(void)
333 {
334 delete soshape_staticstorage;
335 soshape_staticstorage = NULL;
336
337 delete SoShapeP::mutex;
338 SoShapeP::mutex = NULL;
339 }
340
341 // *************************************************************************
342
343 SO_NODE_ABSTRACT_SOURCE(SoShape);
344
345 // *************************************************************************
346
347 /*!
348 Constructor.
349 */
SoShape(void)350 SoShape::SoShape(void)
351 {
352 SO_NODE_INTERNAL_CONSTRUCTOR(SoShape);
353 PRIVATE(this) = new SoShapeP;
354 }
355
356 /*!
357 Destructor.
358 */
~SoShape()359 SoShape::~SoShape()
360 {
361 delete PRIVATE(this);
362 }
363
364 // Doc in parent.
365 void
initClass(void)366 SoShape::initClass(void)
367 {
368 SO_NODE_INTERNAL_INIT_ABSTRACT_CLASS(SoShape, SO_FROM_INVENTOR_1);
369
370 #ifdef COIN_THREADSAFE
371 SoShapeP::mutex = new SbMutex;
372 #endif // COIN_THREADSAFE
373
374 soshape_staticstorage =
375 new SbStorage(sizeof(soshape_staticdata),
376 soshape_construct_staticdata,
377 soshape_destruct_staticdata);
378 SoShapeP::calibrateBBoxCache();
379
380 coin_atexit((coin_atexit_f *)SoShapeP::cleanup, CC_ATEXIT_NORMAL);
381 }
382
383 // Doc in parent.
384 void
getBoundingBox(SoGetBoundingBoxAction * action)385 SoShape::getBoundingBox(SoGetBoundingBoxAction * action)
386 {
387 SbBox3f box;
388 SbVec3f center;
389 this->getBBox(action, box, center);
390 if (!box.isEmpty()) {
391 action->extendBy(box);
392 action->setCenter(center, TRUE);
393 }
394 }
395
396 // Doc in parent.
397 void
GLRender(SoGLRenderAction * action)398 SoShape::GLRender(SoGLRenderAction * action)
399 {
400 // if we get here, the shape do not have a render method and
401 // generatePrimitives should therefore be used to render the
402 // shape. This is probably painfully slow, so if you want speed,
403 // implement the GLRender() method. pederb, 20000612
404
405 if (!this->shouldGLRender(action)) return;
406
407 // test for SoVertexShape node and push data onto the state before
408 // calling generatePrimitives(). This is needed for SoMaterialBundle
409 // to work correctly.
410 SoVertexProperty * vp = NULL;
411 if (this->isOfType(SoVertexShape::getClassTypeId())) {
412 vp = (SoVertexProperty*) ((SoVertexShape*)this)->vertexProperty.getValue();
413 }
414
415 if (vp) {
416 action->getState()->push();
417 vp->doAction(action);
418 }
419 SoMaterialBundle mb(action);
420 mb.sendFirst();
421 soshape_get_staticdata()->currentbundle = &mb; // needed in the primitive callbacks
422 this->generatePrimitives(action);
423
424 if (vp) action->getState()->pop();
425 }
426
427 // Doc in parent.
428 void
callback(SoCallbackAction * action)429 SoShape::callback(SoCallbackAction * action)
430 {
431 if (action->shouldGeneratePrimitives(this)) {
432 soshape_staticdata * shapedata = soshape_get_staticdata();
433 shapedata->primdata->faceCounter = 0;
434 this->generatePrimitives(action);
435 }
436 }
437
438 // test bbox intersection
439 static SbBool
soshape_ray_intersect(SoRayPickAction * action,const SbBox3f & box)440 soshape_ray_intersect(SoRayPickAction * action, const SbBox3f & box)
441 {
442 if (box.isEmpty()) return FALSE;
443 return action->intersect(box, TRUE);
444 }
445
446
447 /*!
448 Calculates picked point based on primitives generated by subclasses.
449 */
450 void
rayPick(SoRayPickAction * action)451 SoShape::rayPick(SoRayPickAction * action)
452 {
453 if (this->shouldRayPick(action)) {
454 this->computeObjectSpaceRay(action);
455
456 if (!PRIVATE(this)->bboxcache ||
457 !PRIVATE(this)->bboxcache->isValid(action->getState()) ||
458 soshape_ray_intersect(action, PRIVATE(this)->bboxcache->getProjectedBox())) {
459 this->generatePrimitives(action);
460 }
461 }
462 }
463
464 /*!
465 A convenience function that returns the size of a \a boundingbox
466 projected onto the screen. Useful for \c SCREEN_SPACE complexity
467 geometry.
468 */
469 void
getScreenSize(SoState * const state,const SbBox3f & boundingbox,SbVec2s & rectsize)470 SoShape::getScreenSize(SoState * const state, const SbBox3f & boundingbox,
471 SbVec2s & rectsize)
472 {
473 SbMatrix projmatrix;
474 projmatrix = (SoModelMatrixElement::get(state) *
475 SoViewingMatrixElement::get(state) *
476 SoProjectionMatrixElement::get(state));
477
478 SbVec2s vpsize = SoViewportRegionElement::get(state).getViewportSizePixels();
479 SbVec3f bmin, bmax;
480 boundingbox.getBounds(bmin, bmax);
481
482 SbVec3f v;
483 SbBox2f normbox;
484 normbox.makeEmpty();
485 for (int i = 0; i < 8; i++) {
486 v.setValue(i&1 ? bmin[0] : bmax[0],
487 i&2 ? bmin[1] : bmax[1],
488 i&4 ? bmin[2] : bmax[2]);
489 projmatrix.multVecMatrix(v, v);
490 normbox.extendBy(SbVec2f(v[0], v[1]));
491 }
492 float nx, ny;
493 normbox.getSize(nx, ny);
494
495 // restrict size of projection. It is often way off when object
496 // intersects the near plane. We should probably do clipping against
497 // the view volume do be 100% correct, but that would be too slow.
498 // pederb, 2001-05-20
499 if (nx > 10.0f) nx = 10.0f;
500 if (ny > 10.0f) ny = 10.0f;
501
502 rectsize[0] = (short) SbMin(32767.0f, float(vpsize[0])*0.5f*nx);
503 rectsize[1] = (short) SbMin(32767.0f, float(vpsize[1])*0.5f*ny);
504 }
505
506 /*!
507 Returns the complexity value to be used by subclasses. Considers
508 complexity type. For \c OBJECT_SPACE complexity this will be a
509 number between 0 and 1. For \c SCREEN_SPACE complexity it is a
510 number from 0 and up.
511 */
512 float
getComplexityValue(SoAction * action)513 SoShape::getComplexityValue(SoAction * action)
514 {
515 SoState * state = action->getState();
516 switch (SoComplexityTypeElement::get(state)) {
517 case SoComplexityTypeElement::SCREEN_SPACE:
518 {
519 SbBox3f box;
520 SbVec3f center;
521 this->getBBox(action, box, center);
522 SbVec2s size;
523 SoShape::getScreenSize(state, box, size);
524 // FIXME: probably needs calibration.
525
526 #if 1 // testing new complexity code
527 // The cast within the sqrt() is done to avoid ambigouity error
528 // from HPUX aCC, as sqrt() can be either "long double sqrt(long
529 // double)" or "float sqrt(float)". mortene.
530 return float(sqrt((float)SbMax(size[0], size[1]))) * 0.4f *
531 SoComplexityElement::get(state);
532 #else // first version
533 float numPixels = float(size[0])* float(size[1]);
534 return numPixels * 0.0001f * SoComplexityElement::get(state);
535 #endif
536 }
537 case SoComplexityTypeElement::OBJECT_SPACE:
538 return SoComplexityElement::get(state);
539 case SoComplexityTypeElement::BOUNDING_BOX:
540 // return default value. We might get here when generating
541 // primitives, not when rendering.
542 return 0.5f;
543 default:
544 assert(0 && "unknown complexity type");
545 return 0.5f;
546 }
547 }
548
549 /*!
550 \COININTERNAL
551 */
552 SbBool
shouldGLRender(SoGLRenderAction * action)553 SoShape::shouldGLRender(SoGLRenderAction * action)
554 {
555 SoState * state = action->getState();
556
557 const SoShapeStyleElement * shapestyle = SoShapeStyleElement::get(state);
558 unsigned int shapestyleflags = shapestyle->getFlags();
559
560 if (shapestyleflags & SoShapeStyleElement::INVISIBLE)
561 return FALSE;
562
563 if (PRIVATE(this)->bboxcache && !state->isCacheOpen() && !SoCullElement::completelyInside(state)) {
564 if (PRIVATE(this)->bboxcache->isValid(state)) {
565 if (SoCullElement::cullTest(state, PRIVATE(this)->bboxcache->getProjectedBox())) {
566 return FALSE;
567 }
568 }
569 }
570
571 SbBool transparent = (shapestyleflags & (SoShapeStyleElement::TRANSP_TEXTURE|
572 SoShapeStyleElement::TRANSP_MATERIAL)) != 0;
573
574 if (shapestyleflags & SoShapeStyleElement::SHADOWMAP) {
575 if (transparent) return FALSE;
576 int style = SoShadowStyleElement::get(state);
577 if (style & SoShadowStyleElement::CASTS_SHADOW) return TRUE;
578 return FALSE;
579 }
580
581 if (action->handleTransparency(transparent))
582 return FALSE;
583
584 if (shapestyleflags & SoShapeStyleElement::BBOXCMPLX) {
585 this->GLRenderBoundingBox(action);
586 return FALSE;
587 }
588
589 // test if we should sort triangles before rendering
590 if (transparent && (shapestyleflags & SoShapeStyleElement::TRANSP_SORTED_TRIANGLES)) {
591 // lock since pvcache is shared among all threads
592 PRIVATE(this)->lock();
593 this->validatePVCache(action);
594
595 int arrays = SoPrimitiveVertexCache::NORMAL|SoPrimitiveVertexCache::COLOR;
596 SoGLMultiTextureImageElement::Model model;
597 SbColor blendcolor;
598 SoGLImage * glimage = SoGLMultiTextureImageElement::get(state, 0, model, blendcolor);
599 if (glimage) arrays |= SoPrimitiveVertexCache::TEXCOORD;
600
601 SoMaterialBundle mb(action);
602 mb.sendFirst();
603 PRIVATE(this)->setupShapeHints(this, state);
604 PRIVATE(this)->pvcache->depthSortTriangles(state);
605 PRIVATE(this)->pvcache->renderTriangles(state, arrays);
606 if (PRIVATE(this)->pvcache->getNumLineIndices() ||
607 PRIVATE(this)->pvcache->getNumPointIndices()) {
608 const SoNormalElement * nelem = SoNormalElement::getInstance(state);
609 if (nelem->getNum() == 0) {
610 glPushAttrib(GL_LIGHTING_BIT);
611 glDisable(GL_LIGHTING);
612 arrays &= SoPrimitiveVertexCache::NORMAL;
613 }
614 PRIVATE(this)->pvcache->renderLines(state, arrays);
615 PRIVATE(this)->pvcache->renderPoints(state, arrays);
616
617 if (nelem->getNum() == 0) {
618 glPopAttrib();
619 }
620 }
621 PRIVATE(this)->unlock();
622 return FALSE; // tell shape _not_ to render
623 }
624
625 if (shapestyleflags & SoShapeStyleElement::BIGIMAGE) {
626 SoGLMultiTextureImageElement::Model model;
627 SbColor blendcolor;
628 SoGLImage * glimage = SoGLMultiTextureImageElement::get(state, 0, model, blendcolor);
629 if (glimage &&
630 glimage->isOfType(SoGLBigImage::getClassTypeId()) &&
631 SoGLMultiTextureEnabledElement::get(state, 0)) {
632
633 // don't attempt to cache bigimage shapes
634 if (state->isCacheOpen()) {
635 SoCacheElement::invalidate(state);
636 }
637 SoGLCacheContextElement::shouldAutoCache(state,
638 SoGLCacheContextElement::DONT_AUTO_CACHE);
639
640 soshape_staticdata * shapedata = soshape_get_staticdata();
641
642 // do this before generating triangles to get correct
643 // material for lines and point (only triangles are handled for now).
644 SoMaterialBundle mb(action);
645 mb.sendFirst();
646 shapedata->currentbundle = &mb;
647
648 SoGLBigImage * big = (SoGLBigImage*) glimage;
649
650 shapedata->rendermode = BIGTEXTURE;
651
652 soshape_bigtexture * bigtex = soshape_get_bigtexture(shapedata, action->getCacheContext());
653 shapedata->currentbigtexture = bigtex;
654 bigtex->beginShape(big, SoTextureQualityElement::get(state));
655 this->generatePrimitives(action);
656 // endShape() returns whether more/less detailed textures need to be
657 // fetched. We force a redraw if this is needed.
658 if (bigtex->endShape(state, this, mb) == FALSE) {
659 action->getCurPath()->getHead()->touch();
660 }
661 shapedata->rendermode = NORMAL;
662
663 return FALSE;
664 }
665 }
666
667 const cc_glglue * glue = sogl_glue_instance(state);
668
669 if (shapestyleflags & SoShapeStyleElement::BUMPMAP) {
670 const SoNodeList & lights = SoLightElement::getLights(state);
671 if (lights.getLength()) {
672 // lock since bumprender and pvcache is shared among all threads
673 PRIVATE(this)->lock();
674 if (PRIVATE(this)->bumprender == NULL) {
675 PRIVATE(this)->bumprender = new soshape_bumprender;
676 }
677 this->validatePVCache(action);
678 if (PRIVATE(this)->pvcache->getNumTriangleIndices() == 0) {
679 PRIVATE(this)->unlock();
680 return TRUE;
681 }
682 SoGLLazyElement::getInstance(state)->send(state, SoLazyElement::ALL_MASK);
683
684 glPushAttrib(GL_DEPTH_BUFFER_BIT);
685 glDepthFunc(GL_LEQUAL);
686 glDisable(GL_LIGHTING);
687
688 glColor3f(1.0f, 1.0f, 1.0f);
689 PRIVATE(this)->setupShapeHints(this, state);
690 const int numlights = lights.getLength();
691 for (int i = 0; i < numlights; i++) {
692 // fetch matrix that convert the light from its object space
693 // to the OpenGL world space
694 SbMatrix lm = SoLightElement::getMatrix(state, i);
695
696 // convert light back to this objects' object space
697 SbMatrix m = SoModelMatrixElement::get(state) *
698 SoViewingMatrixElement::get(state);
699 m = m.inverse();
700 m.multLeft(lm);
701
702
703 // bumprender is shared among all threads, so we need to lock
704 // when we get here since some internal arrays are used while
705 // rendering
706 //
707 // FIXME: about the above comment; i don't see any locking...?
708 // -mortene.
709 PRIVATE(this)->bumprender->renderBump(state, PRIVATE(this)->pvcache,
710 (SoLight*) lights[i], m);
711
712 if (i == 0) glEnable(GL_BLEND);
713 if (i == numlights-1) {
714 glBlendFunc(GL_DST_COLOR, GL_ZERO);
715 }
716 else if (i == 0) {
717 glBlendFunc(GL_ONE, GL_ONE);
718 }
719 }
720
721
722 SoGLLazyElement::getInstance(state)->reset(state,
723 SoLazyElement::DIFFUSE_MASK);
724 SoMaterialBundle mb(action);
725 mb.sendFirst();
726 PRIVATE(this)->setupShapeHints(this, state);
727 PRIVATE(this)->bumprender->renderNormal(state, PRIVATE(this)->pvcache);
728
729 const SbColor spec = SoLazyElement::getSpecular(state);
730 if (spec[0] != 0 || spec[1] != 0 || spec[2] != 0) { // Is the spec. color black?
731
732 // Can the hardware do specular bump maps?
733 if (glue->has_arb_fragment_program &&
734 glue->has_arb_vertex_program) {
735
736 SoGLLazyElement::getInstance(state)->reset(state,
737 SoLazyElement::DIFFUSE_MASK);
738 glEnable(GL_BLEND);
739 glBlendFunc(GL_ONE, GL_ONE);
740
741 for (int i = 0; i < numlights; i++) {
742 SbMatrix lm = SoLightElement::getMatrix(state, i);
743 SbMatrix m = SoModelMatrixElement::get(state) *
744 SoViewingMatrixElement::get(state);
745 m = m.inverse();
746 m.multLeft(lm);
747 PRIVATE(this)->bumprender->renderBumpSpecular(state, PRIVATE(this)->pvcache,
748 (SoLight*) lights[i], m);
749 }
750 }
751
752 }
753
754
755 PRIVATE(this)->unlock();
756
757 glPopAttrib();
758 // we used two units in the bumpmap code
759 SoGLMultiTextureImageElement::restore(state, 0);
760 SoGLMultiTextureImageElement::restore(state, 1);
761 SoGLLazyElement::getInstance(state)->reset(state,
762 SoLazyElement::LIGHT_MODEL_MASK|
763 SoLazyElement::BLENDING_MASK);
764
765 return FALSE;
766 }
767 }
768
769
770 if (shapestyleflags & SoShapeStyleElement::VERTEXARRAY) {
771 // lock since pvcache is shared among all threads
772 PRIVATE(this)->lock();
773 this->validatePVCache(action);
774 PRIVATE(this)->unlock();
775
776 SoGLCacheContextElement::shouldAutoCache(state,
777 SoGLCacheContextElement::DONT_AUTO_CACHE);
778 int arrays = SoPrimitiveVertexCache::NORMAL|SoPrimitiveVertexCache::COLOR;
779 SoGLMultiTextureImageElement::Model model;
780 SbColor blendcolor;
781 SoGLImage * glimage = SoGLMultiTextureImageElement::get(state, 0, model, blendcolor);
782 if (glimage) arrays |= SoPrimitiveVertexCache::TEXCOORD;
783 SoMaterialBundle mb(action);
784 mb.sendFirst();
785 PRIVATE(this)->setupShapeHints(this, state);
786 PRIVATE(this)->pvcache->renderTriangles(state, arrays);
787 if (PRIVATE(this)->pvcache->getNumLineIndices() ||
788 PRIVATE(this)->pvcache->getNumPointIndices()) {
789 const SoNormalElement * nelem = SoNormalElement::getInstance(state);
790 if (nelem->getNum() == 0) {
791 glPushAttrib(GL_LIGHTING_BIT);
792 glDisable(GL_LIGHTING);
793 arrays &= SoPrimitiveVertexCache::NORMAL;
794 }
795 PRIVATE(this)->pvcache->renderLines(state, arrays);
796 PRIVATE(this)->pvcache->renderPoints(state, arrays);
797
798 if (nelem->getNum() == 0) {
799 glPopAttrib();
800 }
801 }
802 // we have rendered, return FALSE
803 return FALSE;
804 }
805
806 #if COIN_DEBUG && 0 // enable this to test generatePrimitives() rendering
807 SoMaterialBundle mb(action);
808 mb.sendFirst();
809 soshape_get_staticdata()->currentbundle = &mb; // needed in the primitive callbacks
810 this->generatePrimitives(action);
811 return FALSE;
812 #else // generatePrimitives() rendering
813 if (PRIVATE(this)->rendercnt < ((1<<SoShapeP::RENDERCNT_BITS)-1)) {
814 PRIVATE(this)->rendercnt++;
815 }
816 return TRUE; // let the shape node render the geometry using OpenGL
817 #endif // ! generatePrimitives() rendering
818 }
819
820 /*!
821 \COININTERNAL
822 */
823 SbBool
shouldRayPick(SoRayPickAction * const action)824 SoShape::shouldRayPick(SoRayPickAction * const action)
825 {
826 switch (SoPickStyleElement::get(action->getState())) {
827 case SoPickStyleElement::SHAPE:
828 case SoPickStyleElement::SHAPE_ON_TOP:
829 case SoPickStyleElement::SHAPE_FRONTFACES:
830 return TRUE;
831 case SoPickStyleElement::BOUNDING_BOX:
832 case SoPickStyleElement::BOUNDING_BOX_ON_TOP:
833 this->rayPickBoundingBox(action);
834 return FALSE;
835 case SoPickStyleElement::UNPICKABLE:
836 return FALSE;
837 default:
838 assert(0 && "unknown pick style");
839 return TRUE;
840 }
841 }
842
843 /*!
844 \COININTERNAL
845 */
846 void
beginSolidShape(SoGLRenderAction * action)847 SoShape::beginSolidShape(SoGLRenderAction * action)
848 {
849 SoState * state = action->getState();
850 state->push();
851
852 SoShapeHintsElement::set(state,
853 SoShapeHintsElement::COUNTERCLOCKWISE,
854 SoShapeHintsElement::SOLID,
855 SoShapeHintsElement::FACE_TYPE_AS_IS);
856 }
857
858 /*!
859 \COININTERNAL
860 */
861 void
endSolidShape(SoGLRenderAction * action)862 SoShape::endSolidShape(SoGLRenderAction * action)
863 {
864 action->getState()->pop();
865 }
866
867 /*!
868 \COININTERNAL
869 */
870 void
computeObjectSpaceRay(SoRayPickAction * const action)871 SoShape::computeObjectSpaceRay(SoRayPickAction * const action)
872 {
873 action->setObjectSpace();
874 }
875
876 /*!
877 \COININTERNAL
878 */
879 void
computeObjectSpaceRay(SoRayPickAction * const action,const SbMatrix & matrix)880 SoShape::computeObjectSpaceRay(SoRayPickAction * const action,
881 const SbMatrix & matrix)
882 {
883 action->setObjectSpace(matrix);
884 }
885
886 /*!
887 Will create triangle detail for a SoPickedPoint. This method will
888 only be called internally, when generatePrimitives() is used for
889 picking (SoShape::rayPick() is not overridden).
890
891 This method returns \c NULL in Open Inventor, and subclasses will
892 need to override this method to create details for a SoPickedPoint.
893
894 This is not necessary with Coin. Of course, if you choose to
895 override it, it will work in the same way as Open Inventor.
896
897 For this to work, you must supply a face or line detail when
898 generating primitives. If you supply \c NULL for the detail argument in
899 SoShape::beginShape(), you'll have to override this method.
900 */
901 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))902 SoShape::createTriangleDetail(SoRayPickAction * COIN_UNUSED_ARG(action),
903 const SoPrimitiveVertex * COIN_UNUSED_ARG(v1),
904 const SoPrimitiveVertex * COIN_UNUSED_ARG(v2),
905 const SoPrimitiveVertex * COIN_UNUSED_ARG(v3),
906 SoPickedPoint * COIN_UNUSED_ARG(pp))
907 {
908 soshape_staticdata * shapedata = soshape_get_staticdata();
909
910 if (shapedata->primdata->faceDetail) {
911 return shapedata->primdata->createPickDetail();
912 }
913 // don't warn here. SoDetail instances are optional for extension nodes.
914 #if COIN_DEBUG && 0
915 SoDebugError::postInfo("SoShape::createTriangleDetail",
916 "Unable to create triangle detail.");
917 #endif // COIN_DEBUG
918 return NULL;
919 }
920
921 /*!
922 Will create line detail for a SoPickedPoint. This method will only
923 be called internally, when generatePrimitives() is used for picking
924 (SoShape::rayPick() is not overridden).
925
926 This method returns \c NULL in Open Inventor, and subclasses will
927 need to override this method to create details for a SoPickedPoint.
928
929 This is not necessary with Coin. Of course, if you choose to
930 override it, it will work in the same way as Open Inventor.
931
932 For this to work, you must supply a face or line detail when
933 generating primitives. If you supply \c NULL for the detail argument in
934 SoShape::beginShape(), you'll have to override this method.
935 */
936 SoDetail *
createLineSegmentDetail(SoRayPickAction * COIN_UNUSED_ARG (action),const SoPrimitiveVertex * COIN_UNUSED_ARG (v1),const SoPrimitiveVertex * COIN_UNUSED_ARG (v2),SoPickedPoint * COIN_UNUSED_ARG (pp))937 SoShape::createLineSegmentDetail(SoRayPickAction * COIN_UNUSED_ARG(action),
938 const SoPrimitiveVertex * COIN_UNUSED_ARG(v1),
939 const SoPrimitiveVertex * COIN_UNUSED_ARG(v2),
940 SoPickedPoint * COIN_UNUSED_ARG(pp))
941 {
942 soshape_staticdata * shapedata = soshape_get_staticdata();
943
944 if (shapedata->primdata->lineDetail) {
945 return shapedata->primdata->createPickDetail();
946 }
947 // don't warn here. SoDetail instances are optional for extension nodes.
948 #if COIN_DEBUG && 0
949 SoDebugError::postInfo("SoShape::createLineSegmentDetail",
950 "Unable to create line segment detail.");
951 #endif // COIN_DEBUG
952 return NULL;
953 }
954
955 /*!
956 Will create point detail for a SoPickedPoint. This method will only
957 be called internally, when generatePrimitives() is used for picking
958 (SoShape::rayPick() is not overridden).
959
960 This method returns \c NULL in Open Inventor, and subclasses will
961 need to override this method to create details for a SoPickedPoint.
962
963 This is not necessary with Coin. Of course, if you choose to
964 override it, it will work in the same way as Open Inventor.
965
966 For this to work, you must supply a point detail in the
967 SoPrimitiveVertex in generatePrimitives().
968 */
969 SoDetail *
createPointDetail(SoRayPickAction *,const SoPrimitiveVertex * v,SoPickedPoint *)970 SoShape::createPointDetail(SoRayPickAction * /* action */,
971 const SoPrimitiveVertex * v,
972 SoPickedPoint * /* pp */)
973 {
974 if (v->getDetail()) return v->getDetail()->copy();
975 return NULL;
976 }
977
978 /*!
979 \COININTERNAL
980 */
981 void
invokeTriangleCallbacks(SoAction * const action,const SoPrimitiveVertex * const v1,const SoPrimitiveVertex * const v2,const SoPrimitiveVertex * const v3)982 SoShape::invokeTriangleCallbacks(SoAction * const action,
983 const SoPrimitiveVertex * const v1,
984 const SoPrimitiveVertex * const v2,
985 const SoPrimitiveVertex * const v3)
986 {
987 if (action->getTypeId().isDerivedFrom(SoRayPickAction::getClassTypeId())) {
988 SoRayPickAction * ra = (SoRayPickAction *) action;
989
990 SbVec3f intersection;
991 SbVec3f barycentric;
992 SbBool front;
993
994 if (ra->intersect(v1->getPoint(), v2->getPoint(), v3->getPoint(),
995 intersection, barycentric, front)) {
996
997 if (ra->isBetweenPlanes(intersection)) {
998 if (SoShapeHintsElement::getVertexOrdering(ra->getState()) ==
999 SoShapeHintsElement::CLOCKWISE) {
1000 front = !front;
1001 }
1002 SoPickedPoint * pp = ra->addIntersection(intersection, front);
1003 if (pp) {
1004 pp->setDetail(this->createTriangleDetail(ra, v1, v2, v3, pp), this);
1005 // calculate normal at picked point
1006 SbVec3f n =
1007 v1->getNormal() * barycentric[0] +
1008 v2->getNormal() * barycentric[1] +
1009 v3->getNormal() * barycentric[2];
1010 n.normalize();
1011 pp->setObjectNormal(n);
1012
1013 // calculate texture coordinate at picked point
1014 SbVec4f tc =
1015 v1->getTextureCoords() * barycentric[0] +
1016 v2->getTextureCoords() * barycentric[1] +
1017 v3->getTextureCoords() * barycentric[2];
1018
1019 pp->setObjectTextureCoords(tc);
1020
1021 // material index need to be approximated, since there is no
1022 // way to average material indices :( This makes it
1023 // impossible to fully support color per vertex. An
1024 // extension to the OIV API would perhaps be a good idea
1025 // here? Maybe calculate the rgba value for diffuse and
1026 // transparency and set it in SoPickedPoint?
1027 float maxval = barycentric[0];
1028 const SoPrimitiveVertex * maxv = v1;
1029 if (barycentric[1] > maxval) {
1030 maxv = v2;
1031 maxval = barycentric[1];
1032 }
1033 if (barycentric[2] > maxval) {
1034 maxv = v3;
1035 }
1036 pp->setMaterialIndex(maxv->getMaterialIndex());
1037 }
1038 }
1039 }
1040 }
1041 else if (action->getTypeId().isDerivedFrom(SoCallbackAction::getClassTypeId())) {
1042 SoCallbackAction * ca = (SoCallbackAction *) action;
1043 ca->invokeTriangleCallbacks(this, v1, v2, v3);
1044 }
1045 else if (action->getTypeId().isDerivedFrom(SoGetPrimitiveCountAction::getClassTypeId())) {
1046 SoGetPrimitiveCountAction * ga = (SoGetPrimitiveCountAction *) action;
1047 ga->incNumTriangles();
1048 }
1049 else if (action->getTypeId().isDerivedFrom(SoGLRenderAction::getClassTypeId())) {
1050 soshape_staticdata * shapedata = soshape_get_staticdata();
1051
1052 switch (shapedata->rendermode) {
1053 case SORTED_TRIANGLES:
1054 shapedata->trianglesort->triangle(action->getState(), v1, v2, v3);
1055 break;
1056 case BIGTEXTURE:
1057 shapedata->currentbigtexture->triangle(action->getState(), v1, v2, v3);
1058 break;
1059 case PVCACHE:
1060 {
1061 int pdidx[3];
1062 pdidx[0] = shapedata->primdata->getPointDetailIndex(v1);
1063 pdidx[1] = shapedata->primdata->getPointDetailIndex(v2);
1064 pdidx[2] = shapedata->primdata->getPointDetailIndex(v3);
1065 PRIVATE(this)->pvcache->addTriangle(v1, v2, v3, pdidx);
1066 }
1067 break;
1068 default:
1069 glBegin(GL_TRIANGLES);
1070 glTexCoord4fv(v1->getTextureCoords().getValue());
1071 glNormal3fv(v1->getNormal().getValue());
1072 shapedata->currentbundle->send(v1->getMaterialIndex(), TRUE);
1073 glVertex3fv(v1->getPoint().getValue());
1074
1075 glTexCoord4fv(v2->getTextureCoords().getValue());
1076 glNormal3fv(v2->getNormal().getValue());
1077 shapedata->currentbundle->send(v2->getMaterialIndex(), TRUE);
1078 glVertex3fv(v2->getPoint().getValue());
1079
1080 glTexCoord4fv(v3->getTextureCoords().getValue());
1081 glNormal3fv(v3->getNormal().getValue());
1082 shapedata->currentbundle->send(v3->getMaterialIndex(), TRUE);
1083 glVertex3fv(v3->getPoint().getValue());
1084 glEnd();
1085 break;
1086 }
1087 }
1088 }
1089
1090 /*!
1091 \COININTERNAL
1092 */
1093 void
invokeLineSegmentCallbacks(SoAction * const action,const SoPrimitiveVertex * const v1,const SoPrimitiveVertex * const v2)1094 SoShape::invokeLineSegmentCallbacks(SoAction * const action,
1095 const SoPrimitiveVertex * const v1,
1096 const SoPrimitiveVertex * const v2)
1097 {
1098 if (action->getTypeId().isDerivedFrom(SoRayPickAction::getClassTypeId())) {
1099 SoRayPickAction * ra = (SoRayPickAction *) action;
1100
1101 SbVec3f intersection;
1102 if (ra->intersect(v1->getPoint(), v2->getPoint(), intersection)) {
1103 if (ra->isBetweenPlanes(intersection)) {
1104 SoPickedPoint * pp = ra->addIntersection(intersection);
1105 if (pp) {
1106 pp->setDetail(this->createLineSegmentDetail(ra, v1, v2, pp), this);
1107 float total = (v2->getPoint()-v1->getPoint()).length();
1108 float len1 = 1.0f;
1109 float len2 = 0.0f;
1110 if (total > 0.0f) {
1111 len1 = (intersection-v1->getPoint()).length();
1112 len2 = (intersection-v2->getPoint()).length();
1113 len1 /= total;
1114 len2 /= total;
1115 }
1116 SbVec3f n =
1117 v1->getNormal() * len1 +
1118 v2->getNormal() * len2;
1119 n.normalize();
1120 pp->setObjectNormal(n);
1121
1122 SbVec4f tc =
1123 v1->getTextureCoords() * len1 +
1124 v2->getTextureCoords() * len2;
1125 pp->setObjectTextureCoords(tc);
1126 pp->setMaterialIndex(len1 >= len2 ?
1127 v1->getMaterialIndex() :
1128 v2->getMaterialIndex());
1129
1130 }
1131 }
1132 }
1133 }
1134 else if (action->getTypeId().isDerivedFrom(SoCallbackAction::getClassTypeId())) {
1135 SoCallbackAction * ca = (SoCallbackAction *) action;
1136 ca->invokeLineSegmentCallbacks(this, v1, v2);
1137 }
1138 else if (action->getTypeId().isDerivedFrom(SoGetPrimitiveCountAction::getClassTypeId())) {
1139 SoGetPrimitiveCountAction * ga = (SoGetPrimitiveCountAction *) action;
1140 ga->incNumLines();
1141 }
1142 else if (action->getTypeId().isDerivedFrom(SoGLRenderAction::getClassTypeId())) {
1143 soshape_staticdata * shapedata = soshape_get_staticdata();
1144 switch (shapedata->rendermode) {
1145 case PVCACHE:
1146 PRIVATE(this)->pvcache->addLine(v1, v2);
1147 break;
1148 default:
1149 glBegin(GL_LINES);
1150 glTexCoord4fv(v1->getTextureCoords().getValue());
1151 glNormal3fv(v1->getNormal().getValue());
1152 shapedata->currentbundle->send(v1->getMaterialIndex(), TRUE);
1153 glVertex3fv(v1->getPoint().getValue());
1154
1155 glTexCoord4fv(v2->getTextureCoords().getValue());
1156 glNormal3fv(v2->getNormal().getValue());
1157 shapedata->currentbundle->send(v2->getMaterialIndex(), TRUE);
1158 glVertex3fv(v2->getPoint().getValue());
1159 glEnd();
1160 break;
1161 }
1162 }
1163 }
1164
1165 /*!
1166 \COININTERNAL
1167 */
1168 void
invokePointCallbacks(SoAction * const action,const SoPrimitiveVertex * const v)1169 SoShape::invokePointCallbacks(SoAction * const action,
1170 const SoPrimitiveVertex * const v)
1171 {
1172 if (action->getTypeId().isDerivedFrom(SoRayPickAction::getClassTypeId())) {
1173 SoRayPickAction * ra = (SoRayPickAction *) action;
1174
1175 SbVec3f intersection = v->getPoint();
1176 if (ra->intersect(intersection)) {
1177 if (ra->isBetweenPlanes(intersection)) {
1178 SoPickedPoint * pp = ra->addIntersection(intersection);
1179 if (pp) {
1180 pp->setDetail(this->createPointDetail(ra, v, pp), this);
1181 pp->setObjectNormal(v->getNormal());
1182 pp->setObjectTextureCoords(v->getTextureCoords());
1183 pp->setMaterialIndex(v->getMaterialIndex());
1184 }
1185 }
1186 }
1187 }
1188 else if (action->getTypeId().isDerivedFrom(SoCallbackAction::getClassTypeId())) {
1189 SoCallbackAction * ca = (SoCallbackAction *) action;
1190 ca->invokePointCallbacks(this, v);
1191 }
1192 else if (action->getTypeId().isDerivedFrom(SoGetPrimitiveCountAction::getClassTypeId())) {
1193 SoGetPrimitiveCountAction * ga = (SoGetPrimitiveCountAction *) action;
1194 ga->incNumPoints();
1195 }
1196 else if (action->getTypeId().isDerivedFrom(SoGLRenderAction::getClassTypeId())) {
1197 soshape_staticdata * shapedata = soshape_get_staticdata();
1198
1199 switch (shapedata->rendermode) {
1200 case PVCACHE:
1201 PRIVATE(this)->pvcache->addPoint(v);
1202 break;
1203 default:
1204 glBegin(GL_POINTS);
1205 glTexCoord4fv(v->getTextureCoords().getValue());
1206 glNormal3fv(v->getNormal().getValue());
1207 shapedata->currentbundle->send(v->getMaterialIndex(), TRUE);
1208 glVertex3fv(v->getPoint().getValue());
1209 glEnd();
1210 break;
1211 }
1212 }
1213 }
1214
1215 /*!
1216
1217 This method is used to generate primitives for a shape. It's
1218 typically called from a node's generatePrimitives() method. If you
1219 have your own shape and want to write a generatePrimitives() method
1220 for that shape, it's probably a good idea to take a peek in the
1221 generatePrimitives() method for a similar shape in Coin.
1222
1223 generatePrimitives() can contain several beginShape()/endShape()
1224 sequences. shapeVertex() is used for each vertex between
1225 beginShape() and endShape(). For instance, to generate primitives
1226 for a triangle you'd do something like this:
1227
1228 \verbatim
1229 SoPrimitiveVertex vertex;
1230
1231 this->beginShape(action, SoShape::POLYGON);
1232 vertex.setPoint(SbVec3f(0.0f, 0.0f, 0.0f));
1233 this->shapeVertex(&vertex);
1234 vertex.setPoint(SbVec3f(1.0f, 0.0f, 0.0f));
1235 this->shapeVertex(&vertex);
1236 vertex.setPoint(SbVec3f(1.0f, 1.0f, 0.0f));
1237 this->shapeVertex(&vertex);
1238 this->endShape();
1239 \endverbatim
1240
1241 Note that the SoPrimitiveVertex instance can simply be placed on the
1242 stack and not allocated. SoShape will copy the needed information
1243 when you call shapeVertex().
1244
1245 Before calling shapeVertex(), you can set extra information for the
1246 SoPrimitiveVertex, including normal, material index, and texture
1247 coordinates.
1248
1249 This method is slightly different from its counterpart from the
1250 original Open Inventor library, as this method has an SoDetail as
1251 the last argument, and not an SoFaceDetail. This is because we
1252 accept more TriangleShape types, and the detail might be a
1253 SoFaceDetail or a SoLineDetail. There is no use sending in a
1254 SoPointDetail, as nothing will be done with it.
1255 */
1256 void
beginShape(SoAction * const action,const TriangleShape shapetype,SoDetail * const detail)1257 SoShape::beginShape(SoAction * const action, const TriangleShape shapetype,
1258 SoDetail * const detail)
1259 {
1260 soshape_get_staticdata()->primdata->beginShape(this, action, shapetype, detail);
1261 }
1262
1263
1264 /*!
1265
1266 This method is used while generating primitives for a shape. See
1267 beginShape() for more details.
1268
1269 \sa beginShape(), endShape()
1270 */
1271 void
shapeVertex(const SoPrimitiveVertex * const v)1272 SoShape::shapeVertex(const SoPrimitiveVertex * const v)
1273 {
1274 soshape_get_staticdata()->primdata->shapeVertex(v);
1275 }
1276
1277 /*!
1278
1279 This method is used while generating primitives for a shape. See
1280 beginShape() for more details.
1281
1282 \sa beginShape(), shapeVertex()
1283 */
1284 void
endShape(void)1285 SoShape::endShape(void)
1286 {
1287 soshape_get_staticdata()->primdata->endShape();
1288 }
1289
1290 /*!
1291 Convenience function which sets up an SoPrimitiveVertex, and sends
1292 it using the SoShape::shapeVertex() function. 2D version
1293 */
1294 void
generateVertex(SoPrimitiveVertex * const pv,const SbVec3f & point,const SbBool usetexfunc,const SoMultiTextureCoordinateElement * const tce,const float s,const float t,const SbVec3f & normal)1295 SoShape::generateVertex(SoPrimitiveVertex * const pv,
1296 const SbVec3f & point,
1297 const SbBool usetexfunc,
1298 const SoMultiTextureCoordinateElement * const tce,
1299 const float s,
1300 const float t,
1301 const SbVec3f & normal)
1302 {
1303 this->generateVertex(pv, point, usetexfunc, tce, s, t, 0.0f, normal);
1304 }
1305
1306 /*!
1307 Convenience function which sets up an SoPrimitiveVertex, and sends
1308 it using the SoShape::shapeVertex() function. 3D version.
1309
1310 \COIN_FUNCTION_EXTENSION
1311
1312 \since Coin 2.0
1313 */
1314 void
generateVertex(SoPrimitiveVertex * const pv,const SbVec3f & point,const SbBool usetexfunc,const SoMultiTextureCoordinateElement * const tce,const float s,const float t,const float r,const SbVec3f & normal)1315 SoShape::generateVertex(SoPrimitiveVertex * const pv,
1316 const SbVec3f & point,
1317 const SbBool usetexfunc,
1318 const SoMultiTextureCoordinateElement * const tce,
1319 const float s,
1320 const float t,
1321 const float r,
1322 const SbVec3f & normal)
1323 {
1324 SbVec4f texCoord;
1325 if (usetexfunc)
1326 texCoord = tce->get(point, normal);
1327 else
1328 texCoord.setValue(s, t, r, 1.0f);
1329 pv->setPoint(point);
1330 pv->setNormal(normal);
1331 pv->setTextureCoords(texCoord);
1332 shapeVertex(pv);
1333 }
1334
1335 // Doc in superclass.
1336 SbBool
affectsState(void) const1337 SoShape::affectsState(void) const
1338 {
1339 // Overridden from default setting in SoNode to return FALSE instead
1340 // of TRUE, as we know for certain that no node classes derived from
1341 // SoShape will affect the rendering state.
1342 return FALSE;
1343 }
1344
1345 // Doc in superclass.
1346 void
getPrimitiveCount(SoGetPrimitiveCountAction * action)1347 SoShape::getPrimitiveCount(SoGetPrimitiveCountAction * action)
1348 {
1349 if (this->shouldPrimitiveCount(action)) this->generatePrimitives(action);
1350 }
1351
1352 /*!
1353 Not implemented in Coin. Should probably have been private in TGS
1354 Inventor API.
1355 */
1356 float
getDecimatedComplexity(SoState * COIN_UNUSED_ARG (state),float complexity)1357 SoShape::getDecimatedComplexity(SoState * COIN_UNUSED_ARG(state), float complexity)
1358 {
1359 COIN_OBSOLETED();
1360 return 1.0f * complexity;
1361 }
1362
1363 /*!
1364 Render a bounding box.
1365 */
1366 void
GLRenderBoundingBox(SoGLRenderAction * action)1367 SoShape::GLRenderBoundingBox(SoGLRenderAction * action)
1368 {
1369 SbBox3f box;
1370 SbVec3f center;
1371 this->getBBox(action, box, center);
1372 center = (box.getMin() + box.getMax()) * 0.5f;
1373 SbVec3f size = box.getMax() - box.getMin();
1374
1375 SoMaterialBundle mb(action);
1376 mb.sendFirst();
1377
1378 {
1379 SoGLShapeHintsElement::forceSend(action->getState(), TRUE, FALSE, FALSE);
1380 }
1381
1382 glPushMatrix();
1383 glTranslatef(center[0], center[1], center[2]);
1384 sogl_render_cube(size[0], size[1], size[2], &mb,
1385 SOGL_NEED_NORMALS | SOGL_NEED_TEXCOORDS, NULL);
1386 glPopMatrix();
1387 }
1388
1389 /*!
1390 \COININTERNAL
1391 */
1392 SbBool
shouldPrimitiveCount(SoGetPrimitiveCountAction * COIN_UNUSED_ARG (action))1393 SoShape::shouldPrimitiveCount(SoGetPrimitiveCountAction * COIN_UNUSED_ARG(action))
1394 {
1395 return TRUE; // FIXME: what to do here? pederb 1999-11-25
1396 }
1397
1398 //
1399 // used when pickStyle == BOUNDING_BOX
1400 //
1401 void
rayPickBoundingBox(SoRayPickAction * action)1402 SoShape::rayPickBoundingBox(SoRayPickAction * action)
1403 {
1404 SbBox3f box;
1405 SbVec3f center;
1406 this->getBBox(action, box, center);
1407 if (box.isEmpty()) return;
1408 this->computeObjectSpaceRay(action);
1409 SbVec3f isect;
1410 if (action->intersect(box, isect, FALSE)) {
1411 if (action->isBetweenPlanes(isect)) {
1412 action->addIntersection(isect);
1413 }
1414 }
1415 }
1416
1417 // Doc from superclass.
1418 void
notify(SoNotList * nl)1419 SoShape::notify(SoNotList * nl)
1420 {
1421 inherited::notify(nl);
1422 PRIVATE(this)->lock();
1423 if (PRIVATE(this)->bboxcache) {
1424 PRIVATE(this)->bboxcache->invalidate();
1425 }
1426 if (PRIVATE(this)->pvcache) {
1427 PRIVATE(this)->pvcache->invalidate();
1428 }
1429 PRIVATE(this)->flags &= ~SoShapeP::SHOULD_BBOX_CACHE;
1430 PRIVATE(this)->rendercnt = 0;
1431 PRIVATE(this)->unlock();
1432 }
1433
1434 /*!
1435 Return the bounding box cache for this shape. It might return
1436 NULL if no bounding box cache has been created. If not NULL, the
1437 caller must check if the cache is valid before using it. This
1438 can be done using SoCache::isValid().
1439
1440 \COIN_FUNCTION_EXTENSION
1441
1442 \since Coin 2.0
1443 */
1444 const SoBoundingBoxCache *
getBoundingBoxCache(void) const1445 SoShape::getBoundingBoxCache(void) const
1446 {
1447 return PRIVATE(this)->bboxcache;
1448 }
1449
1450 // return the bbox for this shape, using the cache if valid,
1451 // calculating it if not.
1452 void
getBBox(SoAction * action,SbBox3f & box,SbVec3f & center)1453 SoShape::getBBox(SoAction * action, SbBox3f & box, SbVec3f & center)
1454 {
1455 SoState * state = action->getState();
1456 SbBool isvalid = PRIVATE(this)->bboxcache && PRIVATE(this)->bboxcache->isValid(state);
1457 if (isvalid) {
1458 box = PRIVATE(this)->bboxcache->getProjectedBox();
1459 // we know center will be set, so just fetch it from the cache
1460 center = PRIVATE(this)->bboxcache->getCenter();
1461 }
1462 if (isvalid) {
1463 return;
1464 }
1465
1466 // destroy the old cache if we have one
1467 if (PRIVATE(this)->bboxcache) {
1468 PRIVATE(this)->lock();
1469 PRIVATE(this)->bboxcache->unref();
1470 PRIVATE(this)->bboxcache = NULL;
1471 PRIVATE(this)->unlock();
1472 // don't create bbox caches for shapes that change
1473 PRIVATE(this)->flags &= ~SoShapeP::SHOULD_BBOX_CACHE;
1474 }
1475
1476 SbBool shouldcache = (PRIVATE(this)->flags & SoShapeP::SHOULD_BBOX_CACHE) != 0;
1477 SbBool storedinvalid = FALSE;
1478 if (shouldcache) {
1479 // must push state to make cache dependencies work
1480 state->push();
1481 storedinvalid = SoCacheElement::setInvalid(FALSE);
1482 assert(PRIVATE(this)->bboxcache == NULL);
1483 PRIVATE(this)->lock();
1484 PRIVATE(this)->bboxcache = new SoBoundingBoxCache(state);
1485 PRIVATE(this)->bboxcache->ref();
1486 PRIVATE(this)->unlock();
1487 SoCacheElement::set(state, PRIVATE(this)->bboxcache);
1488 }
1489 SbTime begin = SbTime::getTimeOfDay();
1490 this->computeBBox(action, box, center);
1491 SbTime end = SbTime::getTimeOfDay();
1492 if (shouldcache) {
1493 PRIVATE(this)->bboxcache->set(box, TRUE, center);
1494 // pop state since we pushed it
1495 state->pop();
1496 SoCacheElement::setInvalid(storedinvalid);
1497 }
1498 // only create cache if calculating it took longer than the limit
1499 else if ((end.getValue() - begin.getValue()) >= SoShapeP::bboxcachetimelimit) {
1500 PRIVATE(this)->flags |= SoShapeP::SHOULD_BBOX_CACHE;
1501 if (action->isOfType(SoGetBoundingBoxAction::getClassTypeId())) {
1502 // just recalculate the bbox so that the cache is created at
1503 // once. SoGLRenderAction and SoRayPickAction might need it.
1504 state->push();
1505 storedinvalid = SoCacheElement::setInvalid(FALSE);
1506 assert(PRIVATE(this)->bboxcache == NULL);
1507 PRIVATE(this)->lock();
1508 PRIVATE(this)->bboxcache = new SoBoundingBoxCache(state);
1509 PRIVATE(this)->bboxcache->ref();
1510 PRIVATE(this)->unlock();
1511 SoCacheElement::set(state, PRIVATE(this)->bboxcache);
1512 box.makeEmpty();
1513 this->computeBBox(action, box, center);
1514 PRIVATE(this)->bboxcache->set(box, TRUE, center);
1515 // pop state since we pushed it
1516 state->pop();
1517 SoCacheElement::setInvalid(storedinvalid);
1518 }
1519 }
1520 }
1521
1522 void
calibrateBBoxCache(void)1523 SoShapeP::calibrateBBoxCache(void)
1524 {
1525 int i;
1526 const int ARRAYSIZE = 100;
1527
1528 // just create 100 random vertices
1529 SbVec3f vecarray[ARRAYSIZE];
1530 for (i = 0; i < ARRAYSIZE; i++) {
1531 for (int j = 0; j < 3; j++) {
1532 vecarray[i][j] = ((float) rand()) / ((float) RAND_MAX);
1533 }
1534 }
1535
1536 // FIXME: should really measure CPU time spent, and not just wall
1537 // time. See the item in Coin/docs/todo.txt on implementing a
1538 // "stopwatch" ADT. 20021111 mortene.
1539 SbTime begin = SbTime::getTimeOfDay();
1540 SbBox3f bbox;
1541 bbox.makeEmpty();
1542 for (i = 0; i < ARRAYSIZE; i++) {
1543 bbox.extendBy(vecarray[i]);
1544 }
1545 SbTime end = SbTime::getTimeOfDay();
1546 SoShapeP::bboxcachetimelimit = end.getValue() - begin.getValue();
1547 }
1548
1549 /*!
1550 Convenience method that enables vertex arrays and/or VBOs
1551 Returns \e TRUE if VBO is used.
1552
1553 \sa finishVertexArray()
1554 \since Coin 3.0
1555 */
1556 SbBool
startVertexArray(SoGLRenderAction * action,const SoCoordinateElement * coords,const SbVec3f * pervertexnormals,const SbBool texpervertex,const SbBool colorpervertex)1557 SoShape::startVertexArray(SoGLRenderAction * action,
1558 const SoCoordinateElement * coords,
1559 const SbVec3f * pervertexnormals,
1560 const SbBool texpervertex,
1561 const SbBool colorpervertex)
1562 {
1563 SoState * state = action->getState();
1564 const cc_glglue * glue = sogl_glue_instance(state);
1565 const SoGLVBOElement * vboelem = SoGLVBOElement::getInstance(state);
1566 const uint32_t contextid = action->getCacheContext();
1567
1568 SbBool dovbo = TRUE;
1569 if (!SoGLDriverDatabase::isSupported(glue, SO_GL_VBO_IN_DISPLAYLIST)) {
1570 if (SoCacheElement::anyOpen(state)) {
1571 dovbo = FALSE;
1572 }
1573 }
1574 SoVBO * vertexvbo = dovbo ? vboelem->getVertexVBO() : NULL;
1575 if (!vertexvbo) dovbo = FALSE;
1576 SbBool didbind = FALSE;
1577
1578 if (colorpervertex) {
1579 const GLvoid * dataptr = NULL;
1580 SoVBO * colorvbo = dovbo ? vboelem->getColorVBO() : NULL;
1581 SoGLLazyElement * lelem = (SoGLLazyElement*) SoLazyElement::getInstance(state);
1582 if (colorvbo) {
1583 lelem->updateColorVBO(colorvbo);
1584 colorvbo->bindBuffer(contextid);
1585 didbind = TRUE;
1586 }
1587 else {
1588 if (didbind) {
1589 cc_glglue_glBindBuffer(glue, GL_ARRAY_BUFFER, 0);
1590 didbind = FALSE;
1591 }
1592 dataptr = (const GLvoid*) lelem->getDiffusePointer();
1593 }
1594 if (colorvbo) {
1595 cc_glglue_glColorPointer(glue, 4, GL_UNSIGNED_BYTE, 0, dataptr);
1596 }
1597 else {
1598 cc_glglue_glColorPointer(glue, 3, GL_FLOAT, 0, dataptr);
1599 }
1600 cc_glglue_glEnableClientState(glue, GL_COLOR_ARRAY);
1601 }
1602 if (texpervertex) {
1603 const SoMultiTextureCoordinateElement * mtelem = NULL;
1604 const SbBool * enabledunits = NULL;
1605 int lastenabled;
1606
1607 enabledunits = SoMultiTextureEnabledElement::getEnabledUnits(state, lastenabled);
1608 if (enabledunits) {
1609 mtelem = SoMultiTextureCoordinateElement::getInstance(state);
1610 }
1611 SoVBO * vbo;
1612 if (!SoGLDriverDatabase::isSupported(glue, SO_GL_MULTITEXTURE)) {
1613 static int hasWarned = 0;
1614 if (lastenabled>0) {
1615 if (!hasWarned) {
1616 SoDebugError::postWarning("SoShape::startVertexArray",
1617 "Multitexturing is not supported on this hardware, but more than one textureunit is in use."
1618 );
1619 hasWarned = 1;
1620 }
1621 }
1622 lastenabled = 0;
1623 }
1624
1625 for (int i = 0; i <= lastenabled; i++) {
1626 if (enabledunits[i] && mtelem->getNum(i)) {
1627 int dim = mtelem->getDimension(i);
1628 const GLvoid * tptr;
1629 switch (dim) {
1630 default:
1631 case 2: tptr = (const GLvoid*) mtelem->getArrayPtr2(i); break;
1632 case 3: tptr = (const GLvoid*) mtelem->getArrayPtr3(i); break;
1633 case 4: tptr = (const GLvoid*) mtelem->getArrayPtr4(i); break;
1634 }
1635 if (SoGLDriverDatabase::isSupported(glue, SO_GL_MULTITEXTURE)) {
1636 cc_glglue_glClientActiveTexture(glue, GL_TEXTURE0 + i);
1637 }
1638 vbo = dovbo ? vboelem->getTexCoordVBO(i) : NULL;
1639 if (vbo) {
1640 vbo->bindBuffer(contextid);
1641 didbind = TRUE;
1642 tptr = NULL;
1643 }
1644 else {
1645 if (didbind) {
1646 cc_glglue_glBindBuffer(glue, GL_ARRAY_BUFFER, 0);
1647 didbind = FALSE;
1648 }
1649 }
1650 cc_glglue_glTexCoordPointer(glue, dim, GL_FLOAT, 0, tptr);
1651 cc_glglue_glEnableClientState(glue, GL_TEXTURE_COORD_ARRAY);
1652 }
1653 }
1654 }
1655 if (pervertexnormals != NULL) {
1656 SoVBO * vbo = dovbo ? vboelem->getNormalVBO() : NULL;
1657 const GLvoid * dataptr = NULL;
1658 if (vbo) {
1659 vbo->bindBuffer(contextid);
1660 didbind = TRUE;
1661 }
1662 else {
1663 dataptr = (const GLvoid*) pervertexnormals;
1664 if (didbind) {
1665 cc_glglue_glBindBuffer(glue, GL_ARRAY_BUFFER, 0);
1666 didbind = FALSE;
1667 }
1668 }
1669 cc_glglue_glNormalPointer(glue, GL_FLOAT, 0, dataptr);
1670 cc_glglue_glEnableClientState(glue, GL_NORMAL_ARRAY);
1671 }
1672 const GLvoid * dataptr = NULL;
1673 if (vertexvbo) {
1674 vertexvbo->bindBuffer(contextid);
1675 }
1676 else {
1677 dataptr = coords->is3D() ?
1678 ((const GLvoid *)coords->getArrayPtr3()) :
1679 ((const GLvoid *)coords->getArrayPtr4());
1680 }
1681 cc_glglue_glVertexPointer(glue, coords->is3D() ? 3 : 4, GL_FLOAT, 0,
1682 dataptr);
1683 cc_glglue_glEnableClientState(glue, GL_VERTEX_ARRAY);
1684
1685 SoGLVertexAttributeElement::getInstance(state)->enableVBO(action);
1686
1687 return dovbo;
1688 }
1689
1690 /*!
1691 Should be called after rendering with vertex arrays. This method
1692 will disable arrays and VBOs enabled in the startVertexArray()
1693 function.
1694
1695 \sa startVertexArray()
1696 \since Coin 3.0
1697 */
1698 void
finishVertexArray(SoGLRenderAction * action,const SbBool vbo,const SbBool normpervertex,const SbBool texpervertex,const SbBool colorpervertex)1699 SoShape::finishVertexArray(SoGLRenderAction * action,
1700 const SbBool vbo,
1701 const SbBool normpervertex,
1702 const SbBool texpervertex,
1703 const SbBool colorpervertex)
1704 {
1705 SoState * state = action->getState();
1706 const cc_glglue * glue = sogl_glue_instance(state);
1707
1708 if (vbo) {
1709 if (!SoGLDriverDatabase::isSupported(glue, SO_GL_VBO_IN_DISPLAYLIST)) {
1710 SoCacheElement::invalidate(state);
1711 SoGLCacheContextElement::shouldAutoCache(state,
1712 SoGLCacheContextElement::DONT_AUTO_CACHE);
1713 }
1714 // unset VBO buffer
1715 cc_glglue_glBindBuffer(glue, GL_ARRAY_BUFFER, 0);
1716 }
1717 cc_glglue_glDisableClientState(glue, GL_VERTEX_ARRAY);
1718 if (normpervertex) {
1719 cc_glglue_glDisableClientState(glue, GL_NORMAL_ARRAY);
1720 }
1721 if (texpervertex) {
1722 int lastenabled;
1723 const SbBool * enabledunits =
1724 SoMultiTextureEnabledElement::getEnabledUnits(state, lastenabled);
1725 if (!SoGLDriverDatabase::isSupported(glue, SO_GL_MULTITEXTURE)) {
1726 //Should already have warned in StartVertexArray
1727 lastenabled = 0;
1728 }
1729
1730 const SoMultiTextureCoordinateElement * mtelem =
1731 SoMultiTextureCoordinateElement::getInstance(state);
1732
1733 for (int i = 0; i <= lastenabled; i++) {
1734 if (enabledunits[i] && mtelem->getNum(i)) {
1735 if (SoGLDriverDatabase::isSupported(glue, SO_GL_MULTITEXTURE)) {
1736 cc_glglue_glClientActiveTexture(glue, GL_TEXTURE0 + i);
1737 }
1738 cc_glglue_glDisableClientState(glue, GL_TEXTURE_COORD_ARRAY);
1739 }
1740 }
1741 cc_glglue_glClientActiveTexture(glue, GL_TEXTURE0);
1742 }
1743 if (colorpervertex) {
1744 SoGLLazyElement * lelem = (SoGLLazyElement*) SoLazyElement::getInstance(state);
1745 lelem->reset(state, SoLazyElement::DIFFUSE_MASK);
1746
1747 cc_glglue_glDisableClientState(glue, GL_COLOR_ARRAY);
1748 }
1749
1750 SoGLVertexAttributeElement::getInstance(state)->disableVBO(action);
1751 }
1752
1753 void
validatePVCache(SoGLRenderAction * action)1754 SoShape::validatePVCache(SoGLRenderAction * action)
1755 {
1756 SoState * state = action->getState();
1757 if (PRIVATE(this)->pvcache == NULL ||
1758 !PRIVATE(this)->pvcache->isValid(state)) {
1759 if (PRIVATE(this)->pvcache) {
1760 PRIVATE(this)->pvcache->unref();
1761 }
1762 // we don't want to create display list caches while building the VBOs
1763 SoCacheElement::invalidate(state);
1764
1765 soshape_staticdata * shapedata = soshape_get_staticdata();
1766 SbBool storedinvalid = SoCacheElement::setInvalid(FALSE);
1767 // must push state to make cache dependencies work
1768 state->push();
1769 PRIVATE(this)->pvcache = new SoPrimitiveVertexCache(state);
1770 PRIVATE(this)->pvcache->ref();
1771 SoCacheElement::set(state, PRIVATE(this)->pvcache);
1772 shapedata->rendermode = PVCACHE;
1773 this->generatePrimitives(action);
1774 shapedata->rendermode = NORMAL;
1775 // needed for out old bumpmap handling
1776 if (PRIVATE(this)->bumprender) PRIVATE(this)->bumprender->calcTangentSpace(PRIVATE(this)->pvcache);
1777 // this _must_ be called after creating the pvcache
1778
1779 // FIXME: consider if we should call a virtual function here to
1780 // enable subclasses to modify the primtive vertex cache. Must be
1781 // done before to state->pop() call.
1782 state->pop();
1783 SoCacheElement::setInvalid(storedinvalid);
1784 PRIVATE(this)->pvcache->close(state);
1785 PRIVATE(this)->testSetupShapeHints(this);
1786 }
1787 }
1788
1789
1790 #undef PRIVATE
1791