|
1 /* |
|
2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of the License "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: Node implementation |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 /*! |
|
20 * \internal |
|
21 * \file |
|
22 * \brief Node implementation |
|
23 */ |
|
24 |
|
25 #ifndef M3G_CORE_INCLUDE |
|
26 # error included by m3g_core.c; do not compile separately. |
|
27 #endif |
|
28 |
|
29 #include "m3g_node.h" |
|
30 #include "m3g_memory.h" |
|
31 #include "m3g_animationtrack.h" |
|
32 #include "m3g_skinnedmesh.h" |
|
33 #include "m3g_tcache.h" |
|
34 #include "m3g_transformable.h" |
|
35 |
|
36 #define TARGET_NONE 0 |
|
37 #define TARGET_X_AXIS 1 |
|
38 #define TARGET_Y_AXIS 2 |
|
39 #define TARGET_Z_AXIS 3 |
|
40 #define TARGET_ORIGIN 4 |
|
41 |
|
42 /*---------------------------------------------------------------------- |
|
43 * Private functions |
|
44 *--------------------------------------------------------------------*/ |
|
45 |
|
46 static M3Guint internalTarget(M3Genum target) |
|
47 { |
|
48 switch (target) { |
|
49 case M3G_NONE: |
|
50 return TARGET_NONE; |
|
51 case M3G_ORIGIN: |
|
52 return TARGET_ORIGIN; |
|
53 case M3G_X_AXIS: |
|
54 return TARGET_X_AXIS; |
|
55 case M3G_Y_AXIS: |
|
56 return TARGET_Y_AXIS; |
|
57 case M3G_Z_AXIS: |
|
58 return TARGET_Z_AXIS; |
|
59 default: |
|
60 M3G_ASSERT(M3G_FALSE); |
|
61 return TARGET_NONE; |
|
62 } |
|
63 } |
|
64 |
|
65 static M3Guint externalTarget(M3Genum target) |
|
66 { |
|
67 switch (target) { |
|
68 case TARGET_NONE: |
|
69 return M3G_NONE; |
|
70 case TARGET_ORIGIN: |
|
71 return M3G_ORIGIN; |
|
72 case TARGET_X_AXIS: |
|
73 return M3G_X_AXIS; |
|
74 case TARGET_Y_AXIS: |
|
75 return M3G_Y_AXIS; |
|
76 case TARGET_Z_AXIS: |
|
77 return M3G_Z_AXIS; |
|
78 default: |
|
79 M3G_ASSERT(M3G_FALSE); |
|
80 return M3G_NONE; |
|
81 } |
|
82 } |
|
83 |
|
84 /*---------------------------------------------------------------------- |
|
85 * Constructor & destructor |
|
86 *--------------------------------------------------------------------*/ |
|
87 |
|
88 /*! |
|
89 * \internal |
|
90 * \brief Initializes a Node object. See specification |
|
91 * for default values. |
|
92 * |
|
93 * \param m3g M3G interface |
|
94 * \param node Node object |
|
95 * \param vfTable virtual function table |
|
96 */ |
|
97 static void m3gInitNode(Interface *m3g, Node *node, M3GClass classID) |
|
98 { |
|
99 /* Node is derived from Transformable */ |
|
100 m3gInitTransformable(&node->transformable, m3g, classID); |
|
101 |
|
102 /* Set default values */ |
|
103 |
|
104 node->enableBits = (NODE_RENDER_BIT|NODE_PICK_BIT); |
|
105 node->alphaFactor = (1u << NODE_ALPHA_FACTOR_BITS) - 1; |
|
106 node->scope = -1; |
|
107 node->zTarget = TARGET_NONE; |
|
108 node->yTarget = TARGET_NONE; |
|
109 } |
|
110 |
|
111 /*! |
|
112 * \internal |
|
113 * \brief Destroys this Node object. |
|
114 * |
|
115 * \param obj Node object |
|
116 */ |
|
117 static void m3gDestroyNode(Object *obj) |
|
118 { |
|
119 Node *node = (Node *) obj; |
|
120 M3G_VALIDATE_OBJECT(node); |
|
121 M3G_ASSERT(node->parent == NULL); |
|
122 m3gDestroyTransformable((Object *) node); |
|
123 } |
|
124 |
|
125 /*---------------------------------------------------------------------- |
|
126 * Internal functions |
|
127 *--------------------------------------------------------------------*/ |
|
128 |
|
129 /*! |
|
130 * \internal |
|
131 * \brief Checks if node is a child of the parent. |
|
132 * |
|
133 * \param parent assumed parent Node object |
|
134 * \param child Node object to check |
|
135 * \retval M3G_TRUE is a child |
|
136 * \retval M3G_FALSE is not a child |
|
137 */ |
|
138 static M3Gbool m3gIsChildOf(const Node *parent, const Node *child) |
|
139 { |
|
140 const Node *n; |
|
141 |
|
142 for (n = child; n != NULL; n = n->parent) { |
|
143 if (n->parent == parent) return M3G_TRUE; |
|
144 } |
|
145 |
|
146 return M3G_FALSE; |
|
147 } |
|
148 |
|
149 /*! |
|
150 * \internal |
|
151 * \brief Executes the given function for each node in a subtree |
|
152 * |
|
153 * The function \c func is executed recursively in each branch, |
|
154 * starting from the leaves. That is, the function is called for the |
|
155 * children of each group before the group itself. |
|
156 * |
|
157 * \param node the node containing the subtree to process |
|
158 * \param func pointer to the function to all for each node |
|
159 * \param params pointer to function-dependent arguments to pass |
|
160 * to each \c func invokation; this may be e.g. a structure |
|
161 * modified by \c func |
|
162 * |
|
163 * \return The return value of the top-level call to \c func |
|
164 */ |
|
165 static void m3gForSubtree(Node *node, NodeFuncPtr func, void *params) |
|
166 { |
|
167 M3GClass nodeClass; |
|
168 M3G_VALIDATE_OBJECT(node); |
|
169 |
|
170 /* Recurse into the children first */ |
|
171 |
|
172 nodeClass = M3G_CLASS(node); |
|
173 |
|
174 if (nodeClass == M3G_CLASS_SKINNED_MESH) { |
|
175 m3gForSubtree((Node*)((SkinnedMesh*)node)->skeleton, func, params); |
|
176 } |
|
177 else if (nodeClass == M3G_CLASS_GROUP || |
|
178 nodeClass == M3G_CLASS_WORLD) { |
|
179 Group *group = (Group*) node; |
|
180 Node *child = group->firstChild; |
|
181 if (child) { |
|
182 do { |
|
183 Node *next = child->right; |
|
184 m3gForSubtree(child, func, params); |
|
185 child = next; |
|
186 } while (child != group->firstChild); |
|
187 } |
|
188 } |
|
189 |
|
190 /* Execute function on self */ |
|
191 |
|
192 (*func)(node, params); |
|
193 } |
|
194 |
|
195 /*! |
|
196 * \internal |
|
197 * \brief Overloaded Object3D method |
|
198 * |
|
199 * \param property animation property |
|
200 * \retval M3G_TRUE property supported |
|
201 * \retval M3G_FALSE property not supported |
|
202 */ |
|
203 static M3Gbool m3gNodeIsCompatible(M3Gint property) |
|
204 { |
|
205 switch (property) { |
|
206 case M3G_ANIM_ALPHA: |
|
207 case M3G_ANIM_PICKABILITY: |
|
208 case M3G_ANIM_VISIBILITY: |
|
209 return M3G_TRUE; |
|
210 default: |
|
211 return m3gTransformableIsCompatible(property); |
|
212 } |
|
213 } |
|
214 |
|
215 /*! |
|
216 * \internal |
|
217 * \brief Overloaded Object3D method |
|
218 * |
|
219 * \param self Node object |
|
220 * \param property animation property |
|
221 * \param valueSize size of value array |
|
222 * \param value value array |
|
223 */ |
|
224 static void m3gNodeUpdateProperty(Object *self, |
|
225 M3Gint property, |
|
226 M3Gint valueSize, |
|
227 const M3Gfloat *value) |
|
228 { |
|
229 Node *node = (Node *)self; |
|
230 M3G_VALIDATE_OBJECT(node); |
|
231 M3G_ASSERT_PTR(value); |
|
232 |
|
233 switch (property) { |
|
234 case M3G_ANIM_ALPHA: |
|
235 M3G_ASSERT(valueSize >= 1); |
|
236 node->alphaFactor = |
|
237 m3gRoundToInt( |
|
238 m3gMul(m3gClampFloat(value[0], 0.f, 1.f), |
|
239 (float)((1 << NODE_ALPHA_FACTOR_BITS) - 1))); |
|
240 break; |
|
241 case M3G_ANIM_PICKABILITY: |
|
242 M3G_ASSERT(valueSize >= 1); |
|
243 node->enableBits &= ~NODE_PICK_BIT; |
|
244 if (value[0] >= 0.5f) { |
|
245 node->enableBits |= NODE_PICK_BIT; |
|
246 } |
|
247 break; |
|
248 case M3G_ANIM_VISIBILITY: |
|
249 M3G_ASSERT(valueSize >= 1); |
|
250 node->enableBits &= ~NODE_RENDER_BIT; |
|
251 if (value[0] >= 0.5f) { |
|
252 node->enableBits |= NODE_RENDER_BIT; |
|
253 } |
|
254 break; |
|
255 default: |
|
256 m3gTransformableUpdateProperty(self, property, valueSize, value); |
|
257 } |
|
258 } |
|
259 |
|
260 /*! |
|
261 * \internal |
|
262 * \brief Overloaded Object3D method |
|
263 * |
|
264 * \param originalObj original Node object |
|
265 * \param cloneObj pointer to cloned Node object |
|
266 * \param pairs array for all object-duplicate pairs |
|
267 * \param numPairs number of pairs |
|
268 */ |
|
269 static M3Gbool m3gNodeDuplicate(const Object *originalObj, |
|
270 Object **cloneObj, |
|
271 Object **pairs, |
|
272 M3Gint *numPairs) |
|
273 { |
|
274 Node *original = (Node *)originalObj; |
|
275 Node *clone = (Node *)*cloneObj; |
|
276 M3G_ASSERT_PTR(*cloneObj); /* abstract class, must be derived */ |
|
277 |
|
278 /* Duplicate base class data */ |
|
279 |
|
280 if (!m3gTransformableDuplicate(originalObj, cloneObj, pairs, numPairs)) { |
|
281 return M3G_FALSE; |
|
282 } |
|
283 |
|
284 /* Duplicate our own data */ |
|
285 |
|
286 clone->zReference = original->zReference; |
|
287 clone->yReference = original->yReference; |
|
288 clone->zTarget = original->zTarget; |
|
289 clone->yTarget = original->yTarget; |
|
290 clone->enableBits = original->enableBits; |
|
291 clone->alphaFactor = original->alphaFactor; |
|
292 clone->scope = original->scope; |
|
293 clone->hasBones = original->hasBones; |
|
294 clone->hasRenderables = original->hasRenderables; |
|
295 |
|
296 return M3G_TRUE; |
|
297 } |
|
298 |
|
299 /*! |
|
300 * \internal |
|
301 * \brief Find corresponding duplicate for a Node |
|
302 * |
|
303 * \param node Node object |
|
304 * \param pairs array for all object-duplicate pairs |
|
305 * \param numPairs number of pairs |
|
306 */ |
|
307 static Node *m3gGetDuplicatedInstance(Node *node, Object **pairs, M3Gint numPairs) |
|
308 { |
|
309 M3Gint i; |
|
310 for (i = 0; i < numPairs; i++) |
|
311 if (pairs[i * 2] == (Object *)node) |
|
312 return (Node *)pairs[i * 2 + 1]; |
|
313 return NULL; |
|
314 } |
|
315 |
|
316 /*! |
|
317 * \internal |
|
318 * \brief Updates references of the duplicate object. |
|
319 * |
|
320 * When objects are duplicated scenegraph references |
|
321 * must be updated to equivalent duplicated references. |
|
322 * This function is overloaded by objects that have |
|
323 * references that has to be updated. |
|
324 * |
|
325 * \param self Node object |
|
326 * \param pairs array for all object-duplicate pairs |
|
327 * \param numPairs number of pairs |
|
328 */ |
|
329 static void m3gNodeUpdateDuplicateReferences(Node *self, Object **pairs, M3Gint numPairs) |
|
330 { |
|
331 if (self->zTarget != TARGET_NONE && self->zReference != NULL) { |
|
332 Node *duplicatedInstance = m3gGetDuplicatedInstance(self, pairs, numPairs); |
|
333 Node *duplicatedRef = m3gGetDuplicatedInstance(self->zReference, pairs, numPairs); |
|
334 if (duplicatedRef != NULL |
|
335 && m3gIsChildOf(m3gGetRoot(duplicatedInstance), duplicatedRef)) { |
|
336 duplicatedInstance->zReference = duplicatedRef; |
|
337 } |
|
338 } |
|
339 if (self->yTarget != TARGET_NONE && self->yReference != NULL) { |
|
340 Node *duplicatedInstance = m3gGetDuplicatedInstance(self, pairs, numPairs); |
|
341 Node *duplicatedRef = m3gGetDuplicatedInstance(self->yReference, pairs, numPairs); |
|
342 if (duplicatedRef != NULL |
|
343 && m3gIsChildOf(m3gGetRoot(duplicatedInstance), duplicatedRef)) { |
|
344 duplicatedInstance->yReference = duplicatedRef; |
|
345 } |
|
346 } |
|
347 } |
|
348 |
|
349 /*! |
|
350 * \internal |
|
351 * \brief Gets size of the subtree |
|
352 * |
|
353 * \param node Node object |
|
354 * \param numRef number of references |
|
355 */ |
|
356 static void m3gDoGetSubtreeSize(Node *node, void *numRef) |
|
357 { |
|
358 M3Gint *num = (M3Gint *)numRef; |
|
359 M3G_UNREF(node); |
|
360 (*num)++; |
|
361 } |
|
362 |
|
363 /*! |
|
364 * \internal |
|
365 * \brief Default function for non-pickable objects |
|
366 * |
|
367 * \param self Camera object |
|
368 * \param mask pick scope mask |
|
369 * \param ray pick ray |
|
370 * \param ri RayIntersection object |
|
371 * \param toGroup transform to originating group |
|
372 * \retval M3G_TRUE always return success |
|
373 */ |
|
374 static M3Gbool m3gNodeRayIntersect(Node *self, |
|
375 M3Gint mask, |
|
376 M3Gfloat *ray, |
|
377 RayIntersection *ri, |
|
378 Matrix *toGroup) |
|
379 { |
|
380 M3G_UNREF(self); |
|
381 M3G_UNREF(mask); |
|
382 M3G_UNREF(ray); |
|
383 M3G_UNREF(ri); |
|
384 M3G_UNREF(toGroup); |
|
385 |
|
386 return M3G_TRUE; |
|
387 } |
|
388 |
|
389 /*! |
|
390 * \internal |
|
391 * \brief Computes the bounding box for this node |
|
392 * |
|
393 * \param self node pointer |
|
394 * \param bbox bounding box structure filled in for non-zero return values |
|
395 * |
|
396 * \return The "yield" factor for the node, i.e. the approximate |
|
397 * rendering cost of the node \em including any internal bounding box |
|
398 * checks; the yield factor is used to estimate the benefit of adding |
|
399 * enclosing bounding boxes at higher levels in the scene tree |
|
400 */ |
|
401 static M3Gint m3gNodeGetBBox(Node *self, AABB *bbox) |
|
402 { |
|
403 M3G_UNREF(self); |
|
404 M3G_UNREF(bbox); |
|
405 return 0; |
|
406 } |
|
407 |
|
408 /*! |
|
409 * \internal |
|
410 * \brief Updates the bounding box for this node |
|
411 */ |
|
412 static M3Gbool m3gNodeValidate(Node *self, M3Gbitmask stateBits, M3Gint scope) |
|
413 { |
|
414 M3G_UNREF(stateBits); |
|
415 M3G_UNREF(scope); |
|
416 |
|
417 /* Invalidate parent state in case we've encountered a previously |
|
418 * disabled node, then reset the dirty bits */ |
|
419 |
|
420 if (self->dirtyBits && self->parent) { |
|
421 m3gInvalidateNode(self->parent, self->dirtyBits); |
|
422 } |
|
423 self->dirtyBits = 0; |
|
424 return M3G_TRUE; |
|
425 } |
|
426 |
|
427 /*! |
|
428 * \internal |
|
429 * \brief Gets a vector according to alignment target |
|
430 * and transforms it with a given transform. |
|
431 * |
|
432 * \param target alignment target |
|
433 * \param transform transform to be applied |
|
434 * \param out vector to fill in |
|
435 */ |
|
436 static void m3gTransformAlignmentTarget(M3Genum target, |
|
437 const Matrix *transform, |
|
438 Vec4 *out) |
|
439 { |
|
440 switch (target) { |
|
441 case TARGET_ORIGIN: |
|
442 *out = Vec4_ORIGIN; |
|
443 break; |
|
444 case TARGET_X_AXIS: |
|
445 *out = Vec4_X_AXIS; |
|
446 break; |
|
447 case TARGET_Y_AXIS: |
|
448 *out = Vec4_Y_AXIS; |
|
449 break; |
|
450 case TARGET_Z_AXIS: |
|
451 *out = Vec4_Z_AXIS; |
|
452 break; |
|
453 default: |
|
454 M3G_ASSERT(M3G_FALSE); |
|
455 } |
|
456 |
|
457 m3gTransformVec4(transform, out); |
|
458 } |
|
459 |
|
460 /*! |
|
461 * \internal |
|
462 * \brief Computes a single alignment rotation for a node. |
|
463 * |
|
464 * \param node Node object |
|
465 * \param srcAxis source axis |
|
466 * \param targetNode Node object |
|
467 * \param targetAxisName target axis name |
|
468 * \param constraint constraint |
|
469 */ |
|
470 static M3Gbool m3gComputeAlignmentRotation(Node *node, |
|
471 const Vec3 *srcAxis, |
|
472 const Node *targetNode, |
|
473 M3Genum targetAxisName, |
|
474 M3Genum constraint) |
|
475 { |
|
476 const Node *parent = node->parent; |
|
477 Matrix transform; |
|
478 Vec4 targetAxis; |
|
479 |
|
480 M3G_VALIDATE_OBJECT(parent); |
|
481 M3G_ASSERT(constraint == TARGET_NONE || constraint == TARGET_Z_AXIS); |
|
482 |
|
483 /* Get the transformation from the reference target node to the |
|
484 * current node, omitting all components except translation. |
|
485 * Rotation is also applied if this is a constrained alignment. */ |
|
486 { |
|
487 const Transformable *tf = &node->transformable; |
|
488 |
|
489 if (!m3gGetTransformTo((M3GNode) targetNode, (M3GNode) parent, |
|
490 &transform)) { |
|
491 return M3G_FALSE; |
|
492 } |
|
493 m3gPreTranslateMatrix(&transform, -tf->tx, -tf->ty, -tf->tz); |
|
494 |
|
495 if (constraint != TARGET_NONE) { |
|
496 Quat rot = tf->orientation; |
|
497 rot.w = -rot.w; |
|
498 m3gPreRotateMatrixQuat(&transform, &rot); |
|
499 } |
|
500 } |
|
501 |
|
502 m3gTransformAlignmentTarget(targetAxisName, &transform, &targetAxis); |
|
503 |
|
504 /* Apply the Z constraint if enabled; this is done by simply |
|
505 * zeroing the Z component of the target vector. If the X and Y |
|
506 * alone don't span a non-zero vector, just exit as there's |
|
507 * nothing defined to rotate about. */ |
|
508 |
|
509 if (constraint == TARGET_Z_AXIS) { |
|
510 M3Gfloat norm = m3gAdd(m3gMul(targetAxis.x, targetAxis.x), |
|
511 m3gMul(targetAxis.y, targetAxis.y)); |
|
512 if (norm < 1.0e-5f) { |
|
513 return M3G_TRUE; |
|
514 } |
|
515 norm = m3gRcpSqrt(norm); |
|
516 |
|
517 targetAxis.x = m3gMul(targetAxis.x, norm); |
|
518 targetAxis.y = m3gMul(targetAxis.y, norm); |
|
519 targetAxis.z = 0.0f; |
|
520 |
|
521 M3G_ASSERT(srcAxis->z == 0.0f); |
|
522 } |
|
523 else { |
|
524 m3gNormalizeVec3((Vec3*)&targetAxis); /* srcAxis will be unit length */ |
|
525 } |
|
526 |
|
527 if (constraint != TARGET_NONE) { |
|
528 Quat rot; |
|
529 m3gSetQuatRotation(&rot, srcAxis, (const Vec3*) &targetAxis); |
|
530 m3gMulQuat(&node->transformable.orientation, &rot); |
|
531 } |
|
532 else { |
|
533 m3gSetQuatRotation(&node->transformable.orientation, |
|
534 srcAxis, (const Vec3*) &targetAxis); |
|
535 } |
|
536 |
|
537 /* Invalidate transformations and bounding boxes after setting |
|
538 * node orientation */ |
|
539 |
|
540 m3gInvalidateTransformable((Transformable*)node); |
|
541 |
|
542 return M3G_TRUE; |
|
543 } |
|
544 |
|
545 /*! |
|
546 * \internal |
|
547 * \brief Computes alignment for a single node. |
|
548 * |
|
549 * \param node Node object |
|
550 * \param refNode Node object |
|
551 * \retval M3G_TRUE alignment ok |
|
552 * \retval M3G_FALSE alignment failed |
|
553 */ |
|
554 static M3Gbool m3gComputeAlignment(Node *node, const Node *refNode) |
|
555 { |
|
556 const Node *root = m3gGetRoot(node); |
|
557 const Node *zRef = node->zReference; |
|
558 const Node *yRef = node->yReference; |
|
559 const M3Genum zTarget = node->zTarget; |
|
560 const M3Genum yTarget = node->yTarget; |
|
561 M3G_VALIDATE_OBJECT(node); |
|
562 |
|
563 /* Quick exit if nothing to do */ |
|
564 |
|
565 if (zTarget == TARGET_NONE && yTarget == TARGET_NONE) { |
|
566 return M3G_TRUE; |
|
567 } |
|
568 |
|
569 /* Check scene graph state */ |
|
570 |
|
571 if (zRef != NULL && (m3gIsChildOf(node, zRef) || m3gGetRoot(zRef) != root)) { |
|
572 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_OPERATION); |
|
573 return M3G_FALSE; |
|
574 } |
|
575 if (yRef != NULL && (m3gIsChildOf(node, yRef) || m3gGetRoot(yRef) != root)) { |
|
576 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_OPERATION); |
|
577 return M3G_FALSE; |
|
578 } |
|
579 |
|
580 /* Compute the alignment rotations for Z and Y */ |
|
581 { |
|
582 if (node->zTarget != TARGET_NONE) { |
|
583 if (zRef == NULL && refNode == node) { |
|
584 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_OPERATION); |
|
585 return M3G_FALSE; |
|
586 } |
|
587 if (!m3gComputeAlignmentRotation( |
|
588 node, |
|
589 (const Vec3*) &Vec4_Z_AXIS, |
|
590 zRef != NULL ? zRef : refNode, |
|
591 zTarget, |
|
592 TARGET_NONE)) { |
|
593 return M3G_FALSE; |
|
594 } |
|
595 } |
|
596 if (node->yTarget != TARGET_NONE) { |
|
597 if (yRef == NULL && refNode == node) { |
|
598 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_OPERATION); |
|
599 return M3G_FALSE; |
|
600 } |
|
601 if (!m3gComputeAlignmentRotation( |
|
602 node, |
|
603 (const Vec3*) &Vec4_Y_AXIS, |
|
604 yRef != NULL ? yRef : refNode, |
|
605 yTarget, |
|
606 zTarget != TARGET_NONE ? TARGET_Z_AXIS : TARGET_NONE)) { |
|
607 return M3G_FALSE; |
|
608 } |
|
609 } |
|
610 } |
|
611 return M3G_TRUE; |
|
612 } |
|
613 |
|
614 /*! |
|
615 * \internal |
|
616 * \brief Gets the transformation to an ancestor node |
|
617 */ |
|
618 static void m3gGetTransformUpPath(const Node *node, const Node *ancestor, Matrix *transform) |
|
619 { |
|
620 M3G_ASSERT(node); |
|
621 |
|
622 if (node == ancestor) { |
|
623 m3gIdentityMatrix(transform); |
|
624 } |
|
625 else { |
|
626 TCache *tc; |
|
627 M3G_ASSERT(!ancestor || m3gIsChildOf(ancestor, node)); |
|
628 |
|
629 /* Look for a cached path */ |
|
630 |
|
631 tc = m3gGetTransformCache(M3G_INTERFACE(node)); |
|
632 if (m3gGetCachedPath(tc, node, ancestor, transform)) { |
|
633 return; |
|
634 } |
|
635 |
|
636 /* No dice -- do a recursive search and cache the result */ |
|
637 |
|
638 if (node->parent == ancestor) { |
|
639 m3gGetCompositeNodeTransform(node, transform); |
|
640 } |
|
641 else { |
|
642 m3gGetTransformUpPath(node->parent, ancestor, transform); |
|
643 { |
|
644 Matrix mtx; |
|
645 m3gGetCompositeNodeTransform(node, &mtx); |
|
646 m3gMulMatrix(transform, &mtx); |
|
647 } |
|
648 } |
|
649 m3gCachePath(tc, node, ancestor, transform); |
|
650 } |
|
651 } |
|
652 |
|
653 /*! |
|
654 * \internal |
|
655 * \brief Gets depth of a node in the scenegraph. |
|
656 * |
|
657 * \param node Node object |
|
658 * \return Depth of the node |
|
659 */ |
|
660 static M3Gint m3gGetDepth(const Node *node) |
|
661 { |
|
662 const Node *n = node; |
|
663 M3Gint depth = 0; |
|
664 |
|
665 while (n->parent != NULL) { |
|
666 n = n->parent; |
|
667 depth++; |
|
668 } |
|
669 |
|
670 return depth; |
|
671 } |
|
672 |
|
673 /*! |
|
674 * \internal |
|
675 * \brief Gets root of a node in the scenegraph. |
|
676 * |
|
677 * \param node Node object |
|
678 * \return root Node object |
|
679 */ |
|
680 static Node *m3gGetRoot(const Node *node) |
|
681 { |
|
682 const Node *n = node; |
|
683 |
|
684 while (n->parent != NULL) { |
|
685 n = n->parent; |
|
686 } |
|
687 |
|
688 return (Node *)n; |
|
689 } |
|
690 |
|
691 /*! |
|
692 * \internal |
|
693 * \brief Gets total alpha factor. |
|
694 * |
|
695 * \param node Node object |
|
696 * \param root root Node object |
|
697 */ |
|
698 static M3Guint m3gGetTotalAlphaFactor(Node *node, const Node *root) |
|
699 { |
|
700 const Node *n = node; |
|
701 M3Guint f = node->alphaFactor; |
|
702 |
|
703 while (n->parent != NULL && n != root) { |
|
704 n = n->parent; |
|
705 f = ((f + 1) * n->alphaFactor) >> NODE_ALPHA_FACTOR_BITS; |
|
706 } |
|
707 return f; |
|
708 } |
|
709 |
|
710 /*! |
|
711 * \internal |
|
712 * \brief Checks if node is enabled for rendering from the root. |
|
713 * |
|
714 * \param node Node object |
|
715 * \param root root Node object |
|
716 * \retval M3G_TRUE node is visible |
|
717 * \retval M3G_FALSE node is not visible |
|
718 */ |
|
719 static M3Gbool m3gHasEnabledPath(const Node *node, const Node *root) |
|
720 { |
|
721 const Node *n; |
|
722 |
|
723 for (n = node; n != NULL; n = n->parent) { |
|
724 if (!(n->enableBits & NODE_RENDER_BIT)) { |
|
725 return M3G_FALSE; |
|
726 } |
|
727 if (n == root) { |
|
728 break; |
|
729 } |
|
730 } |
|
731 |
|
732 return M3G_TRUE; |
|
733 } |
|
734 |
|
735 /*! |
|
736 * \internal |
|
737 * \brief Checks if node is pickable from the root. |
|
738 * |
|
739 * \param node Node object |
|
740 * \param root root Node object |
|
741 * \retval M3G_TRUE node is pickable |
|
742 * \retval M3G_FALSE node is not pickable |
|
743 */ |
|
744 static M3Gbool m3gHasPickablePath(const Node *node, const Node *root) |
|
745 { |
|
746 const Node *n; |
|
747 |
|
748 for (n = node; n != NULL; n = n->parent) { |
|
749 if (!(n->enableBits & NODE_PICK_BIT)) { |
|
750 return M3G_FALSE; |
|
751 } |
|
752 if (n == root) { |
|
753 break; |
|
754 } |
|
755 } |
|
756 |
|
757 return M3G_TRUE; |
|
758 } |
|
759 |
|
760 #if defined(M3G_ENABLE_VF_CULLING) |
|
761 /*! |
|
762 * \brief Invalidates the bounding box hierarchy from a node upwards |
|
763 */ |
|
764 static void m3gInvalidateNode(Node *node, M3Gbitmask flags) |
|
765 { |
|
766 Interface *m3g = M3G_INTERFACE(node); |
|
767 M3G_BEGIN_PROFILE(m3g, M3G_PROFILE_VFC_UPDATE); |
|
768 |
|
769 while (node && (node->dirtyBits & flags) != flags) { |
|
770 node->dirtyBits |= flags; |
|
771 node = node->parent; |
|
772 } |
|
773 M3G_END_PROFILE(m3g, M3G_PROFILE_VFC_UPDATE); |
|
774 } |
|
775 #endif /*M3G_ENABLE_VF_CULLING*/ |
|
776 |
|
777 /*! |
|
778 * \internal |
|
779 * \brief Aligns a node. |
|
780 * |
|
781 * \param node Node object |
|
782 * \param ref reference Node object |
|
783 * |
|
784 * \retval M3G_TRUE continue align |
|
785 * \retval M3G_FALSE abort align |
|
786 */ |
|
787 static M3Gbool m3gNodeAlign(Node *node, const Node *ref) |
|
788 { |
|
789 if (ref == NULL) { |
|
790 return m3gComputeAlignment(node, node); |
|
791 } |
|
792 else { |
|
793 M3G_VALIDATE_OBJECT(ref); |
|
794 return m3gComputeAlignment(node, ref); |
|
795 } |
|
796 } |
|
797 |
|
798 /*! |
|
799 * \internal |
|
800 * \brief Updates node counters when moving nodes around |
|
801 */ |
|
802 static void m3gUpdateNodeCounters(Node *node, |
|
803 M3Gint nonCullableChange, |
|
804 M3Gint renderableChange) |
|
805 { |
|
806 Interface *m3g = M3G_INTERFACE(node); |
|
807 M3Gbool hasRenderables = (renderableChange > 0); |
|
808 M3G_BEGIN_PROFILE(m3g, M3G_PROFILE_VFC_UPDATE); |
|
809 while (node) { |
|
810 M3GClass nodeClass = M3G_CLASS(node); |
|
811 if (nodeClass == M3G_CLASS_GROUP || nodeClass == M3G_CLASS_WORLD) { |
|
812 Group *g = (Group *) node; |
|
813 g->numNonCullables = (M3Gushort)(g->numNonCullables + nonCullableChange); |
|
814 g->numRenderables = (M3Gushort)(g->numRenderables + renderableChange); |
|
815 hasRenderables = (g->numRenderables > 0); |
|
816 } |
|
817 node->hasRenderables = hasRenderables; |
|
818 node = node->parent; |
|
819 } |
|
820 M3G_END_PROFILE(m3g, M3G_PROFILE_VFC_UPDATE); |
|
821 } |
|
822 |
|
823 /*! |
|
824 * \internal |
|
825 * \brief Sets the parent link of this node to a new value |
|
826 * |
|
827 * Relevant reference counts are updated accordingly, so note that |
|
828 * setting the parent to NULL may lead to either the node or the |
|
829 * parent itself being destroyed. |
|
830 * |
|
831 * \param node Node object |
|
832 * \param parent parent Node object |
|
833 */ |
|
834 static void m3gSetParent(Node *node, Node *parent) |
|
835 { |
|
836 M3GClass nodeClass; |
|
837 M3Gint nonCullableChange = 0, renderableChange = 0; |
|
838 M3G_VALIDATE_OBJECT(node); |
|
839 |
|
840 /* Determine the number of various kinds of nodes being moved around */ |
|
841 |
|
842 M3G_BEGIN_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_VFC_UPDATE); |
|
843 nodeClass = M3G_CLASS(node); |
|
844 switch (nodeClass) { |
|
845 case M3G_CLASS_GROUP: |
|
846 { |
|
847 const Group *g = (const Group *) node; |
|
848 nonCullableChange = g->numNonCullables; |
|
849 renderableChange = g->numRenderables; |
|
850 break; |
|
851 } |
|
852 case M3G_CLASS_SPRITE: |
|
853 renderableChange = 1; |
|
854 if (m3gIsScaledSprite((M3GSprite) node)) { |
|
855 break; |
|
856 } |
|
857 /* conditional fall-through! */ |
|
858 case M3G_CLASS_LIGHT: |
|
859 nonCullableChange = 1; |
|
860 break; |
|
861 case M3G_CLASS_SKINNED_MESH: |
|
862 { |
|
863 const SkinnedMesh *mesh = (const SkinnedMesh *) node; |
|
864 nonCullableChange += mesh->skeleton->numNonCullables; |
|
865 renderableChange += mesh->skeleton->numRenderables + 1; |
|
866 break; |
|
867 } |
|
868 case M3G_CLASS_MESH: |
|
869 case M3G_CLASS_MORPHING_MESH: |
|
870 renderableChange = 1; |
|
871 break; |
|
872 default: |
|
873 ; |
|
874 } |
|
875 M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_VFC_UPDATE); |
|
876 |
|
877 /* Invalidate any cached transformation paths through this node |
|
878 * *before* we move the node */ |
|
879 |
|
880 m3gInvalidateCachedPaths(m3gGetTransformCache(M3G_INTERFACE(node)), node); |
|
881 |
|
882 /* Update bookkeeping for the old parent tree */ |
|
883 |
|
884 if (node->parent) { |
|
885 m3gUpdateNodeCounters(node->parent, |
|
886 -nonCullableChange, -renderableChange); |
|
887 if (renderableChange) { |
|
888 m3gInvalidateNode(node->parent, NODE_BBOX_BIT|NODE_TRANSFORMS_BIT); |
|
889 } |
|
890 } |
|
891 |
|
892 /* Change the parent link */ |
|
893 |
|
894 if (node->parent == NULL && parent != NULL) { |
|
895 node->parent = parent; |
|
896 m3gAddRef((Object *) node); |
|
897 } |
|
898 else if (node->parent != NULL && parent == NULL) { |
|
899 node->parent = parent; |
|
900 m3gDeleteRef((Object *) node); |
|
901 } |
|
902 |
|
903 /* Update bookkeeping for the new parent tree */ |
|
904 |
|
905 if (parent) { |
|
906 M3Gbitmask dirtyBits = node->dirtyBits; |
|
907 if (renderableChange) { |
|
908 dirtyBits |= NODE_BBOX_BIT; |
|
909 } |
|
910 if (node->hasBones) { |
|
911 dirtyBits |= NODE_TRANSFORMS_BIT; |
|
912 } |
|
913 m3gUpdateNodeCounters(parent, nonCullableChange, renderableChange); |
|
914 m3gInvalidateNode(parent, dirtyBits); |
|
915 } |
|
916 } |
|
917 |
|
918 /*! |
|
919 * \brief Computes the "near" and "far" box vertices |
|
920 * for plane testing |
|
921 */ |
|
922 static M3G_INLINE void m3gGetTestPoints(const Vec3 *planeNormal, |
|
923 const AABB *box, |
|
924 Vec3 *vNear, Vec3 *vFar) |
|
925 { |
|
926 const M3Gfloat *fNormal = (const M3Gfloat*) planeNormal; |
|
927 M3Gfloat *fNear = (M3Gfloat*) vNear; |
|
928 M3Gfloat *fFar = (M3Gfloat*) vFar; |
|
929 int i; |
|
930 |
|
931 for (i = 0; i < 3; ++i) { |
|
932 M3Gfloat n = *fNormal++; |
|
933 if (IS_NEGATIVE(n)) { |
|
934 *fNear++ = box->max[i]; |
|
935 *fFar++ = box->min[i]; |
|
936 } |
|
937 else { |
|
938 *fNear++ = box->min[i]; |
|
939 *fFar++ = box->max[i]; |
|
940 } |
|
941 } |
|
942 } |
|
943 |
|
944 #if defined(M3G_ENABLE_VF_CULLING) |
|
945 /*! |
|
946 * \internal |
|
947 * \brief Update the frustum culling mask for one level of an AABB hierarchy |
|
948 * |
|
949 * \param s the current traversal state |
|
950 * \param bbox the bounding box to check against |
|
951 */ |
|
952 static void m3gUpdateCullingMask(SetupRenderState *s, |
|
953 const Camera *cam, const AABB *bbox) |
|
954 { |
|
955 M3Gbitmask cullMask = s->cullMask; |
|
956 M3G_BEGIN_PROFILE(M3G_INTERFACE(cam), M3G_PROFILE_VFC_TEST); |
|
957 |
|
958 /* First, check whether any planes are previously marked as |
|
959 * intersecting */ |
|
960 |
|
961 if (cullMask & CULLMASK_ALL) { |
|
962 |
|
963 /* We need to do some culling, so let's get the planes and the |
|
964 * transformation matrix; note that the "toCamera" matrix is |
|
965 * the inverse of the camera-to-node matrix, so we only need |
|
966 * to transpose */ |
|
967 |
|
968 M3Gbitmask planeMask; |
|
969 const Vec4 *camPlanes = m3gFrustumPlanes(cam); |
|
970 |
|
971 Matrix t; |
|
972 m3gMatrixTranspose(&t, &s->toCamera); |
|
973 |
|
974 /* Loop over the active frustum planes, testing the ones we've |
|
975 * previously intersected with */ |
|
976 |
|
977 planeMask = CULLMASK_INTERSECTS; |
|
978 while (planeMask <= cullMask) { |
|
979 if (cullMask & planeMask) { |
|
980 |
|
981 /* Transform the respective frustum plane into the node |
|
982 * local space our AABB is in */ |
|
983 |
|
984 Vec4 plane; |
|
985 plane = *camPlanes++; |
|
986 m3gTransformVec4(&t, &plane); |
|
987 |
|
988 /* Test the AABB against the plane and update the mask |
|
989 * based on the result */ |
|
990 |
|
991 m3gIncStat(M3G_INTERFACE(cam), M3G_STAT_CULLING_TESTS, 1); |
|
992 { |
|
993 /* Get the "near" and "far" corner points of the box */ |
|
994 |
|
995 const Vec3* normal = (Vec3*) &plane; |
|
996 Vec3 vNear, vFar; |
|
997 m3gGetTestPoints(normal, bbox, &vNear, &vFar); |
|
998 |
|
999 /* Our normals point inside, so flip this */ |
|
1000 |
|
1001 plane.w = m3gNegate(plane.w); |
|
1002 |
|
1003 /* "Far" point behind plane? */ |
|
1004 |
|
1005 if (m3gDot3(normal, &vFar) < plane.w) { |
|
1006 /* All outside, no need to test further! */ |
|
1007 cullMask = 0; |
|
1008 break; |
|
1009 } |
|
1010 |
|
1011 /* "Near" point in front of plane? */ |
|
1012 |
|
1013 if (m3gDot3(normal, &vNear) > plane.w) { |
|
1014 cullMask &= ~planeMask; |
|
1015 cullMask |= planeMask >> 1; /* intersects->inside */ |
|
1016 } |
|
1017 } |
|
1018 } |
|
1019 planeMask <<= 2; /* next plane */ |
|
1020 } |
|
1021 s->cullMask = cullMask; /* write the output mask */ |
|
1022 } |
|
1023 M3G_END_PROFILE(M3G_INTERFACE(cam), M3G_PROFILE_VFC_TEST); |
|
1024 } |
|
1025 #endif /*M3G_ENABLE_VF_CULLING*/ |
|
1026 |
|
1027 /*---------------------------------------------------------------------- |
|
1028 * Public API functions |
|
1029 *--------------------------------------------------------------------*/ |
|
1030 |
|
1031 /*! |
|
1032 * \brief Gets transform from node to another. |
|
1033 * |
|
1034 * \param handle Node object |
|
1035 * \param hTarget target Node object |
|
1036 * \param transform transform to fill in |
|
1037 * \retval M3G_TRUE success |
|
1038 * \retval M3G_FALSE failed |
|
1039 */ |
|
1040 M3G_API M3Gbool m3gGetTransformTo(M3GNode handle, |
|
1041 M3GNode hTarget, |
|
1042 M3GMatrix *transform) |
|
1043 { |
|
1044 const Node *node = (Node *) handle; |
|
1045 const Node *target = (Node *) hTarget; |
|
1046 TCache *tc; |
|
1047 |
|
1048 M3G_VALIDATE_OBJECT(node); |
|
1049 M3G_BEGIN_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO); |
|
1050 |
|
1051 /* Look for quick exits first */ |
|
1052 |
|
1053 tc = m3gGetTransformCache(M3G_INTERFACE(node)); |
|
1054 |
|
1055 if (node == target) { |
|
1056 m3gIdentityMatrix(transform); |
|
1057 M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO); |
|
1058 return M3G_TRUE; |
|
1059 } |
|
1060 else if (m3gGetCachedPath(tc, node, target, transform)) { |
|
1061 M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO); |
|
1062 return M3G_TRUE; |
|
1063 } |
|
1064 else { |
|
1065 |
|
1066 /* No luck, must recompute the whole thing -- begin by finding |
|
1067 * a common ancestor node for the pivot point of the path */ |
|
1068 |
|
1069 const Node *pivot = NULL; |
|
1070 { |
|
1071 const Node *s = node; |
|
1072 const Node *t = target; |
|
1073 |
|
1074 /* First traverse to the same depth */ |
|
1075 { |
|
1076 int sd = m3gGetDepth(s); |
|
1077 int td = m3gGetDepth(t); |
|
1078 |
|
1079 while (sd > td) { |
|
1080 s = s->parent; |
|
1081 --sd; |
|
1082 } |
|
1083 while (td > sd) { |
|
1084 t = t->parent; |
|
1085 --td; |
|
1086 } |
|
1087 } |
|
1088 |
|
1089 /* Then traverse until we reach a common node or run out of |
|
1090 * ancestors in both branches, meaning there is no path |
|
1091 * between the nodes */ |
|
1092 |
|
1093 while (s != t) { |
|
1094 s = s->parent; |
|
1095 t = t->parent; |
|
1096 } |
|
1097 pivot = s; |
|
1098 } |
|
1099 if (!pivot) { |
|
1100 M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO); |
|
1101 return M3G_FALSE; |
|
1102 } |
|
1103 |
|
1104 /* Now, fetch the transformations for both branches and |
|
1105 * combine into the complete transformation; optimize by |
|
1106 * skipping most of that altogether for paths where the target |
|
1107 * node is the topmost node of the path */ |
|
1108 |
|
1109 if (pivot != target) { |
|
1110 Matrix targetPath; |
|
1111 Matrix sourcePath; |
|
1112 |
|
1113 /* Look for a cached version of the to-target path to |
|
1114 * avoid the inversion if possible */ |
|
1115 |
|
1116 if (!m3gGetCachedPath(tc, pivot, target, &targetPath)) { |
|
1117 m3gGetTransformUpPath(target, pivot, &targetPath); |
|
1118 |
|
1119 /* Invert the target-side path since we want the |
|
1120 * downstream transformation for that one */ |
|
1121 |
|
1122 M3G_BEGIN_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_INVERT); |
|
1123 if (!m3gInvertMatrix(&targetPath)) { |
|
1124 M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO); |
|
1125 m3gRaiseError(M3G_INTERFACE(node), M3G_ARITHMETIC_ERROR); |
|
1126 return M3G_FALSE; |
|
1127 } |
|
1128 |
|
1129 /* Cache the inverse for future use */ |
|
1130 m3gCachePath(tc, pivot, target, &targetPath); |
|
1131 } |
|
1132 |
|
1133 M3G_ASSERT(m3gIsWUnity(&targetPath)); |
|
1134 |
|
1135 /* Paste in the from-source path to get the complete |
|
1136 * transformation for the path */ |
|
1137 |
|
1138 if (pivot != node) { |
|
1139 m3gGetTransformUpPath(node, pivot, &sourcePath); |
|
1140 |
|
1141 M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_INVERT); |
|
1142 m3gRightMulMatrix(&targetPath, &sourcePath); |
|
1143 m3gCopyMatrix(transform, &targetPath); |
|
1144 M3G_ASSERT(m3gIsWUnity(transform)); |
|
1145 |
|
1146 /* Cache the combined result for future use */ |
|
1147 m3gCachePath(tc, node, target, transform); |
|
1148 } |
|
1149 else { |
|
1150 *transform = targetPath; |
|
1151 } |
|
1152 } |
|
1153 else { |
|
1154 /* For many cases, we only need this upstream path */ |
|
1155 m3gGetTransformUpPath(node, pivot, transform); |
|
1156 } |
|
1157 |
|
1158 M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO); |
|
1159 return M3G_TRUE; |
|
1160 } |
|
1161 } |
|
1162 |
|
1163 /*! |
|
1164 * \brief Sets alignment targets. |
|
1165 * |
|
1166 * \param handle Node object |
|
1167 * \param hZReference Z target Node object |
|
1168 * \param zTarget Z target type |
|
1169 * \param hYReference Y target Node object |
|
1170 * \param yTarget Y target type |
|
1171 */ |
|
1172 M3G_API void m3gSetAlignment(M3GNode handle, |
|
1173 M3GNode hZReference, M3Gint zTarget, |
|
1174 M3GNode hYReference, M3Gint yTarget) |
|
1175 { |
|
1176 Node *node = (Node *) handle; |
|
1177 Node *zReference = (Node *) hZReference; |
|
1178 Node *yReference = (Node *) hYReference; |
|
1179 M3G_VALIDATE_OBJECT(node); |
|
1180 |
|
1181 /* Check for errors */ |
|
1182 if (!m3gInRange(zTarget, M3G_NONE, M3G_Z_AXIS) || |
|
1183 !m3gInRange(yTarget, M3G_NONE, M3G_Z_AXIS)) { |
|
1184 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE); |
|
1185 return; |
|
1186 } |
|
1187 |
|
1188 if (zReference == node || yReference == node) { |
|
1189 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE); |
|
1190 return; |
|
1191 } |
|
1192 |
|
1193 if (zReference == yReference && zTarget == yTarget && zTarget != M3G_NONE) { |
|
1194 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE); |
|
1195 return; |
|
1196 } |
|
1197 |
|
1198 node->zReference = (zTarget != M3G_NONE) ? zReference : NULL; |
|
1199 node->yReference = (yTarget != M3G_NONE) ? yReference : NULL; |
|
1200 node->zTarget = internalTarget(zTarget); |
|
1201 node->yTarget = internalTarget(yTarget); |
|
1202 } |
|
1203 |
|
1204 /*! |
|
1205 * \brief Aligns a node. |
|
1206 * |
|
1207 * \param hNode Node object |
|
1208 * \param hRef reference Node object |
|
1209 */ |
|
1210 M3G_API void m3gAlignNode(M3GNode hNode, M3GNode hRef) |
|
1211 { |
|
1212 Node *node = (Node *)hNode; |
|
1213 const Node *ref = (const Node *)hRef; |
|
1214 M3G_VALIDATE_OBJECT(node); |
|
1215 |
|
1216 if (ref != NULL && (m3gGetRoot(node) != m3gGetRoot(ref))) { |
|
1217 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE); |
|
1218 } |
|
1219 else { |
|
1220 M3G_VFUNC(Node, node, align)(node, !ref ? node : ref); |
|
1221 } |
|
1222 } |
|
1223 |
|
1224 /*! |
|
1225 * \brief Sets node alpha factor. |
|
1226 * |
|
1227 * \param handle Node object |
|
1228 * \param alphaFactor node alpha factor |
|
1229 */ |
|
1230 M3G_API void m3gSetAlphaFactor(M3GNode handle, M3Gfloat alphaFactor) |
|
1231 { |
|
1232 Node *node = (Node *) handle; |
|
1233 M3G_VALIDATE_OBJECT(node); |
|
1234 |
|
1235 if (alphaFactor >= 0.f && alphaFactor <= 1.0f) { |
|
1236 node->alphaFactor = (M3Guint) |
|
1237 m3gRoundToInt(m3gMul(alphaFactor, |
|
1238 (1 << NODE_ALPHA_FACTOR_BITS) - 1)); |
|
1239 } |
|
1240 else { |
|
1241 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE); |
|
1242 } |
|
1243 } |
|
1244 |
|
1245 |
|
1246 /*! |
|
1247 * \brief Gets node alpha factor. |
|
1248 * |
|
1249 * \param handle Node object |
|
1250 * \return node alpha factor |
|
1251 */ |
|
1252 M3G_API M3Gfloat m3gGetAlphaFactor(M3GNode handle) |
|
1253 { |
|
1254 Node *node = (Node *) handle; |
|
1255 M3G_VALIDATE_OBJECT(node); |
|
1256 |
|
1257 return m3gMul((M3Gfloat) node->alphaFactor, |
|
1258 1.f / ((1 << NODE_ALPHA_FACTOR_BITS) - 1)); |
|
1259 } |
|
1260 |
|
1261 /*! |
|
1262 * \brief Sets node redering or picking enable flag. |
|
1263 * |
|
1264 * \param handle Node object |
|
1265 * \param which which flag to enable |
|
1266 * \arg M3G_SETGET_RENDERING |
|
1267 * \arg M3G_SETGET_PICKING |
|
1268 * \param enable enable flag |
|
1269 */ |
|
1270 M3G_API void m3gEnable(M3GNode handle, M3Gint which, M3Gbool enable) |
|
1271 { |
|
1272 Node *node = (Node *) handle; |
|
1273 M3G_VALIDATE_OBJECT(node); |
|
1274 |
|
1275 switch (which) { |
|
1276 case M3G_SETGET_RENDERING: |
|
1277 node->enableBits &= ~NODE_RENDER_BIT; |
|
1278 if (enable) { |
|
1279 node->enableBits |= NODE_RENDER_BIT; |
|
1280 } |
|
1281 break; |
|
1282 case M3G_SETGET_PICKING: |
|
1283 default: |
|
1284 node->enableBits &= ~NODE_PICK_BIT; |
|
1285 if (enable) { |
|
1286 node->enableBits |= NODE_PICK_BIT; |
|
1287 } |
|
1288 break; |
|
1289 } |
|
1290 } |
|
1291 |
|
1292 /*! |
|
1293 * \brief Gets node redering or picking enable flag. |
|
1294 * |
|
1295 * \param handle Node object |
|
1296 * \param which which flag to return |
|
1297 * \arg M3G_SETGET_RENDERING |
|
1298 * \arg M3G_SETGET_PICKING |
|
1299 * \return enable flag |
|
1300 */ |
|
1301 M3G_API M3Gint m3gIsEnabled(M3GNode handle, M3Gint which) |
|
1302 { |
|
1303 Node *node = (Node *) handle; |
|
1304 M3G_VALIDATE_OBJECT(node); |
|
1305 |
|
1306 switch(which) { |
|
1307 case M3G_SETGET_RENDERING: |
|
1308 return (node->enableBits & NODE_RENDER_BIT) != 0; |
|
1309 case M3G_SETGET_PICKING: |
|
1310 default: |
|
1311 return (node->enableBits & NODE_PICK_BIT) != 0; |
|
1312 } |
|
1313 } |
|
1314 |
|
1315 /*! |
|
1316 * \brief Sets node scope. |
|
1317 * |
|
1318 * \param handle Node object |
|
1319 * \param id node scope id |
|
1320 */ |
|
1321 M3G_API void m3gSetScope(M3GNode handle, M3Gint id) |
|
1322 { |
|
1323 Node *node = (Node *) handle; |
|
1324 M3G_VALIDATE_OBJECT(node); |
|
1325 |
|
1326 node->scope = id; |
|
1327 } |
|
1328 |
|
1329 /*! |
|
1330 * \brief Gets node scope. |
|
1331 * |
|
1332 * \param handle Node object |
|
1333 * \return node scope |
|
1334 */ |
|
1335 M3G_API M3Gint m3gGetScope(M3GNode handle) |
|
1336 { |
|
1337 Node *node = (Node *) handle; |
|
1338 M3G_VALIDATE_OBJECT(node); |
|
1339 |
|
1340 return node->scope; |
|
1341 } |
|
1342 |
|
1343 /*! |
|
1344 * \brief Gets node parent. |
|
1345 * |
|
1346 * \param handle Node object |
|
1347 * \return parent Node object |
|
1348 */ |
|
1349 M3G_API M3GNode m3gGetParent(M3GNode handle) |
|
1350 { |
|
1351 Node *node = (Node *) handle; |
|
1352 M3G_VALIDATE_OBJECT(node); |
|
1353 |
|
1354 return node->parent; |
|
1355 } |
|
1356 |
|
1357 /*! |
|
1358 * \brief Gets node alignment Z reference. |
|
1359 * |
|
1360 * \param handle Node object |
|
1361 * \return Z reference Node object |
|
1362 */ |
|
1363 M3G_API M3GNode m3gGetZRef(M3GNode handle) |
|
1364 { |
|
1365 Node *node = (Node *) handle; |
|
1366 M3G_VALIDATE_OBJECT(node); |
|
1367 |
|
1368 return node->zReference; |
|
1369 } |
|
1370 |
|
1371 /*! |
|
1372 * \brief Gets node alignment Y reference. |
|
1373 * |
|
1374 * \param handle Node object |
|
1375 * \return Y reference Node object |
|
1376 */ |
|
1377 M3G_API M3GNode m3gGetYRef(M3GNode handle) |
|
1378 { |
|
1379 Node *node = (Node *) handle; |
|
1380 M3G_VALIDATE_OBJECT(node); |
|
1381 |
|
1382 return node->yReference; |
|
1383 } |
|
1384 |
|
1385 /*! |
|
1386 * \brief Gets node alignment target |
|
1387 * |
|
1388 * \param handle Node object |
|
1389 * \param axis axis |
|
1390 * \return alignment target |
|
1391 */ |
|
1392 M3G_API M3Gint m3gGetAlignmentTarget(M3GNode handle, M3Gint axis) |
|
1393 { |
|
1394 Node *node = (Node *) handle; |
|
1395 M3G_VALIDATE_OBJECT(node); |
|
1396 |
|
1397 switch (axis) { |
|
1398 case M3G_Y_AXIS: return externalTarget(node->yTarget); |
|
1399 case M3G_Z_AXIS: return externalTarget(node->zTarget); |
|
1400 default: |
|
1401 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE); |
|
1402 return 0; |
|
1403 } |
|
1404 } |
|
1405 |
|
1406 /*! |
|
1407 * \brief Gets node subtree size. |
|
1408 * |
|
1409 * \param handle Node object |
|
1410 * \return subtree size |
|
1411 */ |
|
1412 M3G_API M3Gint m3gGetSubtreeSize(M3GNode handle) |
|
1413 { |
|
1414 M3Gint numRef = 0; |
|
1415 Node *node = (Node *) handle; |
|
1416 M3G_VALIDATE_OBJECT(node); |
|
1417 |
|
1418 m3gForSubtree(node, m3gDoGetSubtreeSize, (void *)&numRef); |
|
1419 return numRef; |
|
1420 } |
|
1421 |
|
1422 #undef TARGET_ORIGIN |
|
1423 #undef TARGET_Z_AXIS |
|
1424 #undef TARGET_Y_AXIS |
|
1425 #undef TARGET_X_AXIS |
|
1426 #undef TARGET_NONE |
|
1427 |