1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/graphics/m3g/m3gcore11/src/m3g_group.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,1067 @@
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: Group implementation
1.18 +*
1.19 +*/
1.20 +
1.21 +
1.22 +/*!
1.23 + * \internal
1.24 + * \file
1.25 + * \brief Group 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_group.h"
1.33 +#include "m3g_memory.h"
1.34 +
1.35 +/*----------------------------------------------------------------------
1.36 + * Internal functions
1.37 + *--------------------------------------------------------------------*/
1.38 +
1.39 +/*!
1.40 + * \internal
1.41 + * \brief Links a new child into the child list of this node.
1.42 + *
1.43 + * This assumes that all error checking has been done prior to calling
1.44 + * the function, and the operation is a valid one.
1.45 + *
1.46 + * \param child Node object
1.47 + * \param group Group object
1.48 + */
1.49 +static void m3gLinkChild(Node *child, Group *group)
1.50 +{
1.51 + M3G_VALIDATE_OBJECT(child);
1.52 + M3G_VALIDATE_OBJECT(group);
1.53 +
1.54 + if (group->firstChild == NULL) {
1.55 + group->firstChild = child;
1.56 + child->left = child;
1.57 + child->right = child;
1.58 + }
1.59 + else {
1.60 + Node *linkChild = group->firstChild;
1.61 +
1.62 + child->left = linkChild->left;
1.63 + linkChild->left->right = child;
1.64 +
1.65 + child->right = linkChild;
1.66 + linkChild->left = child;
1.67 + }
1.68 + m3gSetParent(child, (Node *) group);
1.69 +}
1.70 +
1.71 +/*!
1.72 + * \internal
1.73 + * \brief Removes a child from the child list of this node.
1.74 + *
1.75 + * This assumes that all error checking has been done prior to calling
1.76 + * the function, and the operation is a valid one.
1.77 + *
1.78 + * \param child Node object
1.79 + * \param group Group object
1.80 + */
1.81 +static void m3gDetachChild(Node *child, Group *group)
1.82 +{
1.83 + Node *n;
1.84 + M3G_VALIDATE_OBJECT(child);
1.85 + M3G_VALIDATE_OBJECT(group);
1.86 +
1.87 + n = group->firstChild;
1.88 +
1.89 + do {
1.90 + if (n == child) {
1.91 + M3G_VALIDATE_OBJECT(child->right);
1.92 + M3G_VALIDATE_OBJECT(child->left);
1.93 +
1.94 + n->right->left = n->left;
1.95 + n->left->right = n->right;
1.96 +
1.97 + if (group->firstChild == n) {
1.98 + group->firstChild = (n->right != n) ? n->right : NULL;
1.99 + }
1.100 +
1.101 + n->left = NULL;
1.102 + n->right = NULL;
1.103 + m3gSetParent(n, NULL);
1.104 + return;
1.105 + }
1.106 + n = n->right;
1.107 + } while (n != group->firstChild);
1.108 +}
1.109 +
1.110 +/*!
1.111 + * \internal
1.112 + * \brief Destroys this Group object.
1.113 + *
1.114 + * \param obj Group object
1.115 + */
1.116 +static void m3gDestroyGroup(Object *obj)
1.117 +{
1.118 + /* Release child references so they can be deleted */
1.119 +
1.120 + Group *group = (Group *) obj;
1.121 + while (group->firstChild != NULL) {
1.122 + m3gDetachChild(group->firstChild, group);
1.123 + }
1.124 +# if defined(M3G_ENABLE_VF_CULLING)
1.125 + if (group->bbox) {
1.126 + m3gFree(M3G_INTERFACE(group), group->bbox);
1.127 + m3gIncStat(M3G_INTERFACE(group), M3G_STAT_BOUNDING_BOXES, -1);
1.128 + }
1.129 +# endif
1.130 + m3gDestroyNode(obj);
1.131 +}
1.132 +
1.133 +/*!
1.134 + * \internal
1.135 + * \brief Overloaded Node method.
1.136 + *
1.137 + * \param self Group object
1.138 + * \param refNode alignment reference Node object
1.139 + *
1.140 + * \retval M3G_TRUE continue align
1.141 + * \retval M3G_FALSE abort align
1.142 + */
1.143 +static M3Gbool m3gGroupAlign(Node *self, const Node *refNode)
1.144 +{
1.145 + Group *group = (Group *)self;
1.146 + Node *child = group->firstChild;
1.147 +
1.148 + if (!m3gNodeAlign(self, refNode)) {
1.149 + return M3G_FALSE;
1.150 + }
1.151 +
1.152 + if (child) {
1.153 + do {
1.154 + if (!M3G_VFUNC(Node, child, align)(child, refNode)) {
1.155 + return M3G_FALSE;
1.156 + }
1.157 + child = child->right;
1.158 + } while (child != group->firstChild);
1.159 + }
1.160 +
1.161 + return M3G_TRUE;
1.162 +}
1.163 +
1.164 +/*!
1.165 + * \internal
1.166 + * \brief Overloaded Node method.
1.167 + *
1.168 + * Setup group rendering by calling child
1.169 + * nodes' render setup.
1.170 + *
1.171 + * \param self Group object
1.172 + * \param toCamera transform to camera
1.173 + * \param alphaFactor total alpha factor
1.174 + * \param caller caller node
1.175 + * \param renderQueue RenderQueue
1.176 + *
1.177 + * \retval M3G_TRUE continue render setup
1.178 + * \retval M3G_FALSE abort render setup
1.179 + */
1.180 +static M3Gbool m3gGroupSetupRender(Node *self,
1.181 + const Node *caller,
1.182 + SetupRenderState *s,
1.183 + RenderQueue *renderQueue)
1.184 +{
1.185 + Group *group = (Group *)self;
1.186 + M3Gbool enabled, success = M3G_TRUE;
1.187 +
1.188 + /* Check whether we're going up or down, and optimize the
1.189 + * rendering-enabled and visibility checking based on that */
1.190 +
1.191 + enabled = (self->enableBits & NODE_RENDER_BIT) != 0;
1.192 + if (caller != self->parent) {
1.193 + enabled = m3gHasEnabledPath(self, renderQueue->root);
1.194 + s->cullMask = CULLMASK_ALL;
1.195 + }
1.196 + M3G_ASSERT(!self->dirtyBits || !enabled);
1.197 +
1.198 + /* First do the child nodes, unless disabled (inheritable, so
1.199 + * children would be, too) */
1.200 +
1.201 + if (enabled && (group->numNonCullables > 0 || group->numRenderables > 0)) {
1.202 +
1.203 + Node *child = group->firstChild;
1.204 + if (child) {
1.205 +
1.206 + /* Check the bounding box if we have one */
1.207 +
1.208 +# if defined(M3G_ENABLE_VF_CULLING)
1.209 + if (group->bbox) {
1.210 + m3gValidateAABB(group->bbox);
1.211 + m3gUpdateCullingMask(s, renderQueue->camera, group->bbox);
1.212 + }
1.213 +# endif
1.214 +
1.215 + /* If we're not culled, or if we carry lights, we really
1.216 + * need to recurse into each child node */
1.217 +
1.218 + if (s->cullMask || group->numNonCullables > 0) {
1.219 + do {
1.220 + if (child != caller) {
1.221 + SetupRenderState cs;
1.222 + cs.cullMask = s->cullMask;
1.223 +
1.224 + M3G_BEGIN_PROFILE(M3G_INTERFACE(group),
1.225 + M3G_PROFILE_SETUP_TRANSFORMS);
1.226 + m3gGetCompositeNodeTransform(child, &cs.toCamera);
1.227 + m3gPreMultiplyMatrix(&cs.toCamera, &s->toCamera);
1.228 + M3G_END_PROFILE(M3G_INTERFACE(group),
1.229 + M3G_PROFILE_SETUP_TRANSFORMS);
1.230 +
1.231 + if (!M3G_VFUNC(Node, child, setupRender)(
1.232 + child, self, &cs, renderQueue)) {
1.233 + return M3G_FALSE;
1.234 + }
1.235 + }
1.236 + child = child->right;
1.237 + } while (child != group->firstChild);
1.238 + }
1.239 + else {
1.240 + M3GInterface m3g = M3G_INTERFACE(group);
1.241 + M3Gint n = group->numRenderables;
1.242 + m3gIncStat(m3g, M3G_STAT_RENDER_NODES, n);
1.243 + m3gIncStat(m3g, M3G_STAT_RENDER_NODES_CULLED, n);
1.244 + }
1.245 + }
1.246 + }
1.247 +
1.248 + /* Then do the parent node if we're going up the tree. Again, we
1.249 + * can discard the old traversal state at this point. */
1.250 +
1.251 + if (self != renderQueue->root) {
1.252 + Node *parent = self->parent;
1.253 +
1.254 + if (parent != caller && parent != NULL) {
1.255 + Matrix t;
1.256 +
1.257 + M3G_BEGIN_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
1.258 + if (!m3gGetInverseNodeTransform(self, &t)) {
1.259 + return M3G_FALSE;
1.260 + }
1.261 + m3gMulMatrix(&s->toCamera, &t);
1.262 + M3G_END_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
1.263 +
1.264 + success = M3G_VFUNC(Node, parent, setupRender)(parent,
1.265 + self,
1.266 + s,
1.267 + renderQueue);
1.268 + }
1.269 + }
1.270 +
1.271 + return success;
1.272 +}
1.273 +
1.274 +/*!
1.275 + * \internal
1.276 + * \brief Overloaded Object3D method.
1.277 + *
1.278 + * \param self Group object
1.279 + * \param time current world time
1.280 + * \return minimum validity
1.281 + */
1.282 +static M3Gint m3gGroupApplyAnimation(Object *self, M3Gint time)
1.283 +{
1.284 + M3Gint validity, minValidity;
1.285 + Node *child;
1.286 + Group *group = (Group *)self;
1.287 + M3G_VALIDATE_OBJECT(group);
1.288 +
1.289 + minValidity = m3gObjectApplyAnimation(self, time);
1.290 +
1.291 + child = group->firstChild;
1.292 + if (child && minValidity > 0) {
1.293 + do {
1.294 + validity = M3G_VFUNC(Object, child, applyAnimation)(
1.295 + (Object *)child, time);
1.296 + minValidity = validity < minValidity ? validity : minValidity;
1.297 + child = child->right;
1.298 + } while (minValidity > 0 && child != group->firstChild);
1.299 + }
1.300 + return minValidity;
1.301 +}
1.302 +
1.303 +/*!
1.304 + * \internal
1.305 + * \brief Overloaded Node method.
1.306 + *
1.307 + * Call child nodes' ray intersect.
1.308 + *
1.309 + * \param self Group object
1.310 + * \param mask pick scope mask
1.311 + * \param ray pick ray
1.312 + * \param ri RayIntersection object
1.313 + * \param toGroup transform to originating group
1.314 + * \retval M3G_TRUE continue pick
1.315 + * \retval M3G_FALSE abort pick
1.316 + */
1.317 +static M3Gbool m3gGroupRayIntersect(Node *self,
1.318 + M3Gint mask,
1.319 + M3Gfloat *ray,
1.320 + RayIntersection *ri,
1.321 + Matrix *toGroup)
1.322 +{
1.323 + Group *group = (Group *)self;
1.324 + Node *child;
1.325 + Matrix t, nt;
1.326 +
1.327 + m3gIdentityMatrix(&t);
1.328 + m3gIdentityMatrix(&nt);
1.329 +
1.330 + child = group->firstChild;
1.331 + if (child) {
1.332 + do {
1.333 + if (m3gHasPickablePath(child, ri->root)) {
1.334 + m3gCopyMatrix(&t, toGroup);
1.335 + m3gGetCompositeNodeTransform(child, &nt);
1.336 + m3gRightMulMatrix(&t, &nt);
1.337 +
1.338 + if (!M3G_VFUNC(Node, child, rayIntersect)(
1.339 + child, mask, ray, ri, &t)) {
1.340 + return M3G_FALSE;
1.341 + }
1.342 + }
1.343 + child = child->right;
1.344 + } while (child != group->firstChild);
1.345 + }
1.346 +
1.347 + return M3G_TRUE;
1.348 +}
1.349 +
1.350 +/*!
1.351 + * \internal
1.352 + * \brief Initializes pick traversing.
1.353 + *
1.354 + * \param ri RayIntersection object
1.355 + * \param root Root node for the traversing
1.356 + * \param camera Camera object used in pick (2D pick only)
1.357 + * \param x viewport x (2D pick only)
1.358 + * \param y viewport y (2D pick only)
1.359 + */
1.360 +static void m3gInitPick(RayIntersection *ri, Node *root, Camera *camera, M3Gfloat x, M3Gfloat y)
1.361 +{
1.362 + m3gZero(ri, sizeof(*ri));
1.363 +
1.364 + ri->root = root;
1.365 + ri->camera = camera;
1.366 + ri->x = x;
1.367 + ri->y = y;
1.368 + ri->tMin = M3G_MAX_POSITIVE_FLOAT;
1.369 +}
1.370 +
1.371 +/*!
1.372 + * \internal
1.373 + * \brief Fills Java side RayIntersection result.
1.374 + *
1.375 + * \param ri RayIntersection object
1.376 + * \param ray Ray used in pick
1.377 + * \param result Java side float array
1.378 + */
1.379 +static void m3gFillPickResult(RayIntersection *ri, M3Gfloat *ray, M3Gfloat *result)
1.380 +{
1.381 + if (ri->intersected != NULL) {
1.382 + Vec3 n;
1.383 +
1.384 + /* Fill in the values */
1.385 + result[0] = ri->distance;
1.386 + result[1] = (M3Gfloat)ri->submeshIndex;
1.387 + result[2] = ri->textureS[0];
1.388 + result[3] = ri->textureS[1];
1.389 + result[4] = ri->textureT[0];
1.390 + result[5] = ri->textureT[1];
1.391 +
1.392 + /* Normalize normal */
1.393 + n.x = ri->normal[0];
1.394 + n.y = ri->normal[1];
1.395 + n.z = ri->normal[2];
1.396 + m3gNormalizeVec3(&n);
1.397 +
1.398 + result[6] = n.x;
1.399 + result[7] = n.y;
1.400 + result[8] = n.z;
1.401 +
1.402 + result[9] = ray[0];
1.403 + result[10] = ray[1];
1.404 + result[11] = ray[2];
1.405 + result[12] = m3gSub(ray[3], ray[0]);
1.406 + result[13] = m3gSub(ray[4], ray[1]);
1.407 + result[14] = m3gSub(ray[5], ray[2]);
1.408 + }
1.409 +}
1.410 +
1.411 +/*!
1.412 + * \internal
1.413 + * \brief Overloaded Object3D method.
1.414 + *
1.415 + * \param self Group object
1.416 + * \param references array of reference objects
1.417 + * \return number of references
1.418 + */
1.419 +static M3Gint m3gGroupDoGetReferences(Object *self, Object **references)
1.420 +{
1.421 + Group *group = (Group *)self;
1.422 + M3Gint num = m3gObjectDoGetReferences(self, references);
1.423 + Node *child = group->firstChild;
1.424 + if (child) {
1.425 + do {
1.426 + if (references != NULL)
1.427 + references[num] = (Object *)child;
1.428 + child = child->right;
1.429 + num++;
1.430 + } while (child != group->firstChild);
1.431 + }
1.432 + return num;
1.433 +}
1.434 +
1.435 +/*!
1.436 + * \internal
1.437 + * \brief Overloaded Object3D method.
1.438 + *
1.439 + * \param self Group object
1.440 + * \param references array of reference objects
1.441 + * \return number of references
1.442 + */
1.443 +static Object *m3gGroupFindID(Object *self, M3Gint userID)
1.444 +{
1.445 + Group *group = (Group *)self;
1.446 + Object *found = m3gObjectFindID(self, userID);
1.447 +
1.448 + Node *child = group->firstChild;
1.449 + if (child && !found) {
1.450 + do {
1.451 + found = m3gFindID((Object*) child, userID);
1.452 + child = child->right;
1.453 + } while (!found && child != group->firstChild);
1.454 + }
1.455 + return found;
1.456 +}
1.457 +
1.458 +/*!
1.459 + * \internal
1.460 + * \brief Overloaded Object3D method.
1.461 + *
1.462 + * \param originalObj original Group object
1.463 + * \param cloneObj pointer to cloned Group object
1.464 + * \param pairs array for all object-duplicate pairs
1.465 + * \param numPairs number of pairs
1.466 + */
1.467 +static M3Gbool m3gGroupDuplicate(const Object *originalObj,
1.468 + Object **cloneObj,
1.469 + Object **pairs,
1.470 + M3Gint *numPairs)
1.471 +{
1.472 + Node *child;
1.473 + Group *original = (Group *)originalObj;
1.474 + Group *clone;
1.475 +
1.476 + /* Create the clone object, unless already created in a derived
1.477 + * class function */
1.478 +
1.479 + if (*cloneObj == NULL) {
1.480 + clone = (Group *)m3gCreateGroup(originalObj->interface);
1.481 + if (!clone) {
1.482 + return M3G_FALSE; /* out of memory */
1.483 + }
1.484 + *cloneObj = (Object *)clone;
1.485 + }
1.486 + else {
1.487 + clone = (Group *)*cloneObj;
1.488 + }
1.489 +
1.490 + /* Call base class function to duplicate base class data */
1.491 +
1.492 + if (!m3gNodeDuplicate(originalObj, cloneObj, pairs, numPairs)) {
1.493 + return M3G_FALSE; /* out of memory; caller will delete us */
1.494 + }
1.495 +
1.496 + /* Duplicate child nodes. */
1.497 +
1.498 + child = original->firstChild;
1.499 + if (child) {
1.500 + do {
1.501 + Node *temp = NULL;
1.502 + if (!M3G_VFUNC(Object, child, duplicate)(
1.503 + (Object *)child, (Object**)&temp, pairs, numPairs)) {
1.504 + m3gDeleteObject((Object*) temp); /* we have the only reference */
1.505 + return M3G_FALSE;
1.506 + }
1.507 + m3gAddChild(clone, temp);
1.508 + child = child->right;
1.509 + } while (child != original->firstChild);
1.510 + }
1.511 +
1.512 + return M3G_TRUE;
1.513 +}
1.514 +
1.515 +/*!
1.516 + * \internal
1.517 + * \brief Overloaded Node method
1.518 + */
1.519 +static M3Gint m3gGroupGetBBox(Node *self, AABB *bbox)
1.520 +{
1.521 + Group *group = (Group*) self;
1.522 +
1.523 + /* Quick exit for empty volumes */
1.524 +
1.525 + if (!group->firstChild || !self->hasRenderables) {
1.526 + return 0;
1.527 + }
1.528 +
1.529 + /* Assume our existing bbox is ok, but compute a new one if it
1.530 + * isn't */
1.531 +
1.532 + if (group->bbox && !(self->dirtyBits & NODE_BBOX_BIT)) {
1.533 + *bbox = *group->bbox;
1.534 + }
1.535 + else {
1.536 +
1.537 + /* Compute local bounding box by recursively merging the
1.538 + * bounding boxes of all renderable child nodes */
1.539 +
1.540 + Node *child = group->firstChild;
1.541 + M3Gint groupYield = 0;
1.542 +
1.543 + do {
1.544 + if (child->hasRenderables && child->enableBits) {
1.545 +
1.546 + /* Get the transformation for the child node, then
1.547 + * update our existing state with its bounding box */
1.548 +
1.549 + AABB childBBox;
1.550 + M3Gint childYield;
1.551 + Matrix t;
1.552 +
1.553 + childYield = m3gGetNodeBBox(child, &childBBox);
1.554 + if (childYield > 0) {
1.555 + m3gGetCompositeNodeTransform(child, &t);
1.556 + m3gTransformAABB(&childBBox, &t);
1.557 +
1.558 + if (groupYield) {
1.559 + m3gFitAABB(bbox, &childBBox, bbox);
1.560 + }
1.561 + else {
1.562 + *bbox = childBBox;
1.563 + }
1.564 + groupYield += childYield;
1.565 + }
1.566 + }
1.567 + child = child->right;
1.568 + } while (child != group->firstChild);
1.569 +
1.570 + /* Store the updated bbox locally if we have one, or return
1.571 + * the combined child yield factor if we don't */
1.572 +
1.573 + if (group->bbox) {
1.574 + *group->bbox = *bbox;
1.575 + }
1.576 + else {
1.577 + return (groupYield > 0) ? groupYield + VFC_NODE_OVERHEAD : 0;
1.578 + }
1.579 + }
1.580 + return VFC_BBOX_COST + VFC_NODE_OVERHEAD;
1.581 +}
1.582 +
1.583 +/*!
1.584 + * \internal
1.585 + * \brief Overloaded Node method
1.586 + */
1.587 +static M3Gbool m3gGroupValidate(Node *self, M3Gbitmask stateBits, M3Gint scope)
1.588 +{
1.589 + Group *group = (Group*) self;
1.590 +
1.591 + if (stateBits & self->enableBits) {
1.592 +
1.593 + /* First validate child nodes to ensure we don't skip anything,
1.594 + * and allow children to invalidate our state */
1.595 +
1.596 + Node *child = group->firstChild;
1.597 + if (child) {
1.598 + do {
1.599 + if (!m3gValidateNode(child, stateBits, scope)) {
1.600 + return M3G_FALSE;
1.601 + }
1.602 + child = child->right;
1.603 + } while (child != group->firstChild);
1.604 + }
1.605 +
1.606 + /* Re-evaluate our local bounding box if necessary */
1.607 +
1.608 + if (self->hasRenderables && self->dirtyBits & NODE_BBOX_BIT) {
1.609 + AABB bbox;
1.610 + M3Gint yield;
1.611 + M3G_BEGIN_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_VFC_UPDATE);
1.612 +
1.613 + yield = m3gGetNodeBBox(self, &bbox);
1.614 +
1.615 + /* Think about adding a bounding box if we don't yet have one,
1.616 + * or removing the current one if it doesn't seem worth it */
1.617 +
1.618 + if (!group->bbox) {
1.619 + if (yield > (3*VFC_BBOX_COST) >> 1) {
1.620 + group->bbox = m3gAlloc(M3G_INTERFACE(group),
1.621 + sizeof(*group->bbox));
1.622 + if (group->bbox) {
1.623 + m3gIncStat(M3G_INTERFACE(group),
1.624 + M3G_STAT_BOUNDING_BOXES, 1);
1.625 + *group->bbox = bbox;
1.626 + }
1.627 + else {
1.628 + return M3G_FALSE;
1.629 + }
1.630 + }
1.631 + }
1.632 + else if (yield <= VFC_BBOX_COST) {
1.633 + m3gFree(M3G_INTERFACE(group), group->bbox);
1.634 + group->bbox = NULL;
1.635 + m3gIncStat(M3G_INTERFACE(group), M3G_STAT_BOUNDING_BOXES, -1);
1.636 + }
1.637 + M3G_END_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_VFC_UPDATE);
1.638 + }
1.639 + return m3gNodeValidate(self, stateBits, scope);
1.640 + }
1.641 + return M3G_TRUE;
1.642 +}
1.643 +
1.644 +/*!
1.645 + * \internal
1.646 + * \brief Overloaded Node method.
1.647 + *
1.648 + * \param self Group object
1.649 + * \param pairs array for all object-duplicate pairs
1.650 + * \param numPairs number of pairs
1.651 + */
1.652 +static void m3gGroupUpdateDuplicateReferences(Node *self, Object **pairs, M3Gint numPairs)
1.653 +{
1.654 + Group *group = (Group *)self;
1.655 + Node *child = group->firstChild;
1.656 +
1.657 + m3gNodeUpdateDuplicateReferences(self, pairs, numPairs);
1.658 +
1.659 + if (child) {
1.660 + do {
1.661 + M3G_VFUNC(Node, child, updateDuplicateReferences)(
1.662 + child, pairs, numPairs);
1.663 + child = child->right;
1.664 + } while (child != group->firstChild);
1.665 + }
1.666 +}
1.667 +
1.668 +/*!
1.669 + * \internal
1.670 + * \brief Initializes a Group object. See specification
1.671 + * for default values.
1.672 + *
1.673 + * \param m3g M3G interface
1.674 + * \param group Group object
1.675 + * \param vfTable virtual function table
1.676 + */
1.677 +static void m3gInitGroup(Interface *m3g, Group *group, M3GClass classID)
1.678 +{
1.679 + /* Group is derived from Node */
1.680 + m3gInitNode(m3g, &group->node, classID);
1.681 +}
1.682 +
1.683 +/*----------------------------------------------------------------------
1.684 + * Virtual function table
1.685 + *--------------------------------------------------------------------*/
1.686 +
1.687 +static const NodeVFTable m3gvf_Group = {
1.688 + {
1.689 + {
1.690 + m3gGroupApplyAnimation,
1.691 + m3gNodeIsCompatible,
1.692 + m3gNodeUpdateProperty,
1.693 + m3gGroupDoGetReferences,
1.694 + m3gGroupFindID,
1.695 + m3gGroupDuplicate,
1.696 + m3gDestroyGroup
1.697 + }
1.698 + },
1.699 + m3gGroupAlign,
1.700 + NULL, /* pure virtual m3gNodeDoRender */
1.701 + m3gGroupGetBBox,
1.702 + m3gGroupRayIntersect,
1.703 + m3gGroupSetupRender,
1.704 + m3gGroupUpdateDuplicateReferences,
1.705 + m3gGroupValidate
1.706 +};
1.707 +
1.708 +
1.709 +/*----------------------------------------------------------------------
1.710 + * Public API functions
1.711 + *--------------------------------------------------------------------*/
1.712 +
1.713 +/*!
1.714 + * \brief Creates a Group object.
1.715 + *
1.716 + * \param interface M3G interface
1.717 + * \retval Group new Group object
1.718 + * \retval NULL Group creating failed
1.719 + */
1.720 +M3G_API M3GGroup m3gCreateGroup(M3GInterface interface)
1.721 +{
1.722 + Interface *m3g = (Interface *) interface;
1.723 + M3G_VALIDATE_INTERFACE(m3g);
1.724 +
1.725 + {
1.726 + Group *group = m3gAllocZ(m3g, sizeof(Group));
1.727 +
1.728 + if (group != NULL) {
1.729 + m3gInitGroup(m3g, group, M3G_CLASS_GROUP);
1.730 + }
1.731 +
1.732 + return (M3GGroup) group;
1.733 + }
1.734 +}
1.735 +
1.736 +/*!
1.737 + * \brief Adds a node to this group.
1.738 + *
1.739 + * \param handle Group object
1.740 + * \param hNode Node object
1.741 + */
1.742 +M3G_API void m3gAddChild(M3GGroup handle, M3GNode hNode)
1.743 +{
1.744 + Group *group = (Group *) handle;
1.745 + Node *child = (Node *) hNode;
1.746 +
1.747 + M3G_VALIDATE_OBJECT(group);
1.748 +
1.749 + if (child == NULL) {
1.750 + m3gRaiseError(M3G_INTERFACE(group), M3G_NULL_POINTER);
1.751 + return;
1.752 + }
1.753 +
1.754 + if (child == (Node *)group ||
1.755 + (child->parent != NULL && child->parent != (Node *)group) ||
1.756 + m3gIsChildOf(child, (Node *)group) ||
1.757 + m3gGetClass((Object *) child) == M3G_CLASS_WORLD) {
1.758 + m3gRaiseError(M3G_INTERFACE(group), M3G_INVALID_VALUE);
1.759 + return;
1.760 + }
1.761 +
1.762 + if (child->parent == NULL) {
1.763 + m3gLinkChild(child, group);
1.764 + }
1.765 +}
1.766 +
1.767 +/*!
1.768 + * \brief Removes a node from this group.
1.769 + *
1.770 + * \param handle Group object
1.771 + * \param hNode Node object
1.772 + */
1.773 +M3G_API void m3gRemoveChild(M3GGroup handle, M3GNode hNode)
1.774 +{
1.775 + Group *group = (Group *) handle;
1.776 + Node *child = (Node *)hNode;
1.777 + M3G_VALIDATE_OBJECT(group);
1.778 +
1.779 + if (child == NULL) {
1.780 + return;
1.781 + }
1.782 +
1.783 + if (child->hasBones == M3G_TRUE) {
1.784 + m3gRaiseError(M3G_INTERFACE(group), M3G_INVALID_VALUE);
1.785 + return;
1.786 + }
1.787 +
1.788 + if (group->firstChild == NULL) {
1.789 + return;
1.790 + }
1.791 +
1.792 + m3gDetachChild(child, group);
1.793 +}
1.794 +
1.795 +/*!
1.796 + * \brief Performs 3D pick.
1.797 + *
1.798 + * \param handle Group object
1.799 + * \param mask pick scope mask
1.800 + * \param ray pick ray
1.801 + * \arg ray[0] origin X
1.802 + * \arg ray[1] origin Y
1.803 + * \arg ray[2] origin Z
1.804 + * \arg ray[3] direction X
1.805 + * \arg ray[4] direction Y
1.806 + * \arg ray[5] direction Z
1.807 + * \param result java side RayIntersection result
1.808 + * \arg result[0] distance
1.809 + * \arg result[1] submesh index
1.810 + * \arg result[2] textureS[0]
1.811 + * \arg result[3] textureS[1]
1.812 + * \arg result[4] textureT[0]
1.813 + * \arg result[5] textureT[1]
1.814 + * \arg result[6] normal X
1.815 + * \arg result[7] normal Y
1.816 + * \arg result[8] normal Z
1.817 + * \arg result[9] ray ox
1.818 + * \arg result[10] ray oy
1.819 + * \arg result[11] ray oz
1.820 + * \arg result[12] ray dx
1.821 + * \arg result[13] ray dy
1.822 + * \arg result[14] ray dz
1.823 + * \return intersected Node object
1.824 + */
1.825 +
1.826 +#ifdef M3G_ENABLE_PROFILING
1.827 +static M3GNode m3gPick3DInternal(M3GGroup handle,
1.828 + M3Gint mask,
1.829 + M3Gfloat *ray,
1.830 + M3Gfloat *result);
1.831 +
1.832 +M3G_API M3GNode m3gPick3D(M3GGroup handle,
1.833 + M3Gint mask,
1.834 + M3Gfloat *ray,
1.835 + M3Gfloat *result)
1.836 +{
1.837 + M3GNode pickResult;
1.838 + M3G_BEGIN_PROFILE(M3G_INTERFACE(handle), M3G_PROFILE_PICK);
1.839 + pickResult = m3gPick3DInternal(handle, mask, ray, result);
1.840 + M3G_END_PROFILE(M3G_INTERFACE(handle), M3G_PROFILE_PICK);
1.841 + return pickResult;
1.842 +}
1.843 +
1.844 +static M3GNode m3gPick3DInternal(M3GGroup handle,
1.845 + M3Gint mask,
1.846 + M3Gfloat *ray,
1.847 + M3Gfloat *result)
1.848 +#else
1.849 +M3G_API M3GNode m3gPick3D(M3GGroup handle,
1.850 + M3Gint mask,
1.851 + M3Gfloat *ray,
1.852 + M3Gfloat *result)
1.853 +
1.854 +#endif
1.855 +{
1.856 + RayIntersection ri;
1.857 + Matrix toGroup;
1.858 + Group *group = (Group *) handle;
1.859 + M3G_VALIDATE_OBJECT(group);
1.860 +
1.861 + M3G_LOG1(M3G_LOG_STAGES, "Picking group 0x%08X\n", (unsigned) group);
1.862 +
1.863 + /* Check for errors */
1.864 + if (ray[3] == 0 && ray[4] == 0 && ray[5] == 0) {
1.865 + m3gRaiseError(M3G_INTERFACE(group), M3G_INVALID_VALUE);
1.866 + return NULL;
1.867 + }
1.868 + if (!m3gValidateNode((Node*) group, NODE_PICK_BIT, mask)) {
1.869 + return NULL;
1.870 + }
1.871 +
1.872 + m3gInitPick(&ri, (Node *)group, NULL, 0, 0);
1.873 + m3gIdentityMatrix(&toGroup);
1.874 +
1.875 + ray[3] = m3gAdd(ray[3], ray[0]);
1.876 + ray[4] = m3gAdd(ray[4], ray[1]);
1.877 + ray[5] = m3gAdd(ray[5], ray[2]);
1.878 +
1.879 + M3G_VFUNC(Node, group, rayIntersect)( (Node *)group,
1.880 + mask,
1.881 + ray,
1.882 + &ri,
1.883 + &toGroup);
1.884 + m3gFillPickResult(&ri, ray, result);
1.885 + return ri.intersected;
1.886 +}
1.887 +
1.888 +/*!
1.889 + * \brief Performs 2D pick.
1.890 + *
1.891 + * \param handle Group object
1.892 + * \param mask pick scope mask
1.893 + * \param x viewport x
1.894 + * \param y viewport y
1.895 + * \param hCamera Camera object
1.896 + * \param result java side RayIntersection result, see m3gPick3D
1.897 + * \return intersected Node object
1.898 + */
1.899 +
1.900 +#ifdef M3G_ENABLE_PROFILING
1.901 +static M3GNode m3gPick2DInternal(M3GGroup handle,
1.902 + M3Gint mask,
1.903 + M3Gfloat x, M3Gfloat y,
1.904 + M3GCamera hCamera,
1.905 + M3Gfloat *result);
1.906 +
1.907 +M3G_API M3GNode m3gPick2D(M3GGroup handle,
1.908 + M3Gint mask,
1.909 + M3Gfloat x, M3Gfloat y,
1.910 + M3GCamera hCamera,
1.911 + M3Gfloat *result)
1.912 +{
1.913 + M3GNode pickResult;
1.914 + M3G_BEGIN_PROFILE(M3G_INTERFACE(handle), M3G_PROFILE_PICK);
1.915 + pickResult = m3gPick2DInternal(handle, mask, x, y, hCamera, result);
1.916 + M3G_END_PROFILE(M3G_INTERFACE(handle), M3G_PROFILE_PICK);
1.917 + return pickResult;
1.918 +}
1.919 +
1.920 +static M3GNode m3gPick2DInternal(M3GGroup handle,
1.921 + M3Gint mask,
1.922 + M3Gfloat x, M3Gfloat y,
1.923 + M3GCamera hCamera,
1.924 + M3Gfloat *result)
1.925 +#else
1.926 +M3G_API M3GNode m3gPick2D(M3GGroup handle,
1.927 + M3Gint mask,
1.928 + M3Gfloat x, M3Gfloat y,
1.929 + M3GCamera hCamera,
1.930 + M3Gfloat *result)
1.931 +#endif
1.932 +{
1.933 + Vec4 farp, nearp;
1.934 + RayIntersection ri;
1.935 + Matrix toGroup;
1.936 + M3Gfloat ray[6 + 2]; /* Extra floats to store near and far plane z */
1.937 + Node *root;
1.938 + Group *group = (Group *) handle;
1.939 +
1.940 + M3G_LOG2(M3G_LOG_STAGES, "Picking group 0x%08X via camera 0x%08X\n",
1.941 + (unsigned) group, (unsigned) hCamera);
1.942 +
1.943 + M3G_VALIDATE_OBJECT(group);
1.944 +
1.945 + if (hCamera == 0) {
1.946 + m3gRaiseError(M3G_INTERFACE(group), M3G_NULL_POINTER);
1.947 + return NULL;
1.948 + }
1.949 +
1.950 + root = m3gGetRoot((Node *)hCamera);
1.951 +
1.952 + if (root != m3gGetRoot(&group->node)) {
1.953 + m3gRaiseError(M3G_INTERFACE(group), M3G_INVALID_OPERATION);
1.954 + return NULL;
1.955 + }
1.956 + if (!m3gValidateNode(root, NODE_PICK_BIT, mask)) {
1.957 + return NULL;
1.958 + }
1.959 +
1.960 + farp.x = m3gSub(m3gMul(2, x), 1.f);
1.961 + farp.y = m3gSub(1.f, m3gMul(2, y));
1.962 + farp.z = 1.f;
1.963 + farp.w = 1.f;
1.964 +
1.965 + nearp.x = farp.x;
1.966 + nearp.y = farp.y;
1.967 + nearp.z = -1.f;
1.968 + nearp.w = 1.f;
1.969 +
1.970 + m3gCopyMatrix(&toGroup, m3gProjectionMatrix((Camera *)hCamera));
1.971 +
1.972 + M3G_BEGIN_PROFILE(M3G_INTERFACE(group), M3G_PROFILE_TRANSFORM_INVERT);
1.973 + if (!m3gInvertMatrix(&toGroup)) {
1.974 + m3gRaiseError(M3G_INTERFACE(group), M3G_ARITHMETIC_ERROR);
1.975 + return NULL;
1.976 + }
1.977 + M3G_END_PROFILE(M3G_INTERFACE(group), M3G_PROFILE_TRANSFORM_INVERT);
1.978 +
1.979 + m3gTransformVec4(&toGroup, &nearp);
1.980 + m3gTransformVec4(&toGroup, &farp);
1.981 +
1.982 + m3gScaleVec4(&nearp, m3gRcp(nearp.w));
1.983 + m3gScaleVec4(&farp, m3gRcp(farp.w));
1.984 +
1.985 + /* Store near and far plane z for sprite picking */
1.986 + ray[6] = nearp.z;
1.987 + ray[7] = farp.z;
1.988 +
1.989 + if (!m3gGetTransformTo((M3GNode) hCamera, (Node *) group, &toGroup)) {
1.990 + return NULL;
1.991 + }
1.992 +
1.993 + m3gTransformVec4(&toGroup, &nearp);
1.994 + m3gTransformVec4(&toGroup, &farp);
1.995 +
1.996 + m3gScaleVec4(&nearp, m3gRcp(nearp.w));
1.997 + m3gScaleVec4(&farp, m3gRcp(farp.w));
1.998 +
1.999 + ray[0] = nearp.x;
1.1000 + ray[1] = nearp.y;
1.1001 + ray[2] = nearp.z;
1.1002 + ray[3] = farp.x;
1.1003 + ray[4] = farp.y;
1.1004 + ray[5] = farp.z;
1.1005 +
1.1006 +
1.1007 + m3gInitPick(&ri, (Node *)group, (Camera *)hCamera, x, y);
1.1008 + m3gIdentityMatrix(&toGroup);
1.1009 +
1.1010 + M3G_VFUNC(Node, group, rayIntersect)((Node *)group, mask, ray, &ri, &toGroup);
1.1011 +
1.1012 + m3gFillPickResult(&ri, ray, result);
1.1013 + return ri.intersected;
1.1014 +}
1.1015 +
1.1016 +/*!
1.1017 + * \brief Gets a child.
1.1018 + *
1.1019 + * \param handle Group object
1.1020 + * \param idx child index
1.1021 + * \return Node object
1.1022 + */
1.1023 +M3G_API M3GNode m3gGetChild(M3GGroup handle, M3Gint idx)
1.1024 +{
1.1025 + Node *n;
1.1026 + Group *group = (Group *) handle;
1.1027 + M3G_VALIDATE_OBJECT(group);
1.1028 +
1.1029 + if (idx < 0) {
1.1030 + goto InvalidIndex;
1.1031 + }
1.1032 +
1.1033 + n = group->firstChild;
1.1034 +
1.1035 + while (idx-- > 0) {
1.1036 + n = n->right;
1.1037 + if (n == group->firstChild) {
1.1038 + goto InvalidIndex;
1.1039 + }
1.1040 + }
1.1041 + return n;
1.1042 +
1.1043 + InvalidIndex:
1.1044 + m3gRaiseError(M3G_INTERFACE(group), M3G_INVALID_INDEX);
1.1045 + return NULL;
1.1046 +}
1.1047 +
1.1048 +/*!
1.1049 + * \brief Gets children count.
1.1050 + *
1.1051 + * \param handle Group object
1.1052 + * \return children count
1.1053 + */
1.1054 +M3G_API M3Gint m3gGetChildCount(M3GGroup handle)
1.1055 +{
1.1056 + Group *group = (Group *) handle;
1.1057 + M3G_VALIDATE_OBJECT(group);
1.1058 + {
1.1059 + M3Gint count = 0;
1.1060 + const Node *child = group->firstChild;
1.1061 + if (child) {
1.1062 + do {
1.1063 + ++count;
1.1064 + child = child->right;
1.1065 + } while (child != group->firstChild);
1.1066 + }
1.1067 + return count;
1.1068 + }
1.1069 +}
1.1070 +