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 +