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 SoVertexShape SoVertexShape.h Inventor/nodes/SoVertexShape.h
35   \brief The SoVertexShape class is the superclass for all vertex based shapes.
36 
37   \ingroup nodes
38 
39   Basically, every polygon-, line- or point-based shape will inherit
40   this class.  It contains methods for organizing the normal cache,
41   and also holds the SoVertexShape::vertexProperty field which can be
42   used to set vertex data inside the node.
43 */
44 
45 // *************************************************************************
46 
47 #include <Inventor/nodes/SoVertexShape.h>
48 
49 #ifdef HAVE_CONFIG_H
50 #include <config.h>
51 #endif // HAVE_CONFIG_H
52 
53 #include <Inventor/actions/SoGLRenderAction.h>
54 #include <Inventor/caches/SoNormalCache.h>
55 #include <Inventor/elements/SoCacheElement.h>
56 #include <Inventor/elements/SoCoordinateElement.h>
57 #include <Inventor/elements/SoCreaseAngleElement.h>
58 #include <Inventor/elements/SoGLShapeHintsElement.h>
59 #include <Inventor/elements/SoNormalElement.h>
60 #include <Inventor/misc/SoState.h>
61 #include <Inventor/nodes/SoVertexProperty.h>
62 #include <Inventor/threads/SbRWMutex.h>
63 
64 #include "nodes/SoSubNodeP.h"
65 #include "tidbitsp.h"
66 
67 // *************************************************************************
68 
69 /*!
70   \var SoSFNode SoVertexShape::vertexProperty
71 
72   If you set the vertexProperty field, it should be with an
73   SoVertexProperty node. Otherwise it will simply be
74   ignored. Nodetypes inheriting SoVertexShape will then get their
75   coordinate data from the vertexProperty node instead of from the
76   global traversal state.
77 
78   The vertexProperty field of SoVertexShape-derived nodes breaks
79   somewhat with the basic design of Open Inventor, as its contents are
80   not passed to the global state. This is done to provide a simple
81   path to highly optimized rendering of vertexbased shapes.
82 
83   \sa SoVertexProperty
84 
85   \since Coin 1.0
86   \since SGI Inventor 2.1
87 */
88 
89 // *************************************************************************
90 
91 class SoVertexShapeP {
92 public:
93   SoNormalCache * normalcache;
94 
95   // we can use a per-instance mutex here instead of this class-wide
96   // one, but we go for the class-wide one since at least MSWindows
97   // might have a rather strict limit on the total amount of mutex
98   // resources a process / user can hold at any one time.
99   //
100   // i haven't looked too hard at the affected code regions in the
101   // sub-classes, however. it might be that a class-wide lock can
102   // cause significantly less efficient execution in a multi-threaded
103   // environment. if so, we will have to come up with something better
104   // than just a class-wide lock (a mutex pool or something, i
105   // suppose).
106   //
107   // -mortene.
108   static SbRWMutex * normalcachemutex;
109 
110   static void cleanup(void);
111 };
112 
113 // called by atexit
114 void
cleanup(void)115 SoVertexShapeP::cleanup(void)
116 {
117   delete SoVertexShapeP::normalcachemutex;
118   SoVertexShapeP::normalcachemutex = NULL;
119 }
120 
121 SbRWMutex * SoVertexShapeP::normalcachemutex = NULL;
122 
123 #define PRIVATE(obj) ((obj)->pimpl)
124 
125 // *************************************************************************
126 
127 SO_NODE_ABSTRACT_SOURCE(SoVertexShape);
128 
129 // *************************************************************************
130 
131 
132 // doc from superclass
133 void
initClass(void)134 SoVertexShape::initClass(void)
135 {
136   SO_NODE_INTERNAL_INIT_ABSTRACT_CLASS(SoVertexShape, SO_FROM_INVENTOR_1);
137 
138 #ifdef COIN_THREADSAFE
139   SoVertexShapeP::normalcachemutex = new SbRWMutex(SbRWMutex::READ_PRECEDENCE);
140 #endif // COIN_THREADSAFE
141 
142   coin_atexit((coin_atexit_f *)SoVertexShapeP::cleanup, CC_ATEXIT_NORMAL);
143 }
144 
145 /*!
146   Constructor.
147 */
SoVertexShape(void)148 SoVertexShape::SoVertexShape(void)
149 {
150   PRIVATE(this) = new SoVertexShapeP;
151   PRIVATE(this)->normalcache = NULL;
152 
153   SO_NODE_INTERNAL_CONSTRUCTOR(SoVertexShape);
154 
155   SO_NODE_ADD_FIELD(vertexProperty, (NULL));
156 }
157 
158 /*!
159   Destructor.
160 */
~SoVertexShape()161 SoVertexShape::~SoVertexShape()
162 {
163   if (PRIVATE(this)->normalcache) PRIVATE(this)->normalcache->unref();
164   delete PRIVATE(this);
165 }
166 
167 // *************************************************************************
168 
169 // Documented in superclass.
170 void
notify(SoNotList * nl)171 SoVertexShape::notify(SoNotList * nl)
172 {
173   this->readLockNormalCache();
174   if (PRIVATE(this)->normalcache) PRIVATE(this)->normalcache->invalidate();
175   this->readUnlockNormalCache();
176   inherited::notify(nl);
177 }
178 
179 // This documentation block has a copy in vrml97/VertexShape.cpp.
180 /*!
181   \COININTERNAL
182 
183   Subclasses should override this method to generate default normals
184   using the SoNormalBundle class. \c TRUE should be returned if
185   normals were generated, \c FALSE otherwise.
186 
187   Default method returns \c FALSE.
188 
189   \COIN_FUNCTION_EXTENSION
190 */
191 SbBool
generateDefaultNormals(SoState *,SoNormalBundle *)192 SoVertexShape::generateDefaultNormals(SoState *, SoNormalBundle *)
193 {
194   return FALSE;
195 }
196 
197 // This documentation block has a copy in vrml97/VertexShape.cpp.
198 /*!
199   \COININTERNAL
200 
201   Subclasses should override this method to generate default normals
202   using the SoNormalCache class. This is more effective than using
203   SoNormalGenerator. Return \c TRUE if normals were generated, \c
204   FALSE otherwise.
205 
206   Default method just returns \c FALSE.
207 
208   \COIN_FUNCTION_EXTENSION
209 */
210 SbBool
generateDefaultNormals(SoState *,SoNormalCache *)211 SoVertexShape::generateDefaultNormals(SoState * /* state */,
212                                       SoNormalCache * /* nc */)
213 {
214   return FALSE;
215 }
216 
217 // doc from superclass
218 SbBool
shouldGLRender(SoGLRenderAction * action)219 SoVertexShape::shouldGLRender(SoGLRenderAction * action)
220 {
221   return SoShape::shouldGLRender(action);
222 }
223 
224 /*!
225   Sets normal cache to contain the normals specified by \a normals and \a num,
226   and forces cache dependencies on coordinates, shape hints and crease angle.
227 */
228 void
setNormalCache(SoState * const state,const int num,const SbVec3f * normals)229 SoVertexShape::setNormalCache(SoState * const state,
230                               const int num,
231                               const SbVec3f * normals)
232 {
233   this->writeLockNormalCache();
234   if (PRIVATE(this)->normalcache) PRIVATE(this)->normalcache->unref();
235   // create new normal cache with no dependencies
236   state->push();
237   PRIVATE(this)->normalcache = new SoNormalCache(state);
238   PRIVATE(this)->normalcache->ref();
239   PRIVATE(this)->normalcache->set(num, normals);
240   // force element dependencies
241   (void) SoCoordinateElement::getInstance(state);
242   (void) SoShapeHintsElement::getVertexOrdering(state);
243   (void) SoCreaseAngleElement::get(state);
244   state->pop();
245   this->writeUnlockNormalCache();
246 }
247 
248 /*!
249   Returns the current normal cache, or NULL if there is none.
250 */
251 SoNormalCache *
getNormalCache(void) const252 SoVertexShape::getNormalCache(void) const
253 {
254   return PRIVATE(this)->normalcache;
255 }
256 
257 /*!
258 
259   Convenience method that can be used by subclasses to return or
260   create a normal cache. If the current cache is not valid, it takes
261   care of unrefing the old cache and pushing and popping the state to
262   create element dependencies when creating the new cache.
263 
264   When returning from this method, the normal cache will be
265   read locked, and the caller should call readUnlockNormalCache()
266   when the normals in the cache is no longer needed.
267 
268   \COIN_FUNCTION_EXTENSION
269 
270   \since Coin 2.0
271 */
272 SoNormalCache *
generateAndReadLockNormalCache(SoState * const state)273 SoVertexShape::generateAndReadLockNormalCache(SoState * const state)
274 {
275   this->readLockNormalCache();
276   if (PRIVATE(this)->normalcache && PRIVATE(this)->normalcache->isValid(state)) {
277     return PRIVATE(this)->normalcache;
278   }
279   this->readUnlockNormalCache();
280   this->writeLockNormalCache();
281 
282   SbBool storeinvalid = SoCacheElement::setInvalid(FALSE);
283 
284   if (PRIVATE(this)->normalcache) PRIVATE(this)->normalcache->unref();
285   state->push(); // need to push for cache dependencies
286   PRIVATE(this)->normalcache = new SoNormalCache(state);
287   PRIVATE(this)->normalcache->ref();
288   SoCacheElement::set(state, PRIVATE(this)->normalcache);
289   //
290   // See if the node supports the Coin-way of generating normals
291   //
292   if (!generateDefaultNormals(state, PRIVATE(this)->normalcache)) {
293     // FIXME: implement SoNormalBundle
294     if (generateDefaultNormals(state, (SoNormalBundle *)NULL)) {
295       // FIXME: set generator in normal cache
296     }
297   }
298   state->pop(); // don't forget this pop
299 
300   SoCacheElement::setInvalid(storeinvalid);
301   this->writeUnlockNormalCache();
302   this->readLockNormalCache();
303   return PRIVATE(this)->normalcache;
304 }
305 
306 /*!
307   Convenience method that returns the current coordinate and normal
308   element. This method is not part of the OIV API.
309 */
310 void
getVertexData(SoState * state,const SoCoordinateElement * & coords,const SbVec3f * & normals,const SbBool neednormals)311 SoVertexShape::getVertexData(SoState * state,
312                              const SoCoordinateElement *& coords,
313                              const SbVec3f *& normals,
314                              const SbBool neednormals)
315 {
316   coords = SoCoordinateElement::getInstance(state);
317   assert(coords);
318 
319   normals = NULL;
320   if (neednormals) {
321     normals = SoNormalElement::getInstance(state)->getArrayPtr();
322   }
323 }
324 
325 // doc from superclass
326 void
write(SoWriteAction * action)327 SoVertexShape::write(SoWriteAction * action)
328 {
329   inherited::write(action);
330 }
331 
332 // *************************************************************************
333 
334 /*!
335   Read lock the normal cache. This method should be called before
336   fetching the normal cache (using getNormalCache()). When the cached
337   normals are no longer needed, readUnlockNormalCache() must be called.
338 
339   It is also possible to use generateAndReadLockNormalCache().
340 
341   \COIN_FUNCTION_EXTENSION
342 
343   \sa readUnlockNormalCache()
344   \since Coin 2.0
345 */
346 void
readLockNormalCache(void)347 SoVertexShape::readLockNormalCache(void)
348 {
349   if (SoVertexShapeP::normalcachemutex) {
350     SoVertexShapeP::normalcachemutex->readLock();
351   }
352 }
353 
354 /*!
355   Read unlock the normal cache. Should be called when the read-locked
356   cached normals are no longer needed.
357 
358   \sa readLockNormalCache()
359   \since Coin 2.0
360 */
361 void
readUnlockNormalCache(void)362 SoVertexShape::readUnlockNormalCache(void)
363 {
364   if (SoVertexShapeP::normalcachemutex) {
365     SoVertexShapeP::normalcachemutex->readUnlock();
366   }
367 }
368 
369 void
writeLockNormalCache(void)370 SoVertexShape::writeLockNormalCache(void)
371 {
372   if (SoVertexShapeP::normalcachemutex) {
373     SoVertexShapeP::normalcachemutex->writeLock();
374   }
375 }
376 
377 void
writeUnlockNormalCache(void)378 SoVertexShape::writeUnlockNormalCache(void)
379 {
380   if (SoVertexShapeP::normalcachemutex) {
381     SoVertexShapeP::normalcachemutex->writeUnlock();
382   }
383 }
384 
385 // *************************************************************************
386 
387 #undef PRIVATE
388