Update contrib.
2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
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".
9 * Initial Contributors:
10 * Nokia Corporation - initial contribution.
14 * Description: Node implementation
22 * \brief Node implementation
25 #ifndef M3G_CORE_INCLUDE
26 # error included by m3g_core.c; do not compile separately.
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"
37 #define TARGET_X_AXIS 1
38 #define TARGET_Y_AXIS 2
39 #define TARGET_Z_AXIS 3
40 #define TARGET_ORIGIN 4
42 /*----------------------------------------------------------------------
44 *--------------------------------------------------------------------*/
46 static M3Guint internalTarget(M3Genum target)
60 M3G_ASSERT(M3G_FALSE);
65 static M3Guint externalTarget(M3Genum target)
79 M3G_ASSERT(M3G_FALSE);
84 /*----------------------------------------------------------------------
85 * Constructor & destructor
86 *--------------------------------------------------------------------*/
90 * \brief Initializes a Node object. See specification
93 * \param m3g M3G interface
94 * \param node Node object
95 * \param vfTable virtual function table
97 static void m3gInitNode(Interface *m3g, Node *node, M3GClass classID)
99 /* Node is derived from Transformable */
100 m3gInitTransformable(&node->transformable, m3g, classID);
102 /* Set default values */
104 node->enableBits = (NODE_RENDER_BIT|NODE_PICK_BIT);
105 node->alphaFactor = (1u << NODE_ALPHA_FACTOR_BITS) - 1;
107 node->zTarget = TARGET_NONE;
108 node->yTarget = TARGET_NONE;
113 * \brief Destroys this Node object.
115 * \param obj Node object
117 static void m3gDestroyNode(Object *obj)
119 Node *node = (Node *) obj;
120 M3G_VALIDATE_OBJECT(node);
121 M3G_ASSERT(node->parent == NULL);
122 m3gDestroyTransformable((Object *) node);
125 /*----------------------------------------------------------------------
127 *--------------------------------------------------------------------*/
131 * \brief Checks if node is a child of the parent.
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
138 static M3Gbool m3gIsChildOf(const Node *parent, const Node *child)
142 for (n = child; n != NULL; n = n->parent) {
143 if (n->parent == parent) return M3G_TRUE;
151 * \brief Executes the given function for each node in a subtree
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.
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
163 * \return The return value of the top-level call to \c func
165 static void m3gForSubtree(Node *node, NodeFuncPtr func, void *params)
168 M3G_VALIDATE_OBJECT(node);
170 /* Recurse into the children first */
172 nodeClass = M3G_CLASS(node);
174 if (nodeClass == M3G_CLASS_SKINNED_MESH) {
175 m3gForSubtree((Node*)((SkinnedMesh*)node)->skeleton, func, params);
177 else if (nodeClass == M3G_CLASS_GROUP ||
178 nodeClass == M3G_CLASS_WORLD) {
179 Group *group = (Group*) node;
180 Node *child = group->firstChild;
183 Node *next = child->right;
184 m3gForSubtree(child, func, params);
186 } while (child != group->firstChild);
190 /* Execute function on self */
192 (*func)(node, params);
197 * \brief Overloaded Object3D method
199 * \param property animation property
200 * \retval M3G_TRUE property supported
201 * \retval M3G_FALSE property not supported
203 static M3Gbool m3gNodeIsCompatible(M3Gint property)
207 case M3G_ANIM_PICKABILITY:
208 case M3G_ANIM_VISIBILITY:
211 return m3gTransformableIsCompatible(property);
217 * \brief Overloaded Object3D method
219 * \param self Node object
220 * \param property animation property
221 * \param valueSize size of value array
222 * \param value value array
224 static void m3gNodeUpdateProperty(Object *self,
227 const M3Gfloat *value)
229 Node *node = (Node *)self;
230 M3G_VALIDATE_OBJECT(node);
231 M3G_ASSERT_PTR(value);
235 M3G_ASSERT(valueSize >= 1);
238 m3gMul(m3gClampFloat(value[0], 0.f, 1.f),
239 (float)((1 << NODE_ALPHA_FACTOR_BITS) - 1)));
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;
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;
256 m3gTransformableUpdateProperty(self, property, valueSize, value);
262 * \brief Overloaded Object3D method
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
269 static M3Gbool m3gNodeDuplicate(const Object *originalObj,
274 Node *original = (Node *)originalObj;
275 Node *clone = (Node *)*cloneObj;
276 M3G_ASSERT_PTR(*cloneObj); /* abstract class, must be derived */
278 /* Duplicate base class data */
280 if (!m3gTransformableDuplicate(originalObj, cloneObj, pairs, numPairs)) {
284 /* Duplicate our own data */
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;
301 * \brief Find corresponding duplicate for a Node
303 * \param node Node object
304 * \param pairs array for all object-duplicate pairs
305 * \param numPairs number of pairs
307 static Node *m3gGetDuplicatedInstance(Node *node, Object **pairs, M3Gint numPairs)
310 for (i = 0; i < numPairs; i++)
311 if (pairs[i * 2] == (Object *)node)
312 return (Node *)pairs[i * 2 + 1];
318 * \brief Updates references of the duplicate object.
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.
325 * \param self Node object
326 * \param pairs array for all object-duplicate pairs
327 * \param numPairs number of pairs
329 static void m3gNodeUpdateDuplicateReferences(Node *self, Object **pairs, M3Gint numPairs)
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;
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;
351 * \brief Gets size of the subtree
353 * \param node Node object
354 * \param numRef number of references
356 static void m3gDoGetSubtreeSize(Node *node, void *numRef)
358 M3Gint *num = (M3Gint *)numRef;
365 * \brief Default function for non-pickable objects
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
374 static M3Gbool m3gNodeRayIntersect(Node *self,
391 * \brief Computes the bounding box for this node
393 * \param self node pointer
394 * \param bbox bounding box structure filled in for non-zero return values
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
401 static M3Gint m3gNodeGetBBox(Node *self, AABB *bbox)
410 * \brief Updates the bounding box for this node
412 static M3Gbool m3gNodeValidate(Node *self, M3Gbitmask stateBits, M3Gint scope)
414 M3G_UNREF(stateBits);
417 /* Invalidate parent state in case we've encountered a previously
418 * disabled node, then reset the dirty bits */
420 if (self->dirtyBits && self->parent) {
421 m3gInvalidateNode(self->parent, self->dirtyBits);
429 * \brief Gets a vector according to alignment target
430 * and transforms it with a given transform.
432 * \param target alignment target
433 * \param transform transform to be applied
434 * \param out vector to fill in
436 static void m3gTransformAlignmentTarget(M3Genum target,
437 const Matrix *transform,
454 M3G_ASSERT(M3G_FALSE);
457 m3gTransformVec4(transform, out);
462 * \brief Computes a single alignment rotation for a node.
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
470 static M3Gbool m3gComputeAlignmentRotation(Node *node,
472 const Node *targetNode,
473 M3Genum targetAxisName,
476 const Node *parent = node->parent;
480 M3G_VALIDATE_OBJECT(parent);
481 M3G_ASSERT(constraint == TARGET_NONE || constraint == TARGET_Z_AXIS);
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. */
487 const Transformable *tf = &node->transformable;
489 if (!m3gGetTransformTo((M3GNode) targetNode, (M3GNode) parent,
493 m3gPreTranslateMatrix(&transform, -tf->tx, -tf->ty, -tf->tz);
495 if (constraint != TARGET_NONE) {
496 Quat rot = tf->orientation;
498 m3gPreRotateMatrixQuat(&transform, &rot);
502 m3gTransformAlignmentTarget(targetAxisName, &transform, &targetAxis);
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. */
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) {
515 norm = m3gRcpSqrt(norm);
517 targetAxis.x = m3gMul(targetAxis.x, norm);
518 targetAxis.y = m3gMul(targetAxis.y, norm);
521 M3G_ASSERT(srcAxis->z == 0.0f);
524 m3gNormalizeVec3((Vec3*)&targetAxis); /* srcAxis will be unit length */
527 if (constraint != TARGET_NONE) {
529 m3gSetQuatRotation(&rot, srcAxis, (const Vec3*) &targetAxis);
530 m3gMulQuat(&node->transformable.orientation, &rot);
533 m3gSetQuatRotation(&node->transformable.orientation,
534 srcAxis, (const Vec3*) &targetAxis);
537 /* Invalidate transformations and bounding boxes after setting
538 * node orientation */
540 m3gInvalidateTransformable((Transformable*)node);
547 * \brief Computes alignment for a single node.
549 * \param node Node object
550 * \param refNode Node object
551 * \retval M3G_TRUE alignment ok
552 * \retval M3G_FALSE alignment failed
554 static M3Gbool m3gComputeAlignment(Node *node, const Node *refNode)
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);
563 /* Quick exit if nothing to do */
565 if (zTarget == TARGET_NONE && yTarget == TARGET_NONE) {
569 /* Check scene graph state */
571 if (zRef != NULL && (m3gIsChildOf(node, zRef) || m3gGetRoot(zRef) != root)) {
572 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_OPERATION);
575 if (yRef != NULL && (m3gIsChildOf(node, yRef) || m3gGetRoot(yRef) != root)) {
576 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_OPERATION);
580 /* Compute the alignment rotations for Z and Y */
582 if (node->zTarget != TARGET_NONE) {
583 if (zRef == NULL && refNode == node) {
584 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_OPERATION);
587 if (!m3gComputeAlignmentRotation(
589 (const Vec3*) &Vec4_Z_AXIS,
590 zRef != NULL ? zRef : refNode,
596 if (node->yTarget != TARGET_NONE) {
597 if (yRef == NULL && refNode == node) {
598 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_OPERATION);
601 if (!m3gComputeAlignmentRotation(
603 (const Vec3*) &Vec4_Y_AXIS,
604 yRef != NULL ? yRef : refNode,
606 zTarget != TARGET_NONE ? TARGET_Z_AXIS : TARGET_NONE)) {
616 * \brief Gets the transformation to an ancestor node
618 static void m3gGetTransformUpPath(const Node *node, const Node *ancestor, Matrix *transform)
622 if (node == ancestor) {
623 m3gIdentityMatrix(transform);
627 M3G_ASSERT(!ancestor || m3gIsChildOf(ancestor, node));
629 /* Look for a cached path */
631 tc = m3gGetTransformCache(M3G_INTERFACE(node));
632 if (m3gGetCachedPath(tc, node, ancestor, transform)) {
636 /* No dice -- do a recursive search and cache the result */
638 if (node->parent == ancestor) {
639 m3gGetCompositeNodeTransform(node, transform);
642 m3gGetTransformUpPath(node->parent, ancestor, transform);
645 m3gGetCompositeNodeTransform(node, &mtx);
646 m3gMulMatrix(transform, &mtx);
649 m3gCachePath(tc, node, ancestor, transform);
655 * \brief Gets depth of a node in the scenegraph.
657 * \param node Node object
658 * \return Depth of the node
660 static M3Gint m3gGetDepth(const Node *node)
662 const Node *n = node;
665 while (n->parent != NULL) {
675 * \brief Gets root of a node in the scenegraph.
677 * \param node Node object
678 * \return root Node object
680 static Node *m3gGetRoot(const Node *node)
682 const Node *n = node;
684 while (n->parent != NULL) {
693 * \brief Gets total alpha factor.
695 * \param node Node object
696 * \param root root Node object
698 static M3Guint m3gGetTotalAlphaFactor(Node *node, const Node *root)
700 const Node *n = node;
701 M3Guint f = node->alphaFactor;
703 while (n->parent != NULL && n != root) {
705 f = ((f + 1) * n->alphaFactor) >> NODE_ALPHA_FACTOR_BITS;
712 * \brief Checks if node is enabled for rendering from the root.
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
719 static M3Gbool m3gHasEnabledPath(const Node *node, const Node *root)
723 for (n = node; n != NULL; n = n->parent) {
724 if (!(n->enableBits & NODE_RENDER_BIT)) {
737 * \brief Checks if node is pickable from the root.
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
744 static M3Gbool m3gHasPickablePath(const Node *node, const Node *root)
748 for (n = node; n != NULL; n = n->parent) {
749 if (!(n->enableBits & NODE_PICK_BIT)) {
760 #if defined(M3G_ENABLE_VF_CULLING)
762 * \brief Invalidates the bounding box hierarchy from a node upwards
764 static void m3gInvalidateNode(Node *node, M3Gbitmask flags)
766 Interface *m3g = M3G_INTERFACE(node);
767 M3G_BEGIN_PROFILE(m3g, M3G_PROFILE_VFC_UPDATE);
769 while (node && (node->dirtyBits & flags) != flags) {
770 node->dirtyBits |= flags;
773 M3G_END_PROFILE(m3g, M3G_PROFILE_VFC_UPDATE);
775 #endif /*M3G_ENABLE_VF_CULLING*/
779 * \brief Aligns a node.
781 * \param node Node object
782 * \param ref reference Node object
784 * \retval M3G_TRUE continue align
785 * \retval M3G_FALSE abort align
787 static M3Gbool m3gNodeAlign(Node *node, const Node *ref)
790 return m3gComputeAlignment(node, node);
793 M3G_VALIDATE_OBJECT(ref);
794 return m3gComputeAlignment(node, ref);
800 * \brief Updates node counters when moving nodes around
802 static void m3gUpdateNodeCounters(Node *node,
803 M3Gint nonCullableChange,
804 M3Gint renderableChange)
806 Interface *m3g = M3G_INTERFACE(node);
807 M3Gbool hasRenderables = (renderableChange > 0);
808 M3G_BEGIN_PROFILE(m3g, M3G_PROFILE_VFC_UPDATE);
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);
817 node->hasRenderables = hasRenderables;
820 M3G_END_PROFILE(m3g, M3G_PROFILE_VFC_UPDATE);
825 * \brief Sets the parent link of this node to a new value
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.
831 * \param node Node object
832 * \param parent parent Node object
834 static void m3gSetParent(Node *node, Node *parent)
837 M3Gint nonCullableChange = 0, renderableChange = 0;
838 M3G_VALIDATE_OBJECT(node);
840 /* Determine the number of various kinds of nodes being moved around */
842 M3G_BEGIN_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_VFC_UPDATE);
843 nodeClass = M3G_CLASS(node);
845 case M3G_CLASS_GROUP:
847 const Group *g = (const Group *) node;
848 nonCullableChange = g->numNonCullables;
849 renderableChange = g->numRenderables;
852 case M3G_CLASS_SPRITE:
853 renderableChange = 1;
854 if (m3gIsScaledSprite((M3GSprite) node)) {
857 /* conditional fall-through! */
858 case M3G_CLASS_LIGHT:
859 nonCullableChange = 1;
861 case M3G_CLASS_SKINNED_MESH:
863 const SkinnedMesh *mesh = (const SkinnedMesh *) node;
864 nonCullableChange += mesh->skeleton->numNonCullables;
865 renderableChange += mesh->skeleton->numRenderables + 1;
869 case M3G_CLASS_MORPHING_MESH:
870 renderableChange = 1;
875 M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_VFC_UPDATE);
877 /* Invalidate any cached transformation paths through this node
878 * *before* we move the node */
880 m3gInvalidateCachedPaths(m3gGetTransformCache(M3G_INTERFACE(node)), node);
882 /* Update bookkeeping for the old parent tree */
885 m3gUpdateNodeCounters(node->parent,
886 -nonCullableChange, -renderableChange);
887 if (renderableChange) {
888 m3gInvalidateNode(node->parent, NODE_BBOX_BIT|NODE_TRANSFORMS_BIT);
892 /* Change the parent link */
894 if (node->parent == NULL && parent != NULL) {
895 node->parent = parent;
896 m3gAddRef((Object *) node);
898 else if (node->parent != NULL && parent == NULL) {
899 node->parent = parent;
900 m3gDeleteRef((Object *) node);
903 /* Update bookkeeping for the new parent tree */
906 M3Gbitmask dirtyBits = node->dirtyBits;
907 if (renderableChange) {
908 dirtyBits |= NODE_BBOX_BIT;
910 if (node->hasBones) {
911 dirtyBits |= NODE_TRANSFORMS_BIT;
913 m3gUpdateNodeCounters(parent, nonCullableChange, renderableChange);
914 m3gInvalidateNode(parent, dirtyBits);
919 * \brief Computes the "near" and "far" box vertices
922 static M3G_INLINE void m3gGetTestPoints(const Vec3 *planeNormal,
924 Vec3 *vNear, Vec3 *vFar)
926 const M3Gfloat *fNormal = (const M3Gfloat*) planeNormal;
927 M3Gfloat *fNear = (M3Gfloat*) vNear;
928 M3Gfloat *fFar = (M3Gfloat*) vFar;
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];
938 *fNear++ = box->min[i];
939 *fFar++ = box->max[i];
944 #if defined(M3G_ENABLE_VF_CULLING)
947 * \brief Update the frustum culling mask for one level of an AABB hierarchy
949 * \param s the current traversal state
950 * \param bbox the bounding box to check against
952 static void m3gUpdateCullingMask(SetupRenderState *s,
953 const Camera *cam, const AABB *bbox)
955 M3Gbitmask cullMask = s->cullMask;
956 M3G_BEGIN_PROFILE(M3G_INTERFACE(cam), M3G_PROFILE_VFC_TEST);
958 /* First, check whether any planes are previously marked as
961 if (cullMask & CULLMASK_ALL) {
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
968 M3Gbitmask planeMask;
969 const Vec4 *camPlanes = m3gFrustumPlanes(cam);
972 m3gMatrixTranspose(&t, &s->toCamera);
974 /* Loop over the active frustum planes, testing the ones we've
975 * previously intersected with */
977 planeMask = CULLMASK_INTERSECTS;
978 while (planeMask <= cullMask) {
979 if (cullMask & planeMask) {
981 /* Transform the respective frustum plane into the node
982 * local space our AABB is in */
985 plane = *camPlanes++;
986 m3gTransformVec4(&t, &plane);
988 /* Test the AABB against the plane and update the mask
989 * based on the result */
991 m3gIncStat(M3G_INTERFACE(cam), M3G_STAT_CULLING_TESTS, 1);
993 /* Get the "near" and "far" corner points of the box */
995 const Vec3* normal = (Vec3*) &plane;
997 m3gGetTestPoints(normal, bbox, &vNear, &vFar);
999 /* Our normals point inside, so flip this */
1001 plane.w = m3gNegate(plane.w);
1003 /* "Far" point behind plane? */
1005 if (m3gDot3(normal, &vFar) < plane.w) {
1006 /* All outside, no need to test further! */
1011 /* "Near" point in front of plane? */
1013 if (m3gDot3(normal, &vNear) > plane.w) {
1014 cullMask &= ~planeMask;
1015 cullMask |= planeMask >> 1; /* intersects->inside */
1019 planeMask <<= 2; /* next plane */
1021 s->cullMask = cullMask; /* write the output mask */
1023 M3G_END_PROFILE(M3G_INTERFACE(cam), M3G_PROFILE_VFC_TEST);
1025 #endif /*M3G_ENABLE_VF_CULLING*/
1027 /*----------------------------------------------------------------------
1028 * Public API functions
1029 *--------------------------------------------------------------------*/
1032 * \brief Gets transform from node to another.
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
1040 M3G_API M3Gbool m3gGetTransformTo(M3GNode handle,
1042 M3GMatrix *transform)
1044 const Node *node = (Node *) handle;
1045 const Node *target = (Node *) hTarget;
1048 M3G_VALIDATE_OBJECT(node);
1049 M3G_BEGIN_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO);
1051 /* Look for quick exits first */
1053 tc = m3gGetTransformCache(M3G_INTERFACE(node));
1055 if (node == target) {
1056 m3gIdentityMatrix(transform);
1057 M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO);
1060 else if (m3gGetCachedPath(tc, node, target, transform)) {
1061 M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO);
1066 /* No luck, must recompute the whole thing -- begin by finding
1067 * a common ancestor node for the pivot point of the path */
1069 const Node *pivot = NULL;
1071 const Node *s = node;
1072 const Node *t = target;
1074 /* First traverse to the same depth */
1076 int sd = m3gGetDepth(s);
1077 int td = m3gGetDepth(t);
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 */
1100 M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO);
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 */
1109 if (pivot != target) {
1113 /* Look for a cached version of the to-target path to
1114 * avoid the inversion if possible */
1116 if (!m3gGetCachedPath(tc, pivot, target, &targetPath)) {
1117 m3gGetTransformUpPath(target, pivot, &targetPath);
1119 /* Invert the target-side path since we want the
1120 * downstream transformation for that one */
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);
1129 /* Cache the inverse for future use */
1130 m3gCachePath(tc, pivot, target, &targetPath);
1133 M3G_ASSERT(m3gIsWUnity(&targetPath));
1135 /* Paste in the from-source path to get the complete
1136 * transformation for the path */
1138 if (pivot != node) {
1139 m3gGetTransformUpPath(node, pivot, &sourcePath);
1141 M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_INVERT);
1142 m3gRightMulMatrix(&targetPath, &sourcePath);
1143 m3gCopyMatrix(transform, &targetPath);
1144 M3G_ASSERT(m3gIsWUnity(transform));
1146 /* Cache the combined result for future use */
1147 m3gCachePath(tc, node, target, transform);
1150 *transform = targetPath;
1154 /* For many cases, we only need this upstream path */
1155 m3gGetTransformUpPath(node, pivot, transform);
1158 M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO);
1164 * \brief Sets alignment targets.
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
1172 M3G_API void m3gSetAlignment(M3GNode handle,
1173 M3GNode hZReference, M3Gint zTarget,
1174 M3GNode hYReference, M3Gint yTarget)
1176 Node *node = (Node *) handle;
1177 Node *zReference = (Node *) hZReference;
1178 Node *yReference = (Node *) hYReference;
1179 M3G_VALIDATE_OBJECT(node);
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);
1188 if (zReference == node || yReference == node) {
1189 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE);
1193 if (zReference == yReference && zTarget == yTarget && zTarget != M3G_NONE) {
1194 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE);
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);
1205 * \brief Aligns a node.
1207 * \param hNode Node object
1208 * \param hRef reference Node object
1210 M3G_API void m3gAlignNode(M3GNode hNode, M3GNode hRef)
1212 Node *node = (Node *)hNode;
1213 const Node *ref = (const Node *)hRef;
1214 M3G_VALIDATE_OBJECT(node);
1216 if (ref != NULL && (m3gGetRoot(node) != m3gGetRoot(ref))) {
1217 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE);
1220 M3G_VFUNC(Node, node, align)(node, !ref ? node : ref);
1225 * \brief Sets node alpha factor.
1227 * \param handle Node object
1228 * \param alphaFactor node alpha factor
1230 M3G_API void m3gSetAlphaFactor(M3GNode handle, M3Gfloat alphaFactor)
1232 Node *node = (Node *) handle;
1233 M3G_VALIDATE_OBJECT(node);
1235 if (alphaFactor >= 0.f && alphaFactor <= 1.0f) {
1236 node->alphaFactor = (M3Guint)
1237 m3gRoundToInt(m3gMul(alphaFactor,
1238 (1 << NODE_ALPHA_FACTOR_BITS) - 1));
1241 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE);
1247 * \brief Gets node alpha factor.
1249 * \param handle Node object
1250 * \return node alpha factor
1252 M3G_API M3Gfloat m3gGetAlphaFactor(M3GNode handle)
1254 Node *node = (Node *) handle;
1255 M3G_VALIDATE_OBJECT(node);
1257 return m3gMul((M3Gfloat) node->alphaFactor,
1258 1.f / ((1 << NODE_ALPHA_FACTOR_BITS) - 1));
1262 * \brief Sets node redering or picking enable flag.
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
1270 M3G_API void m3gEnable(M3GNode handle, M3Gint which, M3Gbool enable)
1272 Node *node = (Node *) handle;
1273 M3G_VALIDATE_OBJECT(node);
1276 case M3G_SETGET_RENDERING:
1277 node->enableBits &= ~NODE_RENDER_BIT;
1279 node->enableBits |= NODE_RENDER_BIT;
1282 case M3G_SETGET_PICKING:
1284 node->enableBits &= ~NODE_PICK_BIT;
1286 node->enableBits |= NODE_PICK_BIT;
1293 * \brief Gets node redering or picking enable flag.
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
1301 M3G_API M3Gint m3gIsEnabled(M3GNode handle, M3Gint which)
1303 Node *node = (Node *) handle;
1304 M3G_VALIDATE_OBJECT(node);
1307 case M3G_SETGET_RENDERING:
1308 return (node->enableBits & NODE_RENDER_BIT) != 0;
1309 case M3G_SETGET_PICKING:
1311 return (node->enableBits & NODE_PICK_BIT) != 0;
1316 * \brief Sets node scope.
1318 * \param handle Node object
1319 * \param id node scope id
1321 M3G_API void m3gSetScope(M3GNode handle, M3Gint id)
1323 Node *node = (Node *) handle;
1324 M3G_VALIDATE_OBJECT(node);
1330 * \brief Gets node scope.
1332 * \param handle Node object
1333 * \return node scope
1335 M3G_API M3Gint m3gGetScope(M3GNode handle)
1337 Node *node = (Node *) handle;
1338 M3G_VALIDATE_OBJECT(node);
1344 * \brief Gets node parent.
1346 * \param handle Node object
1347 * \return parent Node object
1349 M3G_API M3GNode m3gGetParent(M3GNode handle)
1351 Node *node = (Node *) handle;
1352 M3G_VALIDATE_OBJECT(node);
1354 return node->parent;
1358 * \brief Gets node alignment Z reference.
1360 * \param handle Node object
1361 * \return Z reference Node object
1363 M3G_API M3GNode m3gGetZRef(M3GNode handle)
1365 Node *node = (Node *) handle;
1366 M3G_VALIDATE_OBJECT(node);
1368 return node->zReference;
1372 * \brief Gets node alignment Y reference.
1374 * \param handle Node object
1375 * \return Y reference Node object
1377 M3G_API M3GNode m3gGetYRef(M3GNode handle)
1379 Node *node = (Node *) handle;
1380 M3G_VALIDATE_OBJECT(node);
1382 return node->yReference;
1386 * \brief Gets node alignment target
1388 * \param handle Node object
1390 * \return alignment target
1392 M3G_API M3Gint m3gGetAlignmentTarget(M3GNode handle, M3Gint axis)
1394 Node *node = (Node *) handle;
1395 M3G_VALIDATE_OBJECT(node);
1398 case M3G_Y_AXIS: return externalTarget(node->yTarget);
1399 case M3G_Z_AXIS: return externalTarget(node->zTarget);
1401 m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE);
1407 * \brief Gets node subtree size.
1409 * \param handle Node object
1410 * \return subtree size
1412 M3G_API M3Gint m3gGetSubtreeSize(M3GNode handle)
1415 Node *node = (Node *) handle;
1416 M3G_VALIDATE_OBJECT(node);
1418 m3gForSubtree(node, m3gDoGetSubtreeSize, (void *)&numRef);
1422 #undef TARGET_ORIGIN
1423 #undef TARGET_Z_AXIS
1424 #undef TARGET_Y_AXIS
1425 #undef TARGET_X_AXIS