os/graphics/m3g/m3gcore11/src/m3g_node.c
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/graphics/m3g/m3gcore11/src/m3g_node.c	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,1427 @@
     1.4 +/*
     1.5 +* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
     1.6 +* All rights reserved.
     1.7 +* This component and the accompanying materials are made available
     1.8 +* under the terms of the License "Eclipse Public License v1.0"
     1.9 +* which accompanies this distribution, and is available
    1.10 +* at the URL "http://www.eclipse.org/legal/epl-v10.html".
    1.11 +*
    1.12 +* Initial Contributors:
    1.13 +* Nokia Corporation - initial contribution.
    1.14 +*
    1.15 +* Contributors:
    1.16 +*
    1.17 +* Description: Node implementation
    1.18 +*
    1.19 +*/
    1.20 +
    1.21 +
    1.22 +/*!
    1.23 + * \internal
    1.24 + * \file
    1.25 + * \brief Node implementation
    1.26 + */
    1.27 +
    1.28 +#ifndef M3G_CORE_INCLUDE
    1.29 +#   error included by m3g_core.c; do not compile separately.
    1.30 +#endif
    1.31 +
    1.32 +#include "m3g_node.h"
    1.33 +#include "m3g_memory.h"
    1.34 +#include "m3g_animationtrack.h"
    1.35 +#include "m3g_skinnedmesh.h"
    1.36 +#include "m3g_tcache.h"
    1.37 +#include "m3g_transformable.h"
    1.38 +
    1.39 +#define TARGET_NONE   0
    1.40 +#define TARGET_X_AXIS 1
    1.41 +#define TARGET_Y_AXIS 2
    1.42 +#define TARGET_Z_AXIS 3
    1.43 +#define TARGET_ORIGIN 4
    1.44 +
    1.45 +/*----------------------------------------------------------------------
    1.46 + * Private functions
    1.47 + *--------------------------------------------------------------------*/
    1.48 +
    1.49 +static M3Guint internalTarget(M3Genum target)
    1.50 +{
    1.51 +    switch (target) {
    1.52 +    case M3G_NONE:
    1.53 +        return TARGET_NONE;
    1.54 +    case M3G_ORIGIN:
    1.55 +        return TARGET_ORIGIN;
    1.56 +    case M3G_X_AXIS:
    1.57 +        return TARGET_X_AXIS;
    1.58 +    case M3G_Y_AXIS:
    1.59 +        return TARGET_Y_AXIS;
    1.60 +    case M3G_Z_AXIS:
    1.61 +        return TARGET_Z_AXIS;
    1.62 +    default:
    1.63 +        M3G_ASSERT(M3G_FALSE);
    1.64 +        return TARGET_NONE;
    1.65 +    }
    1.66 +}   
    1.67 +
    1.68 +static M3Guint externalTarget(M3Genum target)
    1.69 +{
    1.70 +    switch (target) {
    1.71 +    case TARGET_NONE:
    1.72 +        return M3G_NONE;
    1.73 +    case TARGET_ORIGIN:
    1.74 +        return M3G_ORIGIN;
    1.75 +    case TARGET_X_AXIS:
    1.76 +        return M3G_X_AXIS;
    1.77 +    case TARGET_Y_AXIS:
    1.78 +        return M3G_Y_AXIS;
    1.79 +    case TARGET_Z_AXIS:
    1.80 +        return M3G_Z_AXIS;
    1.81 +    default:
    1.82 +        M3G_ASSERT(M3G_FALSE);
    1.83 +        return M3G_NONE;
    1.84 +    }
    1.85 +}   
    1.86 +
    1.87 +/*----------------------------------------------------------------------
    1.88 + * Constructor & destructor
    1.89 + *--------------------------------------------------------------------*/
    1.90 +
    1.91 +/*!
    1.92 + * \internal
    1.93 + * \brief Initializes a Node object. See specification
    1.94 + * for default values.
    1.95 + *
    1.96 + * \param m3g           M3G interface
    1.97 + * \param node          Node object
    1.98 + * \param vfTable       virtual function table
    1.99 + */
   1.100 +static void m3gInitNode(Interface *m3g, Node *node, M3GClass classID)
   1.101 +{
   1.102 +	/* Node is derived from Transformable */
   1.103 +	m3gInitTransformable(&node->transformable, m3g, classID);
   1.104 +    
   1.105 +    /* Set default values */
   1.106 +    
   1.107 +    node->enableBits = (NODE_RENDER_BIT|NODE_PICK_BIT);
   1.108 +	node->alphaFactor = (1u << NODE_ALPHA_FACTOR_BITS) - 1;
   1.109 +	node->scope = -1;
   1.110 +    node->zTarget = TARGET_NONE;
   1.111 +    node->yTarget = TARGET_NONE;
   1.112 +}
   1.113 +
   1.114 +/*!
   1.115 + * \internal
   1.116 + * \brief Destroys this Node object.
   1.117 + *
   1.118 + * \param obj Node object
   1.119 + */
   1.120 +static void m3gDestroyNode(Object *obj)
   1.121 +{
   1.122 +    Node *node = (Node *) obj;
   1.123 +    M3G_VALIDATE_OBJECT(node);
   1.124 +    M3G_ASSERT(node->parent == NULL);
   1.125 +    m3gDestroyTransformable((Object *) node);
   1.126 +}
   1.127 +
   1.128 +/*----------------------------------------------------------------------
   1.129 + * Internal functions
   1.130 + *--------------------------------------------------------------------*/
   1.131 +
   1.132 +/*!
   1.133 + * \internal
   1.134 + * \brief Checks if node is a child of the parent.
   1.135 + *
   1.136 + * \param parent    assumed parent Node object
   1.137 + * \param child     Node object to check
   1.138 + * \retval          M3G_TRUE is a child
   1.139 + * \retval          M3G_FALSE is not a child
   1.140 + */
   1.141 +static M3Gbool m3gIsChildOf(const Node *parent, const Node *child)
   1.142 +{
   1.143 +	const Node *n;
   1.144 +	
   1.145 +	for (n = child; n != NULL; n = n->parent) {
   1.146 +		if (n->parent == parent) return M3G_TRUE;
   1.147 +	}
   1.148 +	
   1.149 +	return M3G_FALSE;
   1.150 +}
   1.151 +
   1.152 +/*!
   1.153 + * \internal
   1.154 + * \brief Executes the given function for each node in a subtree
   1.155 + *
   1.156 + * The function \c func is executed recursively in each branch,
   1.157 + * starting from the leaves. That is, the function is called for the
   1.158 + * children of each group before the group itself.
   1.159 + *
   1.160 + * \param node   the node containing the subtree to process
   1.161 + * \param func   pointer to the function to all for each node
   1.162 + * \param params pointer to function-dependent arguments to pass
   1.163 + * to each \c func invokation; this may be e.g. a structure
   1.164 + * modified by \c func
   1.165 + *
   1.166 + * \return The return value of the top-level call to \c func
   1.167 + */
   1.168 +static void m3gForSubtree(Node *node, NodeFuncPtr func, void *params)
   1.169 +{
   1.170 +    M3GClass nodeClass;
   1.171 +    M3G_VALIDATE_OBJECT(node);
   1.172 +    
   1.173 +    /* Recurse into the children first */
   1.174 +    
   1.175 +    nodeClass = M3G_CLASS(node);
   1.176 +    
   1.177 +    if (nodeClass == M3G_CLASS_SKINNED_MESH) {
   1.178 +        m3gForSubtree((Node*)((SkinnedMesh*)node)->skeleton, func, params);
   1.179 +    }
   1.180 +    else if (nodeClass == M3G_CLASS_GROUP ||
   1.181 +             nodeClass == M3G_CLASS_WORLD) {
   1.182 +        Group *group = (Group*) node;
   1.183 +        Node *child = group->firstChild;
   1.184 +        if (child) {
   1.185 +            do {
   1.186 +                Node *next = child->right;
   1.187 +                m3gForSubtree(child, func, params);
   1.188 +                child = next;
   1.189 +            } while (child != group->firstChild);
   1.190 +        }
   1.191 +    }
   1.192 +
   1.193 +    /* Execute function on self */
   1.194 +    
   1.195 +    (*func)(node, params);
   1.196 +}
   1.197 +
   1.198 +/*!
   1.199 + * \internal
   1.200 + * \brief Overloaded Object3D method
   1.201 + *
   1.202 + * \param property      animation property
   1.203 + * \retval M3G_TRUE     property supported
   1.204 + * \retval M3G_FALSE    property not supported
   1.205 + */
   1.206 +static M3Gbool m3gNodeIsCompatible(M3Gint property)
   1.207 +{
   1.208 +    switch (property) {
   1.209 +    case M3G_ANIM_ALPHA:
   1.210 +    case M3G_ANIM_PICKABILITY:
   1.211 +    case M3G_ANIM_VISIBILITY:
   1.212 +        return M3G_TRUE;
   1.213 +    default:
   1.214 +        return m3gTransformableIsCompatible(property);
   1.215 +    }
   1.216 +}
   1.217 +
   1.218 +/*!
   1.219 + * \internal
   1.220 + * \brief Overloaded Object3D method
   1.221 + *
   1.222 + * \param self          Node object
   1.223 + * \param property      animation property
   1.224 + * \param valueSize     size of value array
   1.225 + * \param value         value array
   1.226 + */
   1.227 +static void m3gNodeUpdateProperty(Object *self,
   1.228 +                                  M3Gint property,
   1.229 +                                  M3Gint valueSize,
   1.230 +                                  const M3Gfloat *value)
   1.231 +{
   1.232 +    Node *node = (Node *)self;
   1.233 +    M3G_VALIDATE_OBJECT(node);
   1.234 +    M3G_ASSERT_PTR(value);
   1.235 +
   1.236 +    switch (property) {
   1.237 +    case M3G_ANIM_ALPHA:
   1.238 +        M3G_ASSERT(valueSize >= 1);
   1.239 +        node->alphaFactor =
   1.240 +            m3gRoundToInt(
   1.241 +                m3gMul(m3gClampFloat(value[0], 0.f, 1.f),
   1.242 +                       (float)((1 << NODE_ALPHA_FACTOR_BITS) - 1)));
   1.243 +        break;
   1.244 +    case M3G_ANIM_PICKABILITY:
   1.245 +        M3G_ASSERT(valueSize >= 1);
   1.246 +        node->enableBits &= ~NODE_PICK_BIT;
   1.247 +        if (value[0] >= 0.5f) {
   1.248 +            node->enableBits |= NODE_PICK_BIT;
   1.249 +        }
   1.250 +        break;
   1.251 +    case M3G_ANIM_VISIBILITY:
   1.252 +        M3G_ASSERT(valueSize >= 1);
   1.253 +        node->enableBits &= ~NODE_RENDER_BIT;
   1.254 +        if (value[0] >= 0.5f) {
   1.255 +            node->enableBits |= NODE_RENDER_BIT;
   1.256 +        }
   1.257 +        break;
   1.258 +    default:
   1.259 +        m3gTransformableUpdateProperty(self, property, valueSize, value);
   1.260 +    }
   1.261 +}
   1.262 +
   1.263 +/*!
   1.264 + * \internal
   1.265 + * \brief Overloaded Object3D method
   1.266 + *
   1.267 + * \param originalObj original Node object
   1.268 + * \param cloneObj pointer to cloned Node object
   1.269 + * \param pairs array for all object-duplicate pairs
   1.270 + * \param numPairs number of pairs
   1.271 + */
   1.272 +static M3Gbool m3gNodeDuplicate(const Object *originalObj,
   1.273 +                                Object **cloneObj,
   1.274 +                                Object **pairs,
   1.275 +                                M3Gint *numPairs)
   1.276 +{
   1.277 +    Node *original = (Node *)originalObj;
   1.278 +    Node *clone = (Node *)*cloneObj;
   1.279 +    M3G_ASSERT_PTR(*cloneObj); /* abstract class, must be derived */
   1.280 +
   1.281 +    /* Duplicate base class data */
   1.282 +    
   1.283 +    if (!m3gTransformableDuplicate(originalObj, cloneObj, pairs, numPairs)) {
   1.284 +        return M3G_FALSE;
   1.285 +    }
   1.286 +
   1.287 +    /* Duplicate our own data */
   1.288 +    
   1.289 +    clone->zReference  = original->zReference;
   1.290 +    clone->yReference  = original->yReference;
   1.291 +    clone->zTarget     = original->zTarget;
   1.292 +    clone->yTarget     = original->yTarget;
   1.293 +    clone->enableBits  = original->enableBits;
   1.294 +    clone->alphaFactor = original->alphaFactor;
   1.295 +    clone->scope       = original->scope;
   1.296 +    clone->hasBones    = original->hasBones;
   1.297 +    clone->hasRenderables = original->hasRenderables;
   1.298 +    
   1.299 +    return M3G_TRUE;
   1.300 +}
   1.301 +
   1.302 +/*!
   1.303 + * \internal
   1.304 + * \brief Find corresponding duplicate for a Node
   1.305 + *
   1.306 + * \param node Node object
   1.307 + * \param pairs array for all object-duplicate pairs
   1.308 + * \param numPairs number of pairs
   1.309 + */
   1.310 +static Node *m3gGetDuplicatedInstance(Node *node, Object **pairs, M3Gint numPairs)
   1.311 +{
   1.312 +    M3Gint i;
   1.313 +    for (i = 0; i < numPairs; i++)
   1.314 +        if (pairs[i * 2] == (Object *)node)
   1.315 +            return (Node *)pairs[i * 2 + 1];
   1.316 +    return NULL;
   1.317 +}
   1.318 +
   1.319 +/*!
   1.320 + * \internal
   1.321 + * \brief Updates references of the duplicate object.
   1.322 + *
   1.323 + * When objects are duplicated scenegraph references
   1.324 + * must be updated to equivalent duplicated references.
   1.325 + * This function is overloaded by objects that have
   1.326 + * references that has to be updated.
   1.327 + *
   1.328 + * \param self Node object
   1.329 + * \param pairs array for all object-duplicate pairs
   1.330 + * \param numPairs number of pairs
   1.331 + */
   1.332 +static void m3gNodeUpdateDuplicateReferences(Node *self, Object **pairs, M3Gint numPairs)
   1.333 +{
   1.334 +    if (self->zTarget != TARGET_NONE && self->zReference != NULL) {
   1.335 +        Node *duplicatedInstance = m3gGetDuplicatedInstance(self, pairs, numPairs);
   1.336 +        Node *duplicatedRef = m3gGetDuplicatedInstance(self->zReference, pairs, numPairs);
   1.337 +        if (duplicatedRef != NULL
   1.338 +            && m3gIsChildOf(m3gGetRoot(duplicatedInstance), duplicatedRef)) {
   1.339 +            duplicatedInstance->zReference = duplicatedRef;
   1.340 +        }
   1.341 +    }
   1.342 +    if (self->yTarget != TARGET_NONE && self->yReference != NULL) {
   1.343 +        Node *duplicatedInstance = m3gGetDuplicatedInstance(self, pairs, numPairs);
   1.344 +        Node *duplicatedRef = m3gGetDuplicatedInstance(self->yReference, pairs, numPairs);
   1.345 +        if (duplicatedRef != NULL
   1.346 +            && m3gIsChildOf(m3gGetRoot(duplicatedInstance), duplicatedRef)) {
   1.347 +            duplicatedInstance->yReference = duplicatedRef;
   1.348 +        }
   1.349 +    }
   1.350 +}
   1.351 +
   1.352 +/*!
   1.353 + * \internal
   1.354 + * \brief Gets size of the subtree
   1.355 + *
   1.356 + * \param node Node object
   1.357 + * \param numRef number of references
   1.358 + */
   1.359 +static void m3gDoGetSubtreeSize(Node *node, void *numRef)
   1.360 +{
   1.361 +    M3Gint *num = (M3Gint *)numRef;
   1.362 +    M3G_UNREF(node);
   1.363 +    (*num)++;
   1.364 +}
   1.365 +
   1.366 +/*!
   1.367 + * \internal
   1.368 + * \brief Default function for non-pickable objects
   1.369 + *
   1.370 + * \param self      Camera object
   1.371 + * \param mask      pick scope mask
   1.372 + * \param ray       pick ray
   1.373 + * \param ri        RayIntersection object
   1.374 + * \param toGroup   transform to originating group
   1.375 + * \retval M3G_TRUE always return success
   1.376 + */
   1.377 +static M3Gbool m3gNodeRayIntersect(Node *self,
   1.378 +                                   M3Gint mask,
   1.379 +                                   M3Gfloat *ray,
   1.380 +                                   RayIntersection *ri,
   1.381 +                                   Matrix *toGroup)
   1.382 +{
   1.383 +    M3G_UNREF(self);
   1.384 +    M3G_UNREF(mask);
   1.385 +    M3G_UNREF(ray);
   1.386 +    M3G_UNREF(ri);
   1.387 +    M3G_UNREF(toGroup);
   1.388 +
   1.389 +    return M3G_TRUE;
   1.390 +}
   1.391 +
   1.392 +/*!
   1.393 + * \internal
   1.394 + * \brief Computes the bounding box for this node
   1.395 + *
   1.396 + * \param self  node pointer
   1.397 + * \param bbox  bounding box structure filled in for non-zero return values
   1.398 + * 
   1.399 + * \return The "yield" factor for the node, i.e. the approximate
   1.400 + * rendering cost of the node \em including any internal bounding box
   1.401 + * checks; the yield factor is used to estimate the benefit of adding
   1.402 + * enclosing bounding boxes at higher levels in the scene tree
   1.403 + */
   1.404 +static M3Gint m3gNodeGetBBox(Node *self, AABB *bbox)
   1.405 +{
   1.406 +    M3G_UNREF(self);
   1.407 +    M3G_UNREF(bbox);
   1.408 +    return 0;
   1.409 +}
   1.410 +
   1.411 +/*!
   1.412 + * \internal
   1.413 + * \brief Updates the bounding box for this node
   1.414 + */
   1.415 +static M3Gbool m3gNodeValidate(Node *self, M3Gbitmask stateBits, M3Gint scope)
   1.416 +{
   1.417 +    M3G_UNREF(stateBits);
   1.418 +    M3G_UNREF(scope);
   1.419 +
   1.420 +    /* Invalidate parent state in case we've encountered a previously
   1.421 +     * disabled node, then reset the dirty bits */
   1.422 +    
   1.423 +    if (self->dirtyBits && self->parent) {
   1.424 +        m3gInvalidateNode(self->parent, self->dirtyBits);
   1.425 +    }
   1.426 +    self->dirtyBits = 0;
   1.427 +    return M3G_TRUE;
   1.428 +}
   1.429 +
   1.430 +/*!
   1.431 + * \internal
   1.432 + * \brief Gets a vector according to alignment target
   1.433 + * and transforms it with a given transform.
   1.434 + *
   1.435 + * \param target        alignment target
   1.436 + * \param transform     transform to be applied
   1.437 + * \param out           vector to fill in
   1.438 + */
   1.439 +static void m3gTransformAlignmentTarget(M3Genum target,
   1.440 +                                        const Matrix *transform,
   1.441 +                                        Vec4 *out)
   1.442 +{
   1.443 +	switch (target) {
   1.444 +    case TARGET_ORIGIN:
   1.445 +		*out = Vec4_ORIGIN;
   1.446 +	    break;
   1.447 +	case TARGET_X_AXIS:
   1.448 +		*out = Vec4_X_AXIS;
   1.449 +	    break;
   1.450 +	case TARGET_Y_AXIS:
   1.451 +		*out = Vec4_Y_AXIS;
   1.452 +	    break;
   1.453 +	case TARGET_Z_AXIS:
   1.454 +		*out = Vec4_Z_AXIS;
   1.455 +	    break;
   1.456 +	default:
   1.457 +		M3G_ASSERT(M3G_FALSE);
   1.458 +	}
   1.459 +
   1.460 +	m3gTransformVec4(transform, out);
   1.461 +}
   1.462 +
   1.463 +/*!
   1.464 + * \internal
   1.465 + * \brief Computes a single alignment rotation for a node.
   1.466 + *
   1.467 + * \param node              Node object
   1.468 + * \param srcAxis           source axis
   1.469 + * \param targetNode        Node object
   1.470 + * \param targetAxisName    target axis name
   1.471 + * \param constraint        constraint
   1.472 + */
   1.473 +static M3Gbool m3gComputeAlignmentRotation(Node *node,
   1.474 +                                           const Vec3 *srcAxis,
   1.475 +                                           const Node *targetNode,
   1.476 +                                           M3Genum targetAxisName,
   1.477 +                                           M3Genum constraint)
   1.478 +{
   1.479 +    const Node *parent = node->parent;
   1.480 +    Matrix transform;
   1.481 +    Vec4 targetAxis;
   1.482 +    
   1.483 +    M3G_VALIDATE_OBJECT(parent);
   1.484 +    M3G_ASSERT(constraint == TARGET_NONE || constraint == TARGET_Z_AXIS);
   1.485 +
   1.486 +    /* Get the transformation from the reference target node to the
   1.487 +     * current node, omitting all components except translation.
   1.488 +     * Rotation is also applied if this is a constrained alignment. */
   1.489 +    {
   1.490 +        const Transformable *tf = &node->transformable;
   1.491 +        
   1.492 +        if (!m3gGetTransformTo((M3GNode) targetNode, (M3GNode) parent,
   1.493 +                               &transform)) {
   1.494 +            return M3G_FALSE;
   1.495 +        }
   1.496 +        m3gPreTranslateMatrix(&transform, -tf->tx, -tf->ty, -tf->tz);
   1.497 +        
   1.498 +        if (constraint != TARGET_NONE) {
   1.499 +            Quat rot = tf->orientation;
   1.500 +            rot.w = -rot.w;
   1.501 +            m3gPreRotateMatrixQuat(&transform, &rot);
   1.502 +        }
   1.503 +    }
   1.504 +
   1.505 +    m3gTransformAlignmentTarget(targetAxisName, &transform, &targetAxis);
   1.506 +
   1.507 +    /* Apply the Z constraint if enabled; this is done by simply
   1.508 +     * zeroing the Z component of the target vector.  If the X and Y
   1.509 +     * alone don't span a non-zero vector, just exit as there's
   1.510 +     * nothing defined to rotate about. */
   1.511 +
   1.512 +    if (constraint == TARGET_Z_AXIS) {
   1.513 +        M3Gfloat norm = m3gAdd(m3gMul(targetAxis.x, targetAxis.x),
   1.514 +                               m3gMul(targetAxis.y, targetAxis.y));
   1.515 +        if (norm < 1.0e-5f) {
   1.516 +            return M3G_TRUE;
   1.517 +        }
   1.518 +        norm = m3gRcpSqrt(norm);
   1.519 +        
   1.520 +        targetAxis.x = m3gMul(targetAxis.x, norm);
   1.521 +        targetAxis.y = m3gMul(targetAxis.y, norm);
   1.522 +        targetAxis.z = 0.0f;
   1.523 +                                
   1.524 +        M3G_ASSERT(srcAxis->z == 0.0f);
   1.525 +    }
   1.526 +    else {
   1.527 +        m3gNormalizeVec3((Vec3*)&targetAxis); /* srcAxis will be unit length */
   1.528 +    }
   1.529 +
   1.530 +    if (constraint != TARGET_NONE) {
   1.531 +        Quat rot;
   1.532 +        m3gSetQuatRotation(&rot, srcAxis, (const Vec3*) &targetAxis);
   1.533 +        m3gMulQuat(&node->transformable.orientation, &rot);
   1.534 +    }
   1.535 +    else {
   1.536 +        m3gSetQuatRotation(&node->transformable.orientation,
   1.537 +                           srcAxis, (const Vec3*) &targetAxis);
   1.538 +    }
   1.539 +
   1.540 +    /* Invalidate transformations and bounding boxes after setting
   1.541 +     * node orientation */
   1.542 +    
   1.543 +    m3gInvalidateTransformable((Transformable*)node);
   1.544 +
   1.545 +    return M3G_TRUE;
   1.546 +}
   1.547 +
   1.548 +/*!
   1.549 + * \internal
   1.550 + * \brief Computes alignment for a single node.
   1.551 + *
   1.552 + * \param node              Node object
   1.553 + * \param refNode           Node object
   1.554 + * \retval                  M3G_TRUE alignment ok
   1.555 + * \retval                  M3G_FALSE alignment failed
   1.556 + */
   1.557 +static M3Gbool m3gComputeAlignment(Node *node, const Node *refNode)
   1.558 +{
   1.559 +    const Node *root = m3gGetRoot(node);
   1.560 +    const Node *zRef = node->zReference;
   1.561 +    const Node *yRef = node->yReference;
   1.562 +    const M3Genum zTarget = node->zTarget;
   1.563 +    const M3Genum yTarget = node->yTarget;
   1.564 +    M3G_VALIDATE_OBJECT(node);
   1.565 +
   1.566 +    /* Quick exit if nothing to do */
   1.567 +    
   1.568 +    if (zTarget == TARGET_NONE && yTarget == TARGET_NONE) {
   1.569 +        return M3G_TRUE;
   1.570 +    }
   1.571 +        
   1.572 +    /* Check scene graph state */
   1.573 +    
   1.574 +    if (zRef != NULL && (m3gIsChildOf(node, zRef) || m3gGetRoot(zRef) != root)) {
   1.575 +        m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_OPERATION);
   1.576 +        return M3G_FALSE;
   1.577 +    }
   1.578 +    if (yRef != NULL && (m3gIsChildOf(node, yRef) || m3gGetRoot(yRef) != root)) {
   1.579 +        m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_OPERATION);
   1.580 +        return M3G_FALSE;
   1.581 +    }
   1.582 +
   1.583 +    /* Compute the alignment rotations for Z and Y */
   1.584 +    {
   1.585 +        if (node->zTarget != TARGET_NONE) {
   1.586 +            if (zRef == NULL && refNode == node) {
   1.587 +                m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_OPERATION);
   1.588 +                return M3G_FALSE;
   1.589 +            }
   1.590 +            if (!m3gComputeAlignmentRotation(
   1.591 +                    node,
   1.592 +                    (const Vec3*) &Vec4_Z_AXIS,
   1.593 +                    zRef != NULL ? zRef : refNode,
   1.594 +                    zTarget,
   1.595 +                    TARGET_NONE)) {
   1.596 +                return M3G_FALSE;
   1.597 +            }
   1.598 +        }
   1.599 +        if (node->yTarget != TARGET_NONE) {
   1.600 +            if (yRef == NULL && refNode == node) {
   1.601 +                m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_OPERATION);
   1.602 +                return M3G_FALSE;
   1.603 +            }
   1.604 +            if (!m3gComputeAlignmentRotation(
   1.605 +                    node,
   1.606 +                    (const Vec3*) &Vec4_Y_AXIS,
   1.607 +                    yRef != NULL ? yRef : refNode,
   1.608 +                    yTarget,
   1.609 +                    zTarget != TARGET_NONE ? TARGET_Z_AXIS : TARGET_NONE)) {
   1.610 +                return M3G_FALSE;
   1.611 +            }
   1.612 +        }
   1.613 +    }   
   1.614 +    return M3G_TRUE;
   1.615 +}
   1.616 +
   1.617 +/*!
   1.618 + * \internal
   1.619 + * \brief Gets the transformation to an ancestor node
   1.620 + */
   1.621 +static void m3gGetTransformUpPath(const Node *node, const Node *ancestor, Matrix *transform)
   1.622 +{
   1.623 +    M3G_ASSERT(node);
   1.624 +    
   1.625 +    if (node == ancestor) {
   1.626 +        m3gIdentityMatrix(transform);
   1.627 +    }
   1.628 +    else {
   1.629 +        TCache *tc;
   1.630 +        M3G_ASSERT(!ancestor || m3gIsChildOf(ancestor, node));
   1.631 +    
   1.632 +        /* Look for a cached path */
   1.633 +
   1.634 +        tc = m3gGetTransformCache(M3G_INTERFACE(node));
   1.635 +        if (m3gGetCachedPath(tc, node, ancestor, transform)) {
   1.636 +            return;
   1.637 +        }
   1.638 +    
   1.639 +        /* No dice -- do a recursive search and cache the result */
   1.640 +
   1.641 +        if (node->parent == ancestor) {
   1.642 +            m3gGetCompositeNodeTransform(node, transform);
   1.643 +        }
   1.644 +        else {
   1.645 +            m3gGetTransformUpPath(node->parent, ancestor, transform);
   1.646 +            {
   1.647 +                Matrix mtx;
   1.648 +                m3gGetCompositeNodeTransform(node, &mtx);
   1.649 +                m3gMulMatrix(transform, &mtx);
   1.650 +            }
   1.651 +        }
   1.652 +        m3gCachePath(tc, node, ancestor, transform);
   1.653 +    }
   1.654 +}
   1.655 +
   1.656 +/*!
   1.657 + * \internal
   1.658 + * \brief Gets depth of a node in the scenegraph.
   1.659 + *
   1.660 + * \param node              Node object
   1.661 + * \return                  Depth of the node
   1.662 + */
   1.663 +static M3Gint m3gGetDepth(const Node *node)
   1.664 +{
   1.665 +	const Node *n = node;
   1.666 +	M3Gint depth = 0;
   1.667 +
   1.668 +	while (n->parent != NULL) {
   1.669 +	    n = n->parent;
   1.670 +	    depth++;
   1.671 +	}
   1.672 +
   1.673 +	return depth;
   1.674 +}
   1.675 +
   1.676 +/*!
   1.677 + * \internal
   1.678 + * \brief Gets root of a node in the scenegraph.
   1.679 + *
   1.680 + * \param node              Node object
   1.681 + * \return                  root Node object
   1.682 + */
   1.683 +static Node *m3gGetRoot(const Node *node)
   1.684 +{
   1.685 +    const Node *n = node;
   1.686 +
   1.687 +    while (n->parent != NULL) {
   1.688 +        n = n->parent;
   1.689 +    }
   1.690 +
   1.691 +    return (Node *)n;
   1.692 +}
   1.693 +
   1.694 +/*!
   1.695 + * \internal
   1.696 + * \brief Gets total alpha factor.
   1.697 + *
   1.698 + * \param node              Node object
   1.699 + * \param root              root Node object
   1.700 + */
   1.701 +static M3Guint m3gGetTotalAlphaFactor(Node *node, const Node *root)
   1.702 +{
   1.703 +    const Node *n = node;
   1.704 +    M3Guint f = node->alphaFactor;
   1.705 +    
   1.706 +    while (n->parent != NULL && n != root) {
   1.707 +        n = n->parent;
   1.708 +        f = ((f + 1) * n->alphaFactor) >> NODE_ALPHA_FACTOR_BITS;
   1.709 +    }
   1.710 +    return f;
   1.711 +}
   1.712 +
   1.713 +/*!
   1.714 + * \internal
   1.715 + * \brief Checks if node is enabled for rendering from the root.
   1.716 + *
   1.717 + * \param node              Node object
   1.718 + * \param root              root Node object
   1.719 + * \retval                  M3G_TRUE node is visible
   1.720 + * \retval                  M3G_FALSE node is not visible
   1.721 + */
   1.722 +static M3Gbool m3gHasEnabledPath(const Node *node, const Node *root)
   1.723 +{
   1.724 +	const Node *n;
   1.725 +	
   1.726 +	for (n = node; n != NULL; n = n->parent) {
   1.727 +	    if (!(n->enableBits & NODE_RENDER_BIT)) {
   1.728 +            return M3G_FALSE;
   1.729 +        }
   1.730 +	    if (n == root) {
   1.731 +            break;
   1.732 +        }
   1.733 +	}
   1.734 +
   1.735 +    return M3G_TRUE;
   1.736 +}
   1.737 +
   1.738 +/*!
   1.739 + * \internal
   1.740 + * \brief Checks if node is pickable from the root.
   1.741 + *
   1.742 + * \param node              Node object
   1.743 + * \param root              root Node object
   1.744 + * \retval                  M3G_TRUE node is pickable
   1.745 + * \retval                  M3G_FALSE node is not pickable
   1.746 + */
   1.747 +static M3Gbool m3gHasPickablePath(const Node *node, const Node *root)
   1.748 +{
   1.749 +	const Node *n;
   1.750 +	
   1.751 +	for (n = node; n != NULL; n = n->parent) {
   1.752 +	    if (!(n->enableBits & NODE_PICK_BIT)) {
   1.753 +            return M3G_FALSE;
   1.754 +        }
   1.755 +	    if (n == root) {
   1.756 +            break;
   1.757 +        }
   1.758 +	}
   1.759 +
   1.760 +    return M3G_TRUE;
   1.761 +}
   1.762 +
   1.763 +#if defined(M3G_ENABLE_VF_CULLING)
   1.764 +/*!
   1.765 + * \brief Invalidates the bounding box hierarchy from a node upwards
   1.766 + */
   1.767 +static void m3gInvalidateNode(Node *node, M3Gbitmask flags)
   1.768 +{
   1.769 +    Interface *m3g = M3G_INTERFACE(node);
   1.770 +    M3G_BEGIN_PROFILE(m3g, M3G_PROFILE_VFC_UPDATE);
   1.771 +
   1.772 +    while (node && (node->dirtyBits & flags) != flags) {
   1.773 +        node->dirtyBits |= flags;
   1.774 +        node = node->parent;
   1.775 +    }
   1.776 +    M3G_END_PROFILE(m3g, M3G_PROFILE_VFC_UPDATE);
   1.777 +}
   1.778 +#endif /*M3G_ENABLE_VF_CULLING*/
   1.779 +
   1.780 +/*!
   1.781 + * \internal
   1.782 + * \brief Aligns a node.
   1.783 + *
   1.784 + * \param node              Node object
   1.785 + * \param ref               reference Node object
   1.786 + *
   1.787 + * \retval M3G_TRUE continue align
   1.788 + * \retval M3G_FALSE abort align
   1.789 + */
   1.790 +static M3Gbool m3gNodeAlign(Node *node, const Node *ref) 
   1.791 +{
   1.792 +    if (ref == NULL) {
   1.793 +        return m3gComputeAlignment(node, node);
   1.794 +    }
   1.795 +    else {
   1.796 +        M3G_VALIDATE_OBJECT(ref);
   1.797 +        return m3gComputeAlignment(node, ref);
   1.798 +    }
   1.799 +}
   1.800 +
   1.801 +/*!
   1.802 + * \internal
   1.803 + * \brief Updates node counters when moving nodes around
   1.804 + */
   1.805 +static void m3gUpdateNodeCounters(Node *node,
   1.806 +                                  M3Gint nonCullableChange,
   1.807 +                                  M3Gint renderableChange)
   1.808 +{
   1.809 +    Interface *m3g = M3G_INTERFACE(node);
   1.810 +    M3Gbool hasRenderables = (renderableChange > 0);
   1.811 +    M3G_BEGIN_PROFILE(m3g, M3G_PROFILE_VFC_UPDATE);
   1.812 +    while (node) {
   1.813 +        M3GClass nodeClass = M3G_CLASS(node);
   1.814 +        if (nodeClass == M3G_CLASS_GROUP || nodeClass == M3G_CLASS_WORLD) {
   1.815 +            Group *g = (Group *) node;
   1.816 +            g->numNonCullables = (M3Gushort)(g->numNonCullables + nonCullableChange);
   1.817 +            g->numRenderables  = (M3Gushort)(g->numRenderables + renderableChange);
   1.818 +            hasRenderables = (g->numRenderables > 0);
   1.819 +        }
   1.820 +        node->hasRenderables = hasRenderables;
   1.821 +        node = node->parent;
   1.822 +    }
   1.823 +    M3G_END_PROFILE(m3g, M3G_PROFILE_VFC_UPDATE);
   1.824 +}
   1.825 +
   1.826 +/*!
   1.827 + * \internal
   1.828 + * \brief Sets the parent link of this node to a new value
   1.829 + *
   1.830 + * Relevant reference counts are updated accordingly, so note that
   1.831 + * setting the parent to NULL may lead to either the node or the
   1.832 + * parent itself being destroyed.
   1.833 + *
   1.834 + * \param node Node object
   1.835 + * \param parent parent Node object
   1.836 + */
   1.837 +static void m3gSetParent(Node *node, Node *parent)
   1.838 +{
   1.839 +    M3GClass nodeClass;
   1.840 +    M3Gint nonCullableChange = 0, renderableChange = 0;
   1.841 +    M3G_VALIDATE_OBJECT(node);
   1.842 +
   1.843 +    /* Determine the number of various kinds of nodes being moved around */
   1.844 +
   1.845 +    M3G_BEGIN_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_VFC_UPDATE);
   1.846 +    nodeClass = M3G_CLASS(node);
   1.847 +    switch (nodeClass) {
   1.848 +    case M3G_CLASS_GROUP:
   1.849 +    {
   1.850 +        const Group *g = (const Group *) node;
   1.851 +        nonCullableChange = g->numNonCullables;
   1.852 +        renderableChange  = g->numRenderables;
   1.853 +        break;
   1.854 +    }
   1.855 +    case M3G_CLASS_SPRITE:
   1.856 +        renderableChange = 1;
   1.857 +        if (m3gIsScaledSprite((M3GSprite) node)) {
   1.858 +            break;
   1.859 +        }
   1.860 +        /* conditional fall-through! */
   1.861 +    case M3G_CLASS_LIGHT:
   1.862 +        nonCullableChange = 1;
   1.863 +        break;
   1.864 +    case M3G_CLASS_SKINNED_MESH:
   1.865 +    {
   1.866 +        const SkinnedMesh *mesh = (const SkinnedMesh *) node;
   1.867 +        nonCullableChange += mesh->skeleton->numNonCullables;
   1.868 +        renderableChange  += mesh->skeleton->numRenderables + 1;
   1.869 +        break;
   1.870 +    }
   1.871 +    case M3G_CLASS_MESH:
   1.872 +    case M3G_CLASS_MORPHING_MESH:
   1.873 +        renderableChange = 1;
   1.874 +        break;
   1.875 +    default:
   1.876 +        ;
   1.877 +    }
   1.878 +    M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_VFC_UPDATE);
   1.879 +
   1.880 +    /* Invalidate any cached transformation paths through this node
   1.881 +     * *before* we move the node */
   1.882 +    
   1.883 +    m3gInvalidateCachedPaths(m3gGetTransformCache(M3G_INTERFACE(node)), node);
   1.884 +    
   1.885 +    /* Update bookkeeping for the old parent tree */
   1.886 +
   1.887 +    if (node->parent) {
   1.888 +        m3gUpdateNodeCounters(node->parent,
   1.889 +                              -nonCullableChange, -renderableChange);
   1.890 +        if (renderableChange) {
   1.891 +            m3gInvalidateNode(node->parent, NODE_BBOX_BIT|NODE_TRANSFORMS_BIT);
   1.892 +        }
   1.893 +    }
   1.894 +
   1.895 +    /* Change the parent link */
   1.896 +    
   1.897 +    if (node->parent == NULL && parent != NULL) {
   1.898 +        node->parent = parent;
   1.899 +        m3gAddRef((Object *) node);
   1.900 +    }
   1.901 +    else if (node->parent != NULL && parent == NULL) {
   1.902 +        node->parent = parent;
   1.903 +        m3gDeleteRef((Object *) node);
   1.904 +    }
   1.905 +
   1.906 +    /* Update bookkeeping for the new parent tree */
   1.907 +
   1.908 +    if (parent) {
   1.909 +        M3Gbitmask dirtyBits = node->dirtyBits;
   1.910 +        if (renderableChange) {
   1.911 +            dirtyBits |= NODE_BBOX_BIT;
   1.912 +        }
   1.913 +        if (node->hasBones) {
   1.914 +            dirtyBits |= NODE_TRANSFORMS_BIT;
   1.915 +        }
   1.916 +        m3gUpdateNodeCounters(parent, nonCullableChange, renderableChange);
   1.917 +        m3gInvalidateNode(parent, dirtyBits);
   1.918 +    }
   1.919 +}
   1.920 +
   1.921 +/*!
   1.922 + * \brief Computes the "near" and "far" box vertices
   1.923 + * for plane testing
   1.924 + */
   1.925 +static M3G_INLINE void m3gGetTestPoints(const Vec3 *planeNormal,
   1.926 +                                        const AABB *box,
   1.927 +                                        Vec3 *vNear, Vec3 *vFar)
   1.928 +{
   1.929 +    const M3Gfloat *fNormal = (const M3Gfloat*) planeNormal;
   1.930 +    M3Gfloat *fNear = (M3Gfloat*) vNear;
   1.931 +    M3Gfloat *fFar  = (M3Gfloat*) vFar;
   1.932 +    int i;
   1.933 +    
   1.934 +    for (i = 0; i < 3; ++i) {
   1.935 +        M3Gfloat n = *fNormal++;
   1.936 +        if (IS_NEGATIVE(n)) {
   1.937 +            *fNear++ = box->max[i];
   1.938 +            *fFar++ = box->min[i];
   1.939 +        }
   1.940 +        else {
   1.941 +            *fNear++ = box->min[i];
   1.942 +            *fFar++ = box->max[i];
   1.943 +        }
   1.944 +    }
   1.945 +}
   1.946 +
   1.947 +#if defined(M3G_ENABLE_VF_CULLING)
   1.948 +/*!
   1.949 + * \internal
   1.950 + * \brief Update the frustum culling mask for one level of an AABB hierarchy
   1.951 + *
   1.952 + * \param s    the current traversal state
   1.953 + * \param bbox the bounding box to check against
   1.954 + */
   1.955 +static void m3gUpdateCullingMask(SetupRenderState *s,
   1.956 +                                 const Camera *cam, const AABB *bbox)
   1.957 +{
   1.958 +    M3Gbitmask cullMask = s->cullMask;
   1.959 +    M3G_BEGIN_PROFILE(M3G_INTERFACE(cam), M3G_PROFILE_VFC_TEST);
   1.960 +
   1.961 +    /* First, check whether any planes are previously marked as
   1.962 +     * intersecting */
   1.963 +    
   1.964 +    if (cullMask & CULLMASK_ALL) {
   1.965 +                
   1.966 +        /* We need to do some culling, so let's get the planes and the
   1.967 +         * transformation matrix; note that the "toCamera" matrix is
   1.968 +         * the inverse of the camera-to-node matrix, so we only need
   1.969 +         * to transpose */
   1.970 +
   1.971 +        M3Gbitmask planeMask;
   1.972 +        const Vec4 *camPlanes = m3gFrustumPlanes(cam);
   1.973 +        
   1.974 +        Matrix t;
   1.975 +        m3gMatrixTranspose(&t, &s->toCamera);
   1.976 +        
   1.977 +        /* Loop over the active frustum planes, testing the ones we've
   1.978 +         * previously intersected with */
   1.979 +
   1.980 +        planeMask = CULLMASK_INTERSECTS;
   1.981 +        while (planeMask <= cullMask) {
   1.982 +            if (cullMask & planeMask) {
   1.983 +                
   1.984 +                /* Transform the respective frustum plane into the node
   1.985 +                 * local space our AABB is in */
   1.986 +                
   1.987 +                Vec4 plane;
   1.988 +                plane = *camPlanes++;
   1.989 +                m3gTransformVec4(&t, &plane);
   1.990 +                
   1.991 +                /* Test the AABB against the plane and update the mask
   1.992 +                 * based on the result */
   1.993 +                
   1.994 +                m3gIncStat(M3G_INTERFACE(cam), M3G_STAT_CULLING_TESTS, 1);
   1.995 +                {
   1.996 +                    /* Get the "near" and "far" corner points of the box */
   1.997 +                    
   1.998 +                    const Vec3* normal = (Vec3*) &plane;
   1.999 +                    Vec3 vNear, vFar;
  1.1000 +                    m3gGetTestPoints(normal, bbox, &vNear, &vFar);
  1.1001 +
  1.1002 +                    /* Our normals point inside, so flip this */
  1.1003 +                    
  1.1004 +                    plane.w = m3gNegate(plane.w);
  1.1005 +                    
  1.1006 +                    /* "Far" point behind plane? */
  1.1007 +                    
  1.1008 +                    if (m3gDot3(normal, &vFar) < plane.w) {
  1.1009 +                        /* All outside, no need to test further! */
  1.1010 +                        cullMask = 0;
  1.1011 +                        break;
  1.1012 +                    }
  1.1013 +                    
  1.1014 +                    /* "Near" point in front of plane? */
  1.1015 +
  1.1016 +                    if (m3gDot3(normal, &vNear) > plane.w) {
  1.1017 +                        cullMask &= ~planeMask;
  1.1018 +                        cullMask |= planeMask >> 1; /* intersects->inside */
  1.1019 +                    }
  1.1020 +                }
  1.1021 +            }
  1.1022 +            planeMask <<= 2; /* next plane */
  1.1023 +        }
  1.1024 +        s->cullMask = cullMask; /* write the output mask */
  1.1025 +    }
  1.1026 +    M3G_END_PROFILE(M3G_INTERFACE(cam), M3G_PROFILE_VFC_TEST);
  1.1027 +}
  1.1028 +#endif /*M3G_ENABLE_VF_CULLING*/
  1.1029 +
  1.1030 +/*----------------------------------------------------------------------
  1.1031 + * Public API functions
  1.1032 + *--------------------------------------------------------------------*/
  1.1033 +
  1.1034 +/*!
  1.1035 + * \brief Gets transform from node to another.
  1.1036 + *
  1.1037 + * \param handle            Node object
  1.1038 + * \param hTarget           target Node object
  1.1039 + * \param transform         transform to fill in
  1.1040 + * \retval                  M3G_TRUE success
  1.1041 + * \retval                  M3G_FALSE failed
  1.1042 + */
  1.1043 +M3G_API M3Gbool m3gGetTransformTo(M3GNode handle,
  1.1044 +                                  M3GNode hTarget,
  1.1045 +                                  M3GMatrix *transform)
  1.1046 +{
  1.1047 +    const Node *node = (Node *) handle;
  1.1048 +	const Node *target = (Node *) hTarget;
  1.1049 +    TCache *tc;
  1.1050 +	
  1.1051 +    M3G_VALIDATE_OBJECT(node);
  1.1052 +    M3G_BEGIN_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO);
  1.1053 +
  1.1054 +    /* Look for quick exits first */
  1.1055 +    
  1.1056 +    tc = m3gGetTransformCache(M3G_INTERFACE(node));
  1.1057 +
  1.1058 +    if (node == target) {
  1.1059 +        m3gIdentityMatrix(transform);
  1.1060 +        M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO);
  1.1061 +        return M3G_TRUE;
  1.1062 +    }
  1.1063 +    else if (m3gGetCachedPath(tc, node, target, transform)) {
  1.1064 +        M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO);
  1.1065 +        return M3G_TRUE;
  1.1066 +    }
  1.1067 +    else {
  1.1068 +
  1.1069 +        /* No luck, must recompute the whole thing -- begin by finding
  1.1070 +         * a common ancestor node for the pivot point of the path */
  1.1071 +        
  1.1072 +        const Node *pivot = NULL;
  1.1073 +        {
  1.1074 +            const Node *s = node;
  1.1075 +            const Node *t = target;
  1.1076 +	
  1.1077 +            /* First traverse to the same depth */
  1.1078 +            {
  1.1079 +                int sd = m3gGetDepth(s);
  1.1080 +                int td = m3gGetDepth(t);
  1.1081 +            
  1.1082 +                while (sd > td) {
  1.1083 +                    s = s->parent;
  1.1084 +                    --sd;
  1.1085 +                }
  1.1086 +                while (td > sd) {
  1.1087 +                    t = t->parent;
  1.1088 +                    --td;
  1.1089 +                }
  1.1090 +            }
  1.1091 +
  1.1092 +            /* Then traverse until we reach a common node or run out of
  1.1093 +             * ancestors in both branches, meaning there is no path
  1.1094 +             * between the nodes */
  1.1095 +        
  1.1096 +            while (s != t) {
  1.1097 +                s = s->parent;	
  1.1098 +                t = t->parent;
  1.1099 +            }
  1.1100 +            pivot = s;
  1.1101 +        }
  1.1102 +        if (!pivot) {
  1.1103 +            M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO);
  1.1104 +            return M3G_FALSE;
  1.1105 +        }
  1.1106 +        
  1.1107 +        /* Now, fetch the transformations for both branches and
  1.1108 +         * combine into the complete transformation; optimize by
  1.1109 +         * skipping most of that altogether for paths where the target
  1.1110 +         * node is the topmost node of the path */
  1.1111 +        
  1.1112 +        if (pivot != target) {
  1.1113 +            Matrix targetPath;
  1.1114 +            Matrix sourcePath;
  1.1115 +
  1.1116 +            /* Look for a cached version of the to-target path to
  1.1117 +             * avoid the inversion if possible */
  1.1118 +            
  1.1119 +            if (!m3gGetCachedPath(tc, pivot, target, &targetPath)) {
  1.1120 +                m3gGetTransformUpPath(target, pivot, &targetPath);
  1.1121 +            
  1.1122 +                /* Invert the target-side path since we want the
  1.1123 +                 * downstream transformation for that one */
  1.1124 +            
  1.1125 +                M3G_BEGIN_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_INVERT);
  1.1126 +                if (!m3gInvertMatrix(&targetPath)) {    
  1.1127 +                    M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO);
  1.1128 +                    m3gRaiseError(M3G_INTERFACE(node), M3G_ARITHMETIC_ERROR);
  1.1129 +                    return M3G_FALSE;
  1.1130 +                }
  1.1131 +
  1.1132 +                /* Cache the inverse for future use */
  1.1133 +                m3gCachePath(tc, pivot, target, &targetPath);
  1.1134 +            }
  1.1135 +            
  1.1136 +            M3G_ASSERT(m3gIsWUnity(&targetPath));
  1.1137 +
  1.1138 +            /* Paste in the from-source path to get the complete
  1.1139 +             * transformation for the path */
  1.1140 +
  1.1141 +            if (pivot != node) {
  1.1142 +                m3gGetTransformUpPath(node, pivot, &sourcePath);
  1.1143 +
  1.1144 +                M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_INVERT);
  1.1145 +                m3gRightMulMatrix(&targetPath, &sourcePath);
  1.1146 +                m3gCopyMatrix(transform, &targetPath);
  1.1147 +                M3G_ASSERT(m3gIsWUnity(transform));
  1.1148 +            
  1.1149 +                /* Cache the combined result for future use */
  1.1150 +                m3gCachePath(tc, node, target, transform);
  1.1151 +            }
  1.1152 +            else {
  1.1153 +                *transform = targetPath;
  1.1154 +            }
  1.1155 +        }
  1.1156 +        else {
  1.1157 +            /* For many cases, we only need this upstream path */
  1.1158 +            m3gGetTransformUpPath(node, pivot, transform);
  1.1159 +        }
  1.1160 +        
  1.1161 +        M3G_END_PROFILE(M3G_INTERFACE(node), M3G_PROFILE_TRANSFORM_TO);
  1.1162 +        return M3G_TRUE;
  1.1163 +    }
  1.1164 +}
  1.1165 +
  1.1166 +/*!
  1.1167 + * \brief Sets alignment targets.
  1.1168 + *
  1.1169 + * \param handle            Node object
  1.1170 + * \param hZReference       Z target Node object
  1.1171 + * \param zTarget           Z target type
  1.1172 + * \param hYReference       Y target Node object
  1.1173 + * \param yTarget           Y target type
  1.1174 + */
  1.1175 +M3G_API void m3gSetAlignment(M3GNode handle,
  1.1176 +                             M3GNode hZReference, M3Gint zTarget,
  1.1177 +                             M3GNode hYReference, M3Gint yTarget)
  1.1178 +{
  1.1179 +    Node *node = (Node *) handle;
  1.1180 +	Node *zReference = (Node *) hZReference;
  1.1181 +	Node *yReference = (Node *) hYReference;
  1.1182 +    M3G_VALIDATE_OBJECT(node);
  1.1183 +
  1.1184 +    /* Check for errors */
  1.1185 +	if (!m3gInRange(zTarget, M3G_NONE, M3G_Z_AXIS) ||
  1.1186 +        !m3gInRange(yTarget, M3G_NONE, M3G_Z_AXIS)) {
  1.1187 +		m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE);
  1.1188 +        return;
  1.1189 +	}
  1.1190 +
  1.1191 +	if (zReference == node || yReference == node) {
  1.1192 +		m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE);
  1.1193 +        return;
  1.1194 +	}
  1.1195 +    
  1.1196 +	if (zReference == yReference && zTarget == yTarget && zTarget != M3G_NONE) {
  1.1197 +		m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE);
  1.1198 +        return;
  1.1199 +	}
  1.1200 +
  1.1201 +    node->zReference = (zTarget != M3G_NONE) ? zReference : NULL;
  1.1202 +    node->yReference = (yTarget != M3G_NONE) ? yReference : NULL;
  1.1203 +	node->zTarget = internalTarget(zTarget);
  1.1204 +	node->yTarget = internalTarget(yTarget);
  1.1205 +}
  1.1206 +
  1.1207 +/*!
  1.1208 + * \brief Aligns a node.
  1.1209 + *
  1.1210 + * \param hNode             Node object
  1.1211 + * \param hRef              reference Node object
  1.1212 + */
  1.1213 +M3G_API void m3gAlignNode(M3GNode hNode, M3GNode hRef)
  1.1214 +{
  1.1215 +    Node *node = (Node *)hNode;
  1.1216 +    const Node *ref = (const Node *)hRef;
  1.1217 +    M3G_VALIDATE_OBJECT(node);
  1.1218 +    
  1.1219 +    if (ref != NULL && (m3gGetRoot(node) != m3gGetRoot(ref))) {
  1.1220 +        m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE);
  1.1221 +    }
  1.1222 +    else {
  1.1223 +        M3G_VFUNC(Node, node, align)(node, !ref ? node : ref);
  1.1224 +    }
  1.1225 +}
  1.1226 +
  1.1227 +/*!
  1.1228 + * \brief Sets node alpha factor.
  1.1229 + *
  1.1230 + * \param handle            Node object
  1.1231 + * \param alphaFactor       node alpha factor
  1.1232 + */
  1.1233 +M3G_API void m3gSetAlphaFactor(M3GNode handle, M3Gfloat alphaFactor)
  1.1234 +{
  1.1235 +    Node *node = (Node *) handle;
  1.1236 +    M3G_VALIDATE_OBJECT(node);
  1.1237 +
  1.1238 +    if (alphaFactor >= 0.f && alphaFactor <= 1.0f) {
  1.1239 +		node->alphaFactor = (M3Guint)
  1.1240 +            m3gRoundToInt(m3gMul(alphaFactor,
  1.1241 +                                 (1 << NODE_ALPHA_FACTOR_BITS) - 1));
  1.1242 +	}
  1.1243 +	else {
  1.1244 +	    m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE);
  1.1245 +	}
  1.1246 +}
  1.1247 +
  1.1248 +
  1.1249 +/*!
  1.1250 + * \brief Gets node alpha factor.
  1.1251 + *
  1.1252 + * \param handle            Node object
  1.1253 + * \return                  node alpha factor
  1.1254 + */
  1.1255 +M3G_API M3Gfloat m3gGetAlphaFactor(M3GNode handle)
  1.1256 +{
  1.1257 +    Node *node = (Node *) handle;
  1.1258 +    M3G_VALIDATE_OBJECT(node);
  1.1259 +
  1.1260 +	return m3gMul((M3Gfloat) node->alphaFactor,
  1.1261 +                  1.f / ((1 << NODE_ALPHA_FACTOR_BITS) - 1));
  1.1262 +}
  1.1263 +
  1.1264 +/*!
  1.1265 + * \brief Sets node redering or picking enable flag.
  1.1266 + *
  1.1267 + * \param handle            Node object
  1.1268 + * \param which             which flag to enable
  1.1269 + *                          \arg M3G_SETGET_RENDERING
  1.1270 + *                          \arg M3G_SETGET_PICKING
  1.1271 + * \param enable            enable flag
  1.1272 + */
  1.1273 +M3G_API void m3gEnable(M3GNode handle, M3Gint which, M3Gbool enable)
  1.1274 +{
  1.1275 +    Node *node = (Node *) handle;
  1.1276 +    M3G_VALIDATE_OBJECT(node);
  1.1277 +
  1.1278 +	switch (which) {
  1.1279 +		case M3G_SETGET_RENDERING:
  1.1280 +            node->enableBits &= ~NODE_RENDER_BIT;
  1.1281 +            if (enable) {
  1.1282 +                node->enableBits |= NODE_RENDER_BIT;
  1.1283 +            }
  1.1284 +            break;
  1.1285 +		case M3G_SETGET_PICKING:
  1.1286 +		default:
  1.1287 +            node->enableBits &= ~NODE_PICK_BIT;
  1.1288 +            if (enable) {
  1.1289 +                node->enableBits |= NODE_PICK_BIT;
  1.1290 +            }
  1.1291 +            break;
  1.1292 +	}
  1.1293 +}
  1.1294 +
  1.1295 +/*!
  1.1296 + * \brief Gets node redering or picking enable flag.
  1.1297 + *
  1.1298 + * \param handle            Node object
  1.1299 + * \param which             which flag to return
  1.1300 + *                          \arg M3G_SETGET_RENDERING
  1.1301 + *                          \arg M3G_SETGET_PICKING
  1.1302 + * \return                  enable flag
  1.1303 + */
  1.1304 +M3G_API M3Gint m3gIsEnabled(M3GNode handle, M3Gint which)
  1.1305 +{
  1.1306 +    Node *node = (Node *) handle;
  1.1307 +    M3G_VALIDATE_OBJECT(node);
  1.1308 +
  1.1309 +	switch(which) {
  1.1310 +		case M3G_SETGET_RENDERING:
  1.1311 +            return (node->enableBits & NODE_RENDER_BIT) != 0;
  1.1312 +		case M3G_SETGET_PICKING:
  1.1313 +		default:
  1.1314 +            return (node->enableBits & NODE_PICK_BIT) != 0;
  1.1315 +	}
  1.1316 +}
  1.1317 +
  1.1318 +/*!
  1.1319 + * \brief Sets node scope.
  1.1320 + *
  1.1321 + * \param handle            Node object
  1.1322 + * \param id                node scope id
  1.1323 + */
  1.1324 +M3G_API void m3gSetScope(M3GNode handle, M3Gint id)
  1.1325 +{
  1.1326 +    Node *node = (Node *) handle;
  1.1327 +    M3G_VALIDATE_OBJECT(node);
  1.1328 +
  1.1329 +	node->scope = id;
  1.1330 +}
  1.1331 +
  1.1332 +/*!
  1.1333 + * \brief Gets node scope.
  1.1334 + *
  1.1335 + * \param handle            Node object
  1.1336 + * \return                  node scope
  1.1337 + */
  1.1338 +M3G_API M3Gint m3gGetScope(M3GNode handle)
  1.1339 +{
  1.1340 +    Node *node = (Node *) handle;
  1.1341 +    M3G_VALIDATE_OBJECT(node);
  1.1342 +
  1.1343 +	return node->scope;
  1.1344 +}
  1.1345 +
  1.1346 +/*!
  1.1347 + * \brief Gets node parent.
  1.1348 + *
  1.1349 + * \param handle            Node object
  1.1350 + * \return                  parent Node object
  1.1351 + */
  1.1352 +M3G_API M3GNode m3gGetParent(M3GNode handle)
  1.1353 +{
  1.1354 +    Node *node = (Node *) handle;
  1.1355 +    M3G_VALIDATE_OBJECT(node);
  1.1356 +
  1.1357 +	return node->parent;
  1.1358 +}
  1.1359 +
  1.1360 +/*!
  1.1361 + * \brief Gets node alignment Z reference.
  1.1362 + *
  1.1363 + * \param handle            Node object
  1.1364 + * \return                  Z reference Node object
  1.1365 + */
  1.1366 +M3G_API M3GNode m3gGetZRef(M3GNode handle)
  1.1367 +{
  1.1368 +    Node *node = (Node *) handle;
  1.1369 +    M3G_VALIDATE_OBJECT(node);
  1.1370 +
  1.1371 +	return node->zReference;
  1.1372 +}
  1.1373 +
  1.1374 +/*!
  1.1375 + * \brief Gets node alignment Y reference.
  1.1376 + *
  1.1377 + * \param handle            Node object
  1.1378 + * \return                  Y reference Node object
  1.1379 + */
  1.1380 +M3G_API M3GNode m3gGetYRef(M3GNode handle)
  1.1381 +{
  1.1382 +    Node *node = (Node *) handle;
  1.1383 +    M3G_VALIDATE_OBJECT(node);
  1.1384 +
  1.1385 +	return node->yReference;
  1.1386 +}
  1.1387 +
  1.1388 +/*!
  1.1389 + * \brief Gets node alignment target
  1.1390 + *
  1.1391 + * \param handle            Node object
  1.1392 + * \param axis              axis
  1.1393 + * \return                  alignment target
  1.1394 + */
  1.1395 +M3G_API M3Gint m3gGetAlignmentTarget(M3GNode handle, M3Gint axis)
  1.1396 +{
  1.1397 +    Node *node = (Node *) handle;
  1.1398 +    M3G_VALIDATE_OBJECT(node);
  1.1399 +
  1.1400 +    switch (axis) {
  1.1401 +    case M3G_Y_AXIS: return externalTarget(node->yTarget);
  1.1402 +    case M3G_Z_AXIS: return externalTarget(node->zTarget);
  1.1403 +    default:
  1.1404 +        m3gRaiseError(M3G_INTERFACE(node), M3G_INVALID_VALUE);
  1.1405 +        return 0;
  1.1406 +    }
  1.1407 +}
  1.1408 +
  1.1409 +/*!
  1.1410 + * \brief Gets node subtree size.
  1.1411 + *
  1.1412 + * \param handle            Node object
  1.1413 + * \return                  subtree size
  1.1414 + */
  1.1415 +M3G_API M3Gint m3gGetSubtreeSize(M3GNode handle)
  1.1416 +{
  1.1417 +    M3Gint numRef = 0;
  1.1418 +    Node *node = (Node *) handle;
  1.1419 +    M3G_VALIDATE_OBJECT(node);
  1.1420 +
  1.1421 +    m3gForSubtree(node, m3gDoGetSubtreeSize, (void *)&numRef);
  1.1422 +    return numRef;
  1.1423 +}
  1.1424 +
  1.1425 +#undef TARGET_ORIGIN
  1.1426 +#undef TARGET_Z_AXIS
  1.1427 +#undef TARGET_Y_AXIS
  1.1428 +#undef TARGET_X_AXIS
  1.1429 +#undef TARGET_NONE
  1.1430 +