os/graphics/m3g/m3gcore11/src/m3g_group.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_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 +