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