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