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 SoArray SoArray.h Inventor/nodes/SoArray.h
35   \brief The SoArray class is a group node for setting up regular arrays of subgraphs.
36 
37   \ingroup nodes
38 
39   SoArray presents a convenient way of duplicating a node (typically a
40   shape node) or a complete subgraph in 1 to 3 dimensions.
41 
42   The child node or subgraph can only be translated by regular offsets
43   for all dimensions.
44 
45   For more flexible functionality for duplication of geometry, see the
46   SoMultipleCopy group node, which can do general transformations
47   (including rotation and scaling) for its child.
48 
49   <b>FILE FORMAT/DEFAULTS:</b>
50   \code
51     Array {
52         origin FIRST
53         numElements1 1
54         numElements2 1
55         numElements3 1
56         separation1 1 0 0
57         separation2 0 1 0
58         separation3 0 0 1
59     }
60   \endcode
61 
62   \sa SoMultipleCopy
63 */
64 
65 #include <Inventor/nodes/SoArray.h>
66 
67 #include <Inventor/nodes/SoSwitch.h> // SO_SWITCH_ALL
68 #include <Inventor/actions/SoCallbackAction.h>
69 #include <Inventor/actions/SoSearchAction.h>
70 #include <Inventor/actions/SoGLRenderAction.h>
71 #include <Inventor/actions/SoGetBoundingBoxAction.h>
72 #include <Inventor/elements/SoBBoxModelMatrixElement.h>
73 #include <Inventor/elements/SoSwitchElement.h>
74 #include <Inventor/misc/SoState.h>
75 
76 #include "nodes/SoSubNodeP.h"
77 
78 /*!
79   \enum SoArray::Origin
80 
81   The possible settings for the SoArray::origin field.
82 */
83 
84 
85 /*!
86   \var SoSFEnum SoArray::origin
87 
88   Where the origin of the array should be set, ie how the array
89   elements will be distributed from the local origo.
90 
91   Default value is SoArray::FIRST.
92 */
93 
94 /*!
95   \var SoSFShort SoArray::numElements1
96   Number of duplicates for each X axis row. Default 1.
97 */
98 /*!
99   \var SoSFShort SoArray::numElements2
100   Number of duplicates for each Y axis row. Default 1.
101 */
102 /*!
103   \var SoSFShort SoArray::numElements3
104   Number of duplicates for each Z axis row. Default 1.
105 */
106 
107 /*!
108   \var SoSFVec3f SoArray::separation1
109 
110   Distance in current units between the center point of each element
111   along the X axis. Default [1.0, 0.0, 0.0].
112 */
113 /*!
114   \var SoSFVec3f SoArray::separation2
115 
116   Distance in current units between the center point of each element
117   along the Y axis. Default [0.0, 1.0, 0.0].
118 */
119 /*!
120   \var SoSFVec3f SoArray::separation3
121 
122   Distance in current units between the center point of each element
123   along the Z axis. Default [0.0, 0.0, 1.0].
124 */
125 
126 // *************************************************************************
127 
128 SO_NODE_SOURCE(SoArray);
129 
130 /*!
131   Constructor.
132 */
SoArray(void)133 SoArray::SoArray(void)
134 {
135   SO_NODE_INTERNAL_CONSTRUCTOR(SoArray);
136 
137   SO_NODE_ADD_FIELD(origin, (SoArray::FIRST));
138   SO_NODE_ADD_FIELD(numElements1, (1));
139   SO_NODE_ADD_FIELD(numElements2, (1));
140   SO_NODE_ADD_FIELD(numElements3, (1));
141   SO_NODE_ADD_FIELD(separation1, (SbVec3f(1, 0, 0)));
142   SO_NODE_ADD_FIELD(separation2, (SbVec3f(0, 1, 0)));
143   SO_NODE_ADD_FIELD(separation3, (SbVec3f(0, 0, 1)));
144 
145 
146   SO_NODE_DEFINE_ENUM_VALUE(Origin, FIRST);
147   SO_NODE_DEFINE_ENUM_VALUE(Origin, CENTER);
148   SO_NODE_DEFINE_ENUM_VALUE(Origin, LAST);
149   SO_NODE_SET_SF_ENUM_TYPE(origin, Origin);
150 }
151 
152 /*!
153   Destructor.
154 */
~SoArray()155 SoArray::~SoArray()
156 {
157 }
158 
159 // Doc in superclass.
160 void
initClass(void)161 SoArray::initClass(void)
162 {
163   SO_NODE_INTERNAL_INIT_CLASS(SoArray, SO_FROM_INVENTOR_1);
164 }
165 
166 // Doc in superclass.
167 void
getBoundingBox(SoGetBoundingBoxAction * action)168 SoArray::getBoundingBox(SoGetBoundingBoxAction * action)
169 {
170 #if 0 // OBSOLETED: mortene's old (buggy ?) code (removed 19990423, pederb)
171   // store incoming modelmatrix
172   SbMatrix mat = SoModelMatrixElement::get(action->getState());
173 
174   // get reference to the box
175   SbXfBox3f & box = action->getXfBoundingBox();
176 
177   // store current bbox
178   SbXfBox3f incomingbox = box;
179 
180   // accumulation variables
181   SbVec3f acccenter(0.0f, 0.0f, 0.0f);
182   int numCenters = 0;
183   SbXfBox3f totalbox;
184 
185   for (int i=0; i < numElements3.getValue(); i++) {
186     for (int j=0; j < numElements2.getValue(); j++) {
187       for (int k=0; k < numElements1.getValue(); k++) {
188 
189         float multfactor_i = float(i);
190         float multfactor_j = float(j);
191         float multfactor_k = float(k);
192 
193         switch (origin.getValue()) {
194         case SoArray::FIRST:
195           break;
196         case SoArray::CENTER:
197           multfactor_i = -float(numElements3.getValue()-1.0f)/2.0f + float(i);
198           multfactor_j = -float(numElements2.getValue()-1.0f)/2.0f + float(j);
199           multfactor_k = -float(numElements1.getValue()-1.0f)/2.0f + float(k);
200           break;
201         case SoArray::LAST:
202           multfactor_i = -multfactor_i;
203           multfactor_j = -multfactor_j;
204           multfactor_k = -multfactor_k;
205           break;
206 
207         default: assert(0); break;
208         }
209 
210         SbVec3f instance_pos =
211           separation3.getValue() * multfactor_i +
212           separation2.getValue() * multfactor_j +
213           separation1.getValue() * multfactor_k;
214 
215 #if 0 // debug
216         SoDebugError::postInfo("SoArray::getBoundingBox",
217                                "instance_pos: <%f, %f, %f>",
218                                instance_pos[0],
219                                instance_pos[1],
220                                instance_pos[2]);
221 #endif // debug
222 
223         SbMatrix mat;
224         mat.setTranslate(instance_pos);
225 
226         action->getState()->push();
227 
228         SoSwitchElement::set(action->getState(),
229                              i * numElements2.getValue() *
230                              numElements1.getValue() +
231                              j * numElements1.getValue() + k);
232 
233         // make current box empty to calculate bbox of this separator
234         box.makeEmpty();
235         box.setTransform(SbMatrix::identity());
236 
237         // set local matrix to identity
238         SoBBoxModelMatrixElement::set(action->getState(), this, mat);
239 
240         // traverse all children, calculate the local bbox
241         inherited::getBoundingBox(action);
242 
243         // If center point is set, accumulate center.
244         if (action->isCenterSet()) {
245           acccenter += action->getCenter();
246           numCenters++;
247           action->resetCenter();
248         }
249 
250         // expand box by stored bbox
251         if (!totalbox.isEmpty()) box.extendBy(totalbox);
252         totalbox = box;
253 
254         action->getState()->pop();
255       }
256     }
257   }
258 
259   // transform the local bbox by stored model matrix
260   if (!box.isEmpty()) box.transform(mat);
261   if (!incomingbox.isEmpty()) box.extendBy(incomingbox);
262 
263   if (numCenters != 0)
264     action->setCenter(acccenter / numCenters, FALSE);
265 
266 #else // "new" code, 19990423, pederb
267 
268   float curri = 0.0f;
269   float currj = 0.0f;
270   float currk = 0.0f;
271 
272   float inci = 1.0f;
273   float incj = 1.0f;
274   float inck = 1.0f;
275 
276   // accumulation variables
277   SbVec3f acccenter(0.0f, 0.0f, 0.0f);
278   int numCenters = 0;
279 
280   switch (origin.getValue()) {
281   case SoArray::FIRST:
282     break;
283   case SoArray::CENTER:
284     curri = -(numElements3.getValue()-1.0f)/2.0f;
285     currj = -(numElements2.getValue()-1.0f)/2.0f;
286     currk = -(numElements1.getValue()-1.0f)/2.0f;
287     break;
288   case SoArray::LAST:
289     inci = -1.0f;
290     incj = -1.0f;
291     inck = -1.0f;
292     break;
293   default: assert(0); break;
294   }
295 
296   float initj = currj;
297   float initk = currk;
298 
299   int N = 0;
300   for (int i=0; i < numElements3.getValue(); i++) {
301     currj = initj;
302     for (int j=0; j < numElements2.getValue(); j++) {
303       currk = initk;
304       for (int k=0; k < numElements1.getValue(); k++) {
305 
306         SbVec3f instance_pos =
307           separation3.getValue() * curri +
308           separation2.getValue() * currj +
309           separation1.getValue() * currk;
310 
311         action->getState()->push();
312 
313         // translate bbox matrix
314         SoBBoxModelMatrixElement::translateBy(action->getState(),
315                                               this, instance_pos);
316         SoSwitchElement::set(action->getState(),N++);
317 
318         inherited::getBoundingBox(action);
319 
320         // If center point is set, accumulate center.
321         if (action->isCenterSet()) {
322           acccenter += action->getCenter();
323           numCenters++;
324           action->resetCenter();
325         }
326         // pop back to the original bboxmatrix
327         action->getState()->pop();
328         currk += inck;
329       }
330       currj += incj;
331     }
332     curri += inci;
333   }
334 
335   if (numCenters != 0)
336     action->setCenter(acccenter / float(numCenters), FALSE);
337 #endif // end of new code by pederb
338 }
339 
340 // Doc in superclass.
341 void
GLRender(SoGLRenderAction * action)342 SoArray::GLRender(SoGLRenderAction * action)
343 {
344   SoArray::doAction(action);
345 }
346 
347 // Doc in superclass.
348 SbBool
affectsState(void) const349 SoArray::affectsState(void) const
350 {
351   return FALSE; // state is pushed/popped for each traversal
352 }
353 
354 // Doc in superclass.
355 void
doAction(SoAction * action)356 SoArray::doAction(SoAction *action)
357 {
358   int N = 0;
359   for (int i=0; i < numElements3.getValue(); i++) {
360     for (int j=0; j < numElements2.getValue(); j++) {
361       for (int k=0; k < numElements1.getValue(); k++) {
362 
363         float multfactor_i = float(i);
364         float multfactor_j = float(j);
365         float multfactor_k = float(k);
366 
367         switch (origin.getValue()) {
368         case SoArray::FIRST:
369           break;
370         case SoArray::CENTER:
371           multfactor_i = -float(numElements3.getValue()-1.0f)/2.0f + float(i);
372           multfactor_j = -float(numElements2.getValue()-1.0f)/2.0f + float(j);
373           multfactor_k = -float(numElements1.getValue()-1.0f)/2.0f + float(k);
374           break;
375         case SoArray::LAST:
376           multfactor_i = -multfactor_i;
377           multfactor_j = -multfactor_j;
378           multfactor_k = -multfactor_k;
379           break;
380 
381         default: assert(0); break;
382         }
383 
384         SbVec3f instance_pos =
385           separation3.getValue() * multfactor_i +
386           separation2.getValue() * multfactor_j +
387           separation1.getValue() * multfactor_k;
388 
389         action->getState()->push();
390 
391         SoSwitchElement::set(action->getState(),
392                              N++);
393 
394         SoModelMatrixElement::translateBy(action->getState(), this,
395                                           instance_pos);
396 
397         inherited::doAction(action);
398         action->getState()->pop();
399       }
400     }
401   }
402 }
403 
404 // Doc in superclass.
405 void
callback(SoCallbackAction * action)406 SoArray::callback(SoCallbackAction *action)
407 {
408   SoArray::doAction((SoAction*)action);
409 }
410 
411 // Doc in superclass.
412 void
pick(SoPickAction * action)413 SoArray::pick(SoPickAction *action)
414 {
415   // We came across what we think is a bug in TGS/SGI OIV when
416   // implementing picking for this node and testing against the
417   // original Inventor library. The SoPickedPoint class can return the
418   // object space point, normal and texture coordinates. TGS/SGI OIV
419   // do not consider the translation inside this node before returning
420   // the object space data from SoPickedPoint, since the path in
421   // SoPickedPoint does not say anything about on which copy the pick
422   // occured.
423   //
424   // We solved this simply by extending SoPickedPoint for storing both
425   // world space and object space data.
426 
427   SoArray::doAction((SoAction*)action);
428 }
429 
430 // Doc in superclass.
431 void
handleEvent(SoHandleEventAction * action)432 SoArray::handleEvent(SoHandleEventAction *action)
433 {
434   SoNode::handleEvent(action);
435   inherited::handleEvent(action);
436 }
437 
438 // Doc in superclass
439 void
audioRender(SoAudioRenderAction * action)440 SoArray::audioRender(SoAudioRenderAction * action)
441 {
442   SoArray::doAction((SoAction*)action);
443 }
444 
445 // Doc in superclass.
446 void
getMatrix(SoGetMatrixAction * action)447 SoArray::getMatrix(SoGetMatrixAction *action)
448 {
449   // path does not specify which copy to traverse
450   inherited::getMatrix(action);
451 }
452 
453 // Doc in superclass.
454 void
search(SoSearchAction * action)455 SoArray::search(SoSearchAction * action)
456 {
457   SoState * state = action->getState();
458   state->push();
459   // set Switch element so that subgraphs depending on this element
460   // will traverse all children (it's set during normal traversal in
461   // doAction()).
462   SoSwitchElement::set(action->getState(), SO_SWITCH_ALL);
463   // just use SoGroup::search() to traverse all children.
464   inherited::search(action);
465   state->pop();
466 }
467 
468 // Doc in superclass.
469 void
getPrimitiveCount(SoGetPrimitiveCountAction * action)470 SoArray::getPrimitiveCount(SoGetPrimitiveCountAction *action)
471 {
472   SoArray::doAction((SoAction*)action);
473 }
474