m3g/m3gcore11/src/m3g_node.c
changeset 0 5d03bc08d59c
equal deleted inserted replaced
-1:000000000000 0:5d03bc08d59c
       
     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