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 SoIndexedShape SoIndexedShape.h Inventor/nodes/SoIndexedShape.h
35   \brief The SoIndexedShape class is the superclass for all indexed vertex shapes.
36 
37   \ingroup nodes
38 
39   This is an abstract class which contains storage for four fields for
40   indices to coordinates, normals, materials and texture coordinates
41   for it's subclasses.
42 */
43 
44 #include <Inventor/nodes/SoIndexedShape.h>
45 
46 #include <Inventor/actions/SoAction.h>
47 #include <Inventor/elements/SoCoordinateElement.h>
48 #include <Inventor/elements/SoShapeHintsElement.h>
49 #include <Inventor/elements/SoNormalBindingElement.h>
50 #include <Inventor/elements/SoTextureCoordinateBindingElement.h>
51 #include <Inventor/errors/SoDebugError.h>
52 #include <Inventor/caches/SoNormalCache.h>
53 #include <Inventor/nodes/SoVertexProperty.h>
54 
55 #include "nodes/SoSubNodeP.h"
56 #include "coindefs.h" // COIN_OBSOLETED()
57 
58 /*!
59   \var SoMFInt32 SoIndexedShape::coordIndex
60   Coordinate indices.
61 */
62 /*!
63   \var SoMFInt32 SoIndexedShape::materialIndex
64   Material indices.
65 */
66 /*!
67   \var SoMFInt32 SoIndexedShape::normalIndex
68   Normal indices.
69 */
70 /*!
71   \var SoMFInt32 SoIndexedShape::textureCoordIndex
72   Texture coordinate indices.
73 */
74 
75 
76 SO_NODE_ABSTRACT_SOURCE(SoIndexedShape);
77 
78 /*!
79   Constructor.
80 */
SoIndexedShape()81 SoIndexedShape::SoIndexedShape()
82 {
83   SO_NODE_INTERNAL_CONSTRUCTOR(SoIndexedShape);
84 
85   SO_NODE_ADD_FIELD(coordIndex,(0));
86   SO_NODE_ADD_FIELD(materialIndex,(-1));
87   SO_NODE_ADD_FIELD(normalIndex,(-1));
88   SO_NODE_ADD_FIELD(textureCoordIndex,(-1));
89 }
90 
91 /*!
92   Destructor.
93 */
~SoIndexedShape()94 SoIndexedShape::~SoIndexedShape()
95 {
96 }
97 
98 // Documented in superclass.
99 void
initClass(void)100 SoIndexedShape::initClass(void)
101 {
102   SO_NODE_INTERNAL_INIT_ABSTRACT_CLASS(SoIndexedShape, SO_FROM_INVENTOR_1);
103 }
104 
105 // Collects common error msg code for computeBBox() for both
106 // Coordinate3 and Coordinate4 loops.
107 static void
error_idx_out_of_bounds(const SoIndexedShape * node,int idxidx,int upper)108 error_idx_out_of_bounds(const SoIndexedShape * node, int idxidx, int upper)
109 {
110   SbName n = node->getName();
111   SbString ns(" ");
112   ns += n;
113 
114   SbName t = node->getTypeId().getName();
115 
116   SbString bounds;
117   if (upper > 0) {
118     // (Note: the if-expression above should have been "upper >= 0",
119     // if it weren't for the default SoCoordinateElement containing
120     // one default coordinate.)
121     bounds.sprintf("should be within [0, %d]", upper);
122   }
123   else {
124     bounds = "but no coordinates are available from the state!";
125   }
126 
127   SoDebugError::post("SoIndexedShape::computeBBox",
128                      "coordinate index nr %d for %snode%s of type %s is "
129                      "out of bounds: is %d, %s",
130                      idxidx,
131                      (n == "") ? "<unnamed> " : "",
132                      (n != "") ? ns.getString() : "",
133                      t.getString(),
134                      node->coordIndex[idxidx],
135                      bounds.getString());
136 }
137 
138 // Documented in superclass. Overridden to calculate bounding box of
139 // all indexed coordinates, using the coordIndex field.
140 void
computeBBox(SoAction * action,SbBox3f & box,SbVec3f & center)141 SoIndexedShape::computeBBox(SoAction * action, SbBox3f & box, SbVec3f & center)
142 {
143   assert(box.isEmpty());
144   SoState * state = action->getState();
145 
146   const SoCoordinateElement * coordelem = NULL;
147   SoNode *vpnode = this->vertexProperty.getValue();
148   SoVertexProperty *vp =
149     (vpnode && vpnode->isOfType(SoVertexProperty::getClassTypeId())) ?
150     (SoVertexProperty *)vpnode : NULL;
151   SbBool vpvtx = vp && (vp->vertex.getNum() > 0);
152   if (!vpvtx) {
153     coordelem = SoCoordinateElement::getInstance(state);
154   }
155 
156   const int numcoords = vpvtx ? vp->vertex.getNum() : coordelem->getNum();
157   int numacc = 0; // to calculate weighted center point
158   center.setValue(0.0f, 0.0f, 0.0f);
159 
160   if (vpvtx || coordelem->is3D()) {
161     const SbVec3f * coords = vpvtx ?
162       vp->vertex.getValues(0) :
163       coordelem->getArrayPtr3();
164 
165     const int32_t * ptr = this->coordIndex.getValues(0);
166     const int32_t * endptr = ptr + this->coordIndex.getNum();
167     while (ptr < endptr) {
168       const int idx = *ptr++;
169       if (idx < numcoords) {
170         if (idx >= 0) {
171           box.extendBy(coords[idx]);
172           center += coords[idx];
173           numacc++;
174         }
175       }
176 #if COIN_DEBUG
177       else {
178         const ptrdiff_t faultyidxpos = (ptr - this->coordIndex.getValues(0)) - 1;
179         error_idx_out_of_bounds(this, (int)faultyidxpos, numcoords - 1);
180         if (numcoords <= 1) break; // give only one error msg on missing coords
181         // (the default state is that there's a default
182         // SoCoordinateElement element with a single default
183         // coordinate point setup)
184       }
185 #endif // COIN_DEBUG
186     }
187   }
188   else {
189     SbVec3f tmp;
190     const SbVec4f * coords = coordelem->getArrayPtr4();
191     const int32_t * ptr = this->coordIndex.getValues(0);
192     const int32_t * endptr = ptr + this->coordIndex.getNum();
193     while (ptr < endptr) {
194       int idx = *ptr++;
195       if (idx < numcoords) {
196         if (idx >= 0) {
197           SbVec4f h = coords[idx];
198           h.getReal(tmp);
199           box.extendBy(tmp);
200           center += tmp;
201           numacc++;
202         }
203       }
204 #if COIN_DEBUG
205       else {
206         const ptrdiff_t faultyidxpos = (ptr - this->coordIndex.getValues(0)) - 1;
207         error_idx_out_of_bounds(this, (int)faultyidxpos, numcoords - 1);
208         if (numcoords <= 1) break; // give only one error msg on missing coords
209         // (the default state is that there's a default
210         // SoCoordinateElement element with a single default
211         // coordinate point setup)
212       }
213 #endif // COIN_DEBUG
214     }
215   }
216   if (numacc) center /= (float) numacc;
217 }
218 
219 /*!
220   Returns whether texture coordinates should be indexed or not.
221 
222   \sa SoTextureCoordinateBinding
223 */
224 SbBool
areTexCoordsIndexed(SoAction * action)225 SoIndexedShape::areTexCoordsIndexed(SoAction * action)
226 {
227   return SoTextureCoordinateBindingElement::get(action->getState()) ==
228     SoTextureCoordinateBindingElement::PER_VERTEX_INDEXED;
229 }
230 
231 /*!
232   Starting at index position \a startCoord, returns the number of
233   indices until either the end of index array or a separator index (-1).
234 */
235 int
getNumVerts(const int startCoord)236 SoIndexedShape::getNumVerts(const int startCoord)
237 {
238   const int32_t * ptr = this->coordIndex.getValues(0);
239   const int32_t * endptr = ptr + this->coordIndex.getNum();
240   ptr += startCoord;
241   int cnt = 0;
242   while (ptr < endptr && *ptr >= 0) cnt++, ptr++;
243   return cnt;
244 }
245 
246 /*!
247   Not implemented. Probably only used for internal purposes in SGI's
248   original Open Inventor, which means it should have been private.
249 
250   Let us know if you need this method for any code you are porting and
251   we'll look into implement it properly.
252 */
253 void
setupIndices(const int,const int,const SbBool,const SbBool)254 SoIndexedShape::setupIndices(const int /* numParts */,
255                              const int /* numFaces */,
256                              const SbBool /* needNormals */,
257                              const SbBool /* needTexCoords */)
258 {
259   COIN_OBSOLETED();
260 }
261 
262 /*!
263   Not implemented. Probably only used for internal purposes in SGI's
264   original Open Inventor, which means it should have been private.
265 
266   Let us know if you need this method for any code you are porting and
267   we'll look into implement it properly.
268 */
269 const int32_t *
getNormalIndices()270 SoIndexedShape::getNormalIndices()
271 {
272   COIN_OBSOLETED();
273   return NULL;
274 }
275 
276 /*!
277   Not implemented. Probably only used for internal purposes in SGI's
278   original Open Inventor, which means it should have been private.
279 
280   Let us know if you need this method for any code you are porting and
281   we'll look into implement it properly.
282 */
283 const int32_t *
getColorIndices()284 SoIndexedShape::getColorIndices()
285 {
286   COIN_OBSOLETED();
287   return NULL;
288 }
289 
290 /*!
291   Not implemented. Probably only used for internal purposes in SGI's
292   original Open Inventor, which means it should have been private.
293 
294   Let us know if you need this method for any code you are porting and
295   we'll look into implement it properly.
296 */
297 const int32_t *
getTexCoordIndices()298 SoIndexedShape::getTexCoordIndices()
299 {
300   COIN_OBSOLETED();
301   return NULL;
302 }
303 
304 /*!
305   Convenience method that will fetch data needed for rendering or
306   generating primitives. Takes care of normal cache.
307 
308   This method was not part of the original SGI Open Inventor API, and
309   is an extension specific for Coin.
310 */
311 SbBool
getVertexData(SoState * state,const SoCoordinateElement * & coords,const SbVec3f * & normals,const int32_t * & cindices,const int32_t * & nindices,const int32_t * & tindices,const int32_t * & mindices,int & numcindices,const SbBool needNormals,SbBool & normalCacheUsed)312 SoIndexedShape::getVertexData(SoState * state,
313                               const SoCoordinateElement *& coords,
314                               const SbVec3f *& normals,
315                               const int32_t *& cindices,
316                               const int32_t *& nindices,
317                               const int32_t *& tindices,
318                               const int32_t *& mindices,
319                               int & numcindices,
320                               const SbBool needNormals,
321                               SbBool & normalCacheUsed)
322 {
323   SoVertexShape::getVertexData(state, coords, normals, needNormals);
324 
325   cindices = this->coordIndex.getValues(0);
326   numcindices = this->coordIndex.getNum();
327 
328   mindices = this->materialIndex.getValues(0);
329   if (this->materialIndex.getNum() <= 0 || mindices[0] < 0) mindices = NULL;
330 
331   tindices = this->textureCoordIndex.getValues(0);
332   if (this->textureCoordIndex.getNum() <= 0 || tindices[0] < 0) tindices = NULL;
333 
334   normalCacheUsed = FALSE;
335   nindices = NULL;
336   if (needNormals) {
337     nindices = this->normalIndex.getValues(0);
338     if (this->normalIndex.getNum() <= 0 || nindices[0] < 0) nindices = NULL;
339 
340     if (normals == NULL) {
341       SoNormalCache * nc = this->generateAndReadLockNormalCache(state);
342       normals = nc->getNormals();
343       nindices = nc->getIndices();
344       normalCacheUsed = TRUE;
345 
346       // if no normals were generated, unlock normal cache before
347       // returning
348       if (normals == NULL) {
349         this->readUnlockNormalCache();
350         normalCacheUsed = FALSE;
351       }
352     }
353   }
354   return TRUE;
355 }
356