os/graphics/m3g/m3gcore11/src/m3g_skinnedmesh.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_skinnedmesh.c	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,2074 @@
     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: SkinnedMesh implementation
    1.18 +*
    1.19 +*/
    1.20 +
    1.21 +
    1.22 +/*!
    1.23 + * \internal
    1.24 + * \file
    1.25 + * \brief SkinnedMesh 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_skinnedmesh.h"
    1.33 +#include "m3g_memory.h"
    1.34 +#include "m3g_animationtrack.h"
    1.35 +
    1.36 +/*----------------------------------------------------------------------
    1.37 + * Internal structures
    1.38 + *--------------------------------------------------------------------*/
    1.39 +
    1.40 +struct BoneRecord
    1.41 +{
    1.42 +    Node *node;
    1.43 +
    1.44 +    /*! \internal \brief "At-rest" transformation from skinned mesh to bone */
    1.45 +    Matrix toBone;
    1.46 +
    1.47 +    /*! \internal \brief Cached animated transformation for positions */
    1.48 +    M3Gshort baseMatrix[9];
    1.49 +    M3Gshort posVec[3];
    1.50 +    M3Gshort baseExp, posExp, maxExp;
    1.51 +
    1.52 +    /*! \internal \brief Cached animated transformation for normals */
    1.53 +    M3Gshort normalMatrix[9];
    1.54 +};
    1.55 +
    1.56 +/*----------------------------------------------------------------------
    1.57 + * Internal functions
    1.58 + *--------------------------------------------------------------------*/
    1.59 +
    1.60 +/*!
    1.61 + * \internal
    1.62 + * \brief Destroys this SkinnedMesh object.
    1.63 + *
    1.64 + * \param obj SkinnedMesh object
    1.65 + */
    1.66 +static void m3gDestroySkinnedMesh(Object *obj)
    1.67 +{
    1.68 +    SkinnedMesh *mesh = (SkinnedMesh *) obj;
    1.69 +    M3G_VALIDATE_OBJECT(mesh);
    1.70 +    {
    1.71 +        int i;
    1.72 +        Interface *m3g = M3G_INTERFACE(mesh);
    1.73 +        
    1.74 +        m3gDeleteVertexBuffer(mesh->morphedVB);
    1.75 +
    1.76 +        for (i = 0; i < mesh->bonesPerVertex; ++i) {
    1.77 +            m3gFree(m3g, mesh->boneIndices[i]);
    1.78 +            m3gFree(m3g, mesh->boneWeights[i]);
    1.79 +            m3gFree(m3g, mesh->normalizedWeights[i]);
    1.80 +        }
    1.81 +        m3gFree(m3g, mesh->weightShifts);
    1.82 +        
    1.83 +        for (i = 0; i < m3gArraySize(&mesh->bones); ++i) {
    1.84 +            m3gFree(m3g, m3gGetArrayElement(&mesh->bones, i));
    1.85 +        }
    1.86 +        m3gDestroyArray(&mesh->bones, m3g);
    1.87 +        
    1.88 +        if (mesh->skeleton != NULL) {
    1.89 +            m3gSetParent((Node*) mesh->skeleton, NULL);
    1.90 +            M3G_ASSIGN_REF(mesh->skeleton, NULL);
    1.91 +        }
    1.92 +    }
    1.93 +    m3gDestroyMesh(obj);
    1.94 +}
    1.95 +
    1.96 +
    1.97 +/*!
    1.98 + * \internal
    1.99 + * \brief Get a bone index for a given node
   1.100 + *
   1.101 + * This finds an existing record if the bone has been added
   1.102 + * previously, or creates a new one if no record exists yet.
   1.103 + *
   1.104 + * \note Inline because only called from AddTransform.
   1.105 + */
   1.106 +static M3G_INLINE M3Gint m3gBoneIndex(SkinnedMesh *mesh, Node *node)
   1.107 +{
   1.108 +    PointerArray *boneArray = &mesh->bones;
   1.109 +    const int numBones = m3gArraySize(boneArray);
   1.110 +    
   1.111 +    /* First look for an existing record in the array */
   1.112 +    {
   1.113 +        int i;
   1.114 +        
   1.115 +        for (i = 0; i < numBones; ++i) {
   1.116 +            Bone *b = m3gGetArrayElement(boneArray, i);
   1.117 +            if (b->node == node) {
   1.118 +                return i;
   1.119 +            }
   1.120 +        }
   1.121 +    }
   1.122 +
   1.123 +    /* Not found; create a new one, append to the array, and set up
   1.124 +     * the "at-rest" transformation for the bone. Note, however, that
   1.125 +     * we can only store a maximum of 256 bones with byte indices! */
   1.126 +    {
   1.127 +        Interface *m3g = M3G_INTERFACE(mesh);
   1.128 +        
   1.129 +        if (numBones >= 256) {
   1.130 +            /* Out of available bone indices */
   1.131 +            m3gRaiseError(m3g, M3G_OUT_OF_MEMORY);
   1.132 +            return -1;
   1.133 +        }
   1.134 +        else {
   1.135 +            M3Gint idx;
   1.136 +            Bone *bone = (Bone*) m3gAllocZ(m3g, sizeof(Bone));
   1.137 +            if (!bone || !m3gGetTransformTo((Node*) mesh, node,
   1.138 +                                            &bone->toBone)) {
   1.139 +                m3gFree(m3g, bone);
   1.140 +                return -1; /* out of memory or singular transform */
   1.141 +            }
   1.142 +            bone->node = node;
   1.143 +
   1.144 +            idx = m3gArrayAppend(boneArray, bone, m3g);
   1.145 +            if (idx < 0) {
   1.146 +                m3gFree(m3g, bone);
   1.147 +                return -1; /* out of memory */
   1.148 +            }
   1.149 +            return idx;
   1.150 +        }
   1.151 +    }
   1.152 +}
   1.153 +
   1.154 +/*!
   1.155 + * \internal
   1.156 + * \brief Reallocate the per-vertex data if necessary.
   1.157 + *
   1.158 + * \note Inline because only called from AddTransform.
   1.159 + */
   1.160 +static M3G_INLINE M3Gbool m3gEnsureVertexCount(SkinnedMesh *mesh, M3Gint count)
   1.161 +{
   1.162 +    /* Reallocate only if vertex count increased */
   1.163 +    
   1.164 +    if (count > mesh->weightedVertexCount) {
   1.165 +        
   1.166 +        Interface *m3g = M3G_INTERFACE(mesh);
   1.167 +
   1.168 +        int i;
   1.169 +
   1.170 +        /* Reallocate the weight shift array */
   1.171 +        {
   1.172 +            M3Gubyte *pNew = (M3Gubyte*) m3gAllocZ(m3g, count);
   1.173 +            if (!pNew) {
   1.174 +                return M3G_FALSE;
   1.175 +            }
   1.176 +            m3gCopy(pNew, mesh->weightShifts, mesh->weightedVertexCount);
   1.177 +            m3gFree(m3g, mesh->weightShifts);
   1.178 +            mesh->weightShifts = pNew;
   1.179 +        }
   1.180 +
   1.181 +        /* Reallocate each of the bone index and weight arrays */
   1.182 +        
   1.183 +        for (i = 0; i < mesh->bonesPerVertex; ++i) {
   1.184 +            
   1.185 +            M3Gubyte *pNew;
   1.186 +            
   1.187 +            /* Weights */
   1.188 +            pNew = (M3Gubyte*) m3gAllocZ(m3g, count);
   1.189 +            if (!pNew) {
   1.190 +                return M3G_FALSE; /* out of memory */
   1.191 +            }
   1.192 +            m3gCopy(pNew, mesh->boneWeights[i], mesh->weightedVertexCount);
   1.193 +            m3gFree(m3g, mesh->boneWeights[i]);
   1.194 +            mesh->boneWeights[i] = pNew;
   1.195 +            
   1.196 +            pNew = (M3Gubyte*) m3gAllocZ(m3g, count);
   1.197 +            if (!pNew) {
   1.198 +                return M3G_FALSE; /* out of memory */
   1.199 +            }
   1.200 +            m3gCopy(pNew, mesh->normalizedWeights[i],
   1.201 +                    mesh->weightedVertexCount);
   1.202 +            m3gFree(m3g, mesh->normalizedWeights[i]);
   1.203 +            mesh->normalizedWeights[i] = pNew;
   1.204 +
   1.205 +            /* Indices */
   1.206 +            pNew = (M3Gubyte*) m3gAllocZ(m3g, count);
   1.207 +            if (!pNew) {
   1.208 +                return M3G_FALSE; /* out of memory */
   1.209 +            }
   1.210 +            m3gCopy(pNew, mesh->boneIndices[i], mesh->weightedVertexCount);
   1.211 +            m3gFree(m3g, mesh->boneIndices[i]);
   1.212 +            mesh->boneIndices[i] = pNew;
   1.213 +        }
   1.214 +
   1.215 +        mesh->weightedVertexCount = count;
   1.216 +    }
   1.217 +    return M3G_TRUE;
   1.218 +}
   1.219 +    
   1.220 +/*!
   1.221 + * \internal
   1.222 + * \brief Reallocate the per-vertex data if necessary.
   1.223 + *
   1.224 + * \note Inline because only called from AddTransform.
   1.225 + */
   1.226 +static M3G_INLINE M3Gbool m3gEnsureBonesPerVertex(SkinnedMesh *mesh,
   1.227 +                                                  M3Gint count)
   1.228 +{
   1.229 +    M3G_ASSERT(count <= M3G_MAX_VERTEX_TRANSFORMS);
   1.230 +
   1.231 +    /* Allocate only if per-vertex bone count increased */
   1.232 +    
   1.233 +    if (count > mesh->bonesPerVertex) {
   1.234 +        
   1.235 +        Interface *m3g = M3G_INTERFACE(mesh);
   1.236 +        
   1.237 +        const M3Gint vertexCount = mesh->weightedVertexCount;
   1.238 +        M3Gubyte *pNew;
   1.239 +        
   1.240 +        int i;
   1.241 +
   1.242 +        /* Allocate new arrays for bone indices and weights until
   1.243 +         * we're satisfied */
   1.244 +        
   1.245 +        for (i = mesh->bonesPerVertex; i < count; ++i) {
   1.246 +            pNew = (M3Gubyte*) m3gAllocZ(m3g, vertexCount);
   1.247 +            if (!pNew) {
   1.248 +                goto AllocFailed; /* out of memory */
   1.249 +            }
   1.250 +            mesh->boneIndices[i] = pNew;
   1.251 +            
   1.252 +            pNew = (M3Gubyte*) m3gAllocZ(m3g, vertexCount);
   1.253 +            if (!pNew) {
   1.254 +                goto AllocFailed; /* out of memory */
   1.255 +            }
   1.256 +            mesh->boneWeights[i] = pNew;
   1.257 +            
   1.258 +            pNew = (M3Gubyte*) m3gAllocZ(m3g, vertexCount);
   1.259 +            if (!pNew) {
   1.260 +                goto AllocFailed; /* out of memory */
   1.261 +            }
   1.262 +            mesh->normalizedWeights[i] = pNew;
   1.263 +        }
   1.264 +
   1.265 +        mesh->bonesPerVertex = count;
   1.266 +        return M3G_TRUE;
   1.267 +
   1.268 +        /* In case of failure, clean up to keep the bonesPerVertex
   1.269 +         * counter in sync with the actual number of arrays
   1.270 +         * allocated */
   1.271 +    
   1.272 +    AllocFailed:
   1.273 +        for (i = mesh->bonesPerVertex; i < count; ++i) {
   1.274 +            m3gFree(m3g, mesh->boneIndices[i]);
   1.275 +            m3gFree(m3g, mesh->boneWeights[i]);
   1.276 +            m3gFree(m3g, mesh->normalizedWeights[i]);
   1.277 +
   1.278 +            mesh->boneIndices[i] = NULL;
   1.279 +            mesh->boneWeights[i] = NULL;
   1.280 +            mesh->normalizedWeights[i] = NULL;
   1.281 +        }
   1.282 +        return M3G_FALSE;
   1.283 +    }
   1.284 +    return M3G_TRUE;
   1.285 +}
   1.286 +    
   1.287 +/*!
   1.288 + * \internal
   1.289 + * \brief Add a new bone influence to a vertex
   1.290 + *
   1.291 + * If the target vertex is already affected by
   1.292 + * M3G_MAX_VERTEX_TRANSFORMS bones, the one with the lowest weight is
   1.293 + * discarded.
   1.294 + */
   1.295 +static M3G_INLINE void m3gAddInfluence(SkinnedMesh *mesh,
   1.296 +                                       M3Gint vertexIndex,
   1.297 +                                       M3Gint boneIndex,
   1.298 +                                       M3Gint weight) 
   1.299 +{
   1.300 +    M3Gint bonesPerVertex = mesh->bonesPerVertex;
   1.301 +    M3Guint minWeight = weight;
   1.302 +    M3Gint minWeightIndex = -1;
   1.303 +    int i;
   1.304 +
   1.305 +    /* Shift the weight into the same scale with the other weights for
   1.306 +     * this vertex. */
   1.307 +
   1.308 +    weight >>= mesh->weightShifts[vertexIndex];
   1.309 +
   1.310 +    /* Look for an existing weight for our bone, or find the index
   1.311 +     * with the lowest weight if not found, and store it in
   1.312 +     * minWeightIndex.  Note that we're not separately tagging indices
   1.313 +     * as used/unused; unused ones will merely have a weight of
   1.314 +     * zero. */
   1.315 +        
   1.316 +    for (i = 0; i < bonesPerVertex; ++i) {
   1.317 +
   1.318 +        /* If we find an existing weight for our bone, just add to
   1.319 +         * that and break out. Otherwise, keep track of the minimum
   1.320 +         * weight encountered so far. */
   1.321 +        
   1.322 +        if (mesh->boneIndices[i][vertexIndex] == boneIndex) {
   1.323 +            weight += mesh->boneWeights[i][vertexIndex];
   1.324 +            minWeightIndex = i;
   1.325 +            break;
   1.326 +        }
   1.327 +        else {
   1.328 +            M3Guint tempWeight = mesh->boneWeights[i][vertexIndex];
   1.329 +            if (tempWeight < minWeight) {
   1.330 +                minWeight = tempWeight;
   1.331 +                minWeightIndex = i;
   1.332 +            }
   1.333 +        }
   1.334 +    }
   1.335 +
   1.336 +    /* Check whether our total weight exceeds the allocated range,
   1.337 +     * shifting all existing weights down if necessary */
   1.338 +
   1.339 +    while (weight >= (1 << 8)) { /* byte range */
   1.340 +        weight >>= 1;
   1.341 +        mesh->weightShifts[vertexIndex] += 1;
   1.342 +        for (i = 0; i < bonesPerVertex; ++i) {
   1.343 +            mesh->boneWeights[i][vertexIndex] >>= 1;
   1.344 +        }
   1.345 +        M3G_ASSERT(mesh->weightShifts[vertexIndex] <= 31);
   1.346 +    }
   1.347 +
   1.348 +    /* Add the index and weight contribution of the new
   1.349 +     * transformation, provided that the minimum weight found was
   1.350 +     * indeed smaller than the one we're adding */
   1.351 +        
   1.352 +    if (minWeightIndex >= 0) {
   1.353 +        mesh->boneIndices[minWeightIndex][vertexIndex] = (M3Gubyte) boneIndex;
   1.354 +        mesh->boneWeights[minWeightIndex][vertexIndex] = (M3Gubyte) weight;
   1.355 +
   1.356 +        /* Need an update of the normalizing scales, too, as well as
   1.357 +         * the actual transformed vertices */
   1.358 +        
   1.359 +        mesh->weightsDirty = M3G_TRUE;
   1.360 +        m3gInvalidateNode((Node*) mesh, NODE_TRANSFORMS_BIT|NODE_BBOX_BIT);
   1.361 +    }
   1.362 +}
   1.363 +
   1.364 +/*!
   1.365 + * \internal
   1.366 + * \brief Computes the normalization scales for vertex weights
   1.367 + */
   1.368 +static void m3gNormalizeWeights(SkinnedMesh *mesh)
   1.369 +{
   1.370 +    const M3Gint bonesPerVertex = mesh->bonesPerVertex;
   1.371 +    const M3Gint vertexCount = mesh->weightedVertexCount;
   1.372 +    M3Gint vi;
   1.373 +
   1.374 +    for (vi = 0; vi < vertexCount; ++vi) {
   1.375 +        M3Gint k;
   1.376 +        
   1.377 +        /* Sum up the 8-bit (possibly downshifted) weights */
   1.378 +        
   1.379 +        M3Guint sum = 0;
   1.380 +        for (k = 0; k < bonesPerVertex; ++k) {
   1.381 +            sum += mesh->boneWeights[k][vi];
   1.382 +        }
   1.383 +
   1.384 +        /* Compute an 8.24 reciprocal of the weights, then scale with
   1.385 +         * that to normalize, and shift to 1.7 fixed point */
   1.386 +        {
   1.387 +            M3Guint s = (sum > 0 ? (1U << 24) / sum : 0);
   1.388 +
   1.389 +            sum = 0;
   1.390 +            for (k = 0; k < bonesPerVertex; ++k) {
   1.391 +                M3Guint normalized = (s * mesh->boneWeights[k][vi]) >> 17;
   1.392 +                M3G_ASSERT(m3gInRange((M3Gint)normalized, 0, 128));
   1.393 +                sum += normalized;
   1.394 +                mesh->normalizedWeights[k][vi] = (M3Gubyte) normalized;
   1.395 +            }
   1.396 +            
   1.397 +            /* NOTE there is a maximum of ½ rounding error per
   1.398 +             * component, plus the rounding error from the reciprocal
   1.399 +             * calculation, so the sum of weights will often not sum
   1.400 +             * to 128 exactly! We therefore only assert against
   1.401 +             * clearly out-of-range values here */
   1.402 +            
   1.403 +            M3G_ASSERT(sum == 0 || m3gInRange((M3Gint) sum, 96, 128));
   1.404 +        }
   1.405 +    }
   1.406 +
   1.407 +    mesh->weightsDirty = M3G_FALSE;
   1.408 +}
   1.409 +
   1.410 +/*!
   1.411 + * \internal
   1.412 + * \brief Computes an optimal exponent value for a fixed point
   1.413 + * transformation
   1.414 + *
   1.415 + * This scales the translation exponent up to optimally utilize the
   1.416 + * 32-bit intermediate precision if the matrix exponent is smaller.
   1.417 + */
   1.418 +static M3Gint m3gOptimalExponent(M3Gint matrixExp, M3Gint transExp)
   1.419 +{
   1.420 +    M3Gint maxExp = matrixExp;
   1.421 +    M3Gint shift = transExp - matrixExp;
   1.422 +    if (shift > 0) {
   1.423 +
   1.424 +        /* The matrix part will always occupy less than half of the
   1.425 +         * available range if shifted down by at least one bit, so we
   1.426 +         * can shift the translation up by a maximum of 15 bits.  If
   1.427 +         * the matrix is shifted by more than 31 bits, it will always
   1.428 +         * flush to zero, freeing the full 32-bit range for the
   1.429 +         * translation alone. */
   1.430 +
   1.431 +        if (shift >= 32) {      /* matrix will flush to zero */
   1.432 +            shift = 16;
   1.433 +        }
   1.434 +        else if (shift >= 16) { /* matrix always < half of the range */
   1.435 +            shift = 15;
   1.436 +        }
   1.437 +        else {
   1.438 +            shift -= 1;     /* shift matrix by at least one bit */
   1.439 +        }
   1.440 +        
   1.441 +        maxExp = transExp - shift;
   1.442 +    }
   1.443 +    
   1.444 +    M3G_ASSERT(maxExp >= matrixExp && maxExp >= transExp - 16);
   1.445 +    return maxExp;
   1.446 +}
   1.447 +
   1.448 +/*
   1.449 + * \brief Fixed point vertex transformation
   1.450 + *
   1.451 + * \param mtx        pointer to a 3x3 16-bit matrix
   1.452 + * \param mtxExp     exponent for the matrix elements (upshift from int)
   1.453 + * \param trans      pointer to 3-element 16-bit translation vector
   1.454 + * \param transExp   exponent for the translation vector
   1.455 + * \param maxExp     precalculated "optimal" exponent
   1.456 + * \param vx         vertex X coordinate (16-bit range)
   1.457 + * \param vy         vertex Y coordinate (16-bit range)
   1.458 + * \param vz         vertex Z coordinate (16-bit range)
   1.459 + * \param out        output vertex, 25 bits of precision
   1.460 + * \return exponent value for \c out
   1.461 + */
   1.462 +static M3Gint m3gFixedPointTransform(const M3Gshort *mtx, M3Gint mtxExp,
   1.463 +                                     const M3Gshort *trans, M3Gint transExp,
   1.464 +                                     M3Gint maxExp,
   1.465 +                                     M3Gint vx, M3Gint vy, M3Gint vz,
   1.466 +                                     M3Gint *out)
   1.467 +{
   1.468 +    M3Gint shift;
   1.469 +    M3Gint ox = 0, oy = 0, oz = 0;
   1.470 +    
   1.471 +    /* First put in the translation part, upscaled to the optimal
   1.472 +     * range for this bone */
   1.473 +
   1.474 +    if (trans) {
   1.475 +        shift = maxExp - (transExp - 16);
   1.476 +        M3G_ASSERT(shift >= 0);
   1.477 +        if (shift < 32) {
   1.478 +            ox += ((M3Gint) trans[0] << 16) >> shift;
   1.479 +            oy += ((M3Gint) trans[1] << 16) >> shift;
   1.480 +            oz += ((M3Gint) trans[2] << 16) >> shift;
   1.481 +        }
   1.482 +    }
   1.483 +        
   1.484 +    /* Add the input multiplied with the base 3x3 matrix and shifted
   1.485 +     * to the "maxExp" scale, provided that it has any effect on the
   1.486 +     * outcome */
   1.487 +    
   1.488 +    shift = maxExp - mtxExp;
   1.489 +    M3G_ASSERT(shift >= 0);
   1.490 +    if (shift < 32) {
   1.491 +        
   1.492 +#       if defined(M3G_DEBUG)
   1.493 +        M3Gint iMin = (-1 << 31) + (65535 * 32768 >> shift);
   1.494 +        M3Gint iMax = (M3Gint)((1u << 31)-1) - (65535 * 32768 >> shift);
   1.495 +        M3G_ASSERT(m3gInRange(ox, iMin, iMax));
   1.496 +        M3G_ASSERT(m3gInRange(oy, iMin, iMax));
   1.497 +        M3G_ASSERT(m3gInRange(oz, iMin, iMax));
   1.498 +#       endif /* M3G_DEBUG */
   1.499 +        
   1.500 +        ox += (mtx[0] * vx + mtx[3] * vy + mtx[6] * vz) >> shift;
   1.501 +        oy += (mtx[1] * vx + mtx[4] * vy + mtx[7] * vz) >> shift;
   1.502 +        oz += (mtx[2] * vx + mtx[5] * vy + mtx[8] * vz) >> shift;
   1.503 +    }
   1.504 +
   1.505 +    /* Shift the output down to fit into 25 bits; we're dropping 7
   1.506 +     * bits of precision here, so adjust the exponent accordingly */
   1.507 +
   1.508 +    out[0] = ox >> 7;
   1.509 +    out[1] = oy >> 7;
   1.510 +    out[2] = oz >> 7;
   1.511 +    return maxExp + 7;
   1.512 +}
   1.513 +
   1.514 +/*!
   1.515 + * \internal
   1.516 + * \brief Applies scale and bias to a vertex
   1.517 + *
   1.518 + * This is required for vertices that have no bones attached.
   1.519 + * 
   1.520 + * \param mesh    the SkinnedMesh object
   1.521 + * \param vx      vertex X coordinate (16-bit range)
   1.522 + * \param vy      vertex Y coordinate (16-bit range)
   1.523 + * \param vz      vertex Z coordinate (16-bit range)
   1.524 + * \param upshift scaling value for the input coordinates and the
   1.525 + *                translation component of the transformation
   1.526 + * \param vertex  output vertex position
   1.527 + * \return exponent value for \c vertex
   1.528 + */
   1.529 +static M3Gint m3gScaleAndBiasVertex(const SkinnedMesh *mesh,
   1.530 +                                    M3Gint vx, M3Gint vy, M3Gint vz,
   1.531 +                                    M3Gint upshift,
   1.532 +                                    M3Gshort *vertex)
   1.533 +{
   1.534 +    M3Gint temp[3];
   1.535 +    M3Gint expo;
   1.536 +
   1.537 +    M3G_ASSERT(m3gInRange(vx, -1 << 15, (1 << 15) - 1));
   1.538 +    M3G_ASSERT(m3gInRange(vy, -1 << 15, (1 << 15) - 1));
   1.539 +    M3G_ASSERT(m3gInRange(vz, -1 << 15, (1 << 15) - 1));
   1.540 +    
   1.541 +    expo = m3gFixedPointTransform(mesh->scaleMatrix, mesh->scaleExp,
   1.542 +                                  mesh->biasVector, mesh->biasExp + upshift,
   1.543 +                                  mesh->scaleBiasExp,
   1.544 +                                  vx << upshift, vy << upshift, vz << upshift,
   1.545 +                                  temp) - upshift;
   1.546 +
   1.547 +    /* Scale down from 25 to 16 bits, adjusting the exponent
   1.548 +     * accordingly */
   1.549 +    
   1.550 +    vertex[0] = (M3Gshort)(temp[0] >> 9);
   1.551 +    vertex[1] = (M3Gshort)(temp[1] >> 9);
   1.552 +    vertex[2] = (M3Gshort)(temp[2] >> 9);
   1.553 +    expo += 9;
   1.554 +    
   1.555 +    M3G_ASSERT(m3gInRange(expo, -127, 127));
   1.556 +    return expo;
   1.557 +}
   1.558 +
   1.559 +/*!
   1.560 + * \internal
   1.561 + * \brief Computes the blended position for a single vertex
   1.562 + *
   1.563 + * \param mesh    the SkinnedMesh object
   1.564 + * \param vidx    vertex index (for accessing bone data)
   1.565 + * \param vx      vertex X coordinate (16-bit range)
   1.566 + * \param vy      vertex Y coordinate (16-bit range)
   1.567 + * \param vz      vertex Z coordinate (16-bit range)
   1.568 + * \param upshift scaling value for the input coordinates and the
   1.569 + *                translation component of the transformation
   1.570 + * \param vertex  output vertex position
   1.571 + * \return exponent value for \c vertex
   1.572 + */
   1.573 +static M3Gint m3gBlendVertex(const SkinnedMesh *mesh,
   1.574 +                             M3Gint vidx,
   1.575 +                             M3Gint vx, M3Gint vy, M3Gint vz,
   1.576 +                             M3Gint upshift,
   1.577 +                             M3Gshort *vertex)
   1.578 +{
   1.579 +    const M3Gint boneCount = mesh->bonesPerVertex;
   1.580 +    const PointerArray *boneArray = &mesh->bones;
   1.581 +    M3Gint i;
   1.582 +    
   1.583 +    M3Gint outExp = -128;
   1.584 +    M3Gint sumWeights = 0;
   1.585 +    
   1.586 +    M3Gint ox = 0, oy = 0, oz = 0;
   1.587 +    
   1.588 +    vx <<= upshift;
   1.589 +    vy <<= upshift;
   1.590 +    vz <<= upshift;
   1.591 +
   1.592 +    M3G_ASSERT(m3gInRange(vx, -1 << 15, (1 << 15) - 1));
   1.593 +    M3G_ASSERT(m3gInRange(vy, -1 << 15, (1 << 15) - 1));
   1.594 +    M3G_ASSERT(m3gInRange(vz, -1 << 15, (1 << 15) - 1));
   1.595 +
   1.596 +    /* Loop over the bones and sum the contribution from each */
   1.597 +    
   1.598 +    for (i = 0; i < boneCount; ++i) {
   1.599 +        
   1.600 +        M3Gint weight = (M3Gint) mesh->normalizedWeights[i][vidx];
   1.601 +        sumWeights += weight;
   1.602 +
   1.603 +        /* Skip bones with zero weights */
   1.604 +        
   1.605 +        if (weight > 0) {
   1.606 +            
   1.607 +            const Bone *bone = (const Bone *)
   1.608 +                m3gGetArrayElement(boneArray, mesh->boneIndices[i][vidx]);
   1.609 +            M3Gint temp[3];
   1.610 +            M3Gint shift;
   1.611 +
   1.612 +            shift = m3gFixedPointTransform(bone->baseMatrix, bone->baseExp,
   1.613 +                                           bone->posVec, bone->posExp + upshift,
   1.614 +                                           bone->maxExp,
   1.615 +                                           vx, vy, vz,
   1.616 +                                           temp);
   1.617 +
   1.618 +            shift = outExp - shift;
   1.619 +            if (shift < 0) {
   1.620 +                shift = -shift;
   1.621 +                if (shift < 31) {
   1.622 +                    ox >>= shift;
   1.623 +                    oy >>= shift;
   1.624 +                    oz >>= shift;
   1.625 +                }
   1.626 +                else {
   1.627 +                    ox = oy = oz = 0;
   1.628 +                }
   1.629 +                outExp += shift;
   1.630 +                shift = 0;
   1.631 +            }
   1.632 +
   1.633 +            /* Apply the vertex weights: 1.7 * 25.0 -> 26.7, but since
   1.634 +             * the weights are positive and sum to 1, we should stay
   1.635 +             * within the 32-bit range */
   1.636 +            
   1.637 +            if (shift < 31) {
   1.638 +                
   1.639 +                M3G_ASSERT(m3gInRange(temp[0], -1 << 24, (1 << 24) - 1));
   1.640 +                M3G_ASSERT(m3gInRange(temp[1], -1 << 24, (1 << 24) - 1));
   1.641 +                M3G_ASSERT(m3gInRange(temp[2], -1 << 24, (1 << 24) - 1));
   1.642 +                
   1.643 +                ox += (weight * temp[0]) >> shift;
   1.644 +                oy += (weight * temp[1]) >> shift;
   1.645 +                oz += (weight * temp[2]) >> shift;
   1.646 +            }
   1.647 +        }
   1.648 +    }
   1.649 +
   1.650 +    /* Before returning, we still need to check for the special case
   1.651 +     * of all-zero weights, and shift the values from the post-scaling
   1.652 +     * 32-bit precision back into the 16-bit range; we're essentially
   1.653 +     * dropping the (25 - 16) bits of the blended result, so the
   1.654 +     * exponent must change accordingly */
   1.655 +
   1.656 +    if (sumWeights > 0) {
   1.657 +        vertex[0] = (M3Gshort)(ox >> 16);
   1.658 +        vertex[1] = (M3Gshort)(oy >> 16);
   1.659 +        vertex[2] = (M3Gshort)(oz >> 16);
   1.660 +        outExp = outExp - upshift + 9;
   1.661 +
   1.662 +        M3G_ASSERT(m3gInRange(outExp, -127, 127));
   1.663 +        return outExp;
   1.664 +    }
   1.665 +    else {
   1.666 +        vx >>= upshift;
   1.667 +        vy >>= upshift;
   1.668 +        vz >>= upshift;
   1.669 +        return m3gScaleAndBiasVertex(mesh, vx, vy, vz, upshift, vertex);
   1.670 +    }
   1.671 +}
   1.672 +
   1.673 +/*!
   1.674 + * \internal
   1.675 + * \brief Computes the blended normal vector for a single vertex
   1.676 + *
   1.677 + * \param mesh    the SkinnedMesh object
   1.678 + * \param vidx    vertex index (for accessing bone data)
   1.679 + * \param nx      normal X coordinate (16-bit range)
   1.680 + * \param ny      normal Y coordinate (16-bit range)
   1.681 + * \param nz      normal Z coordinate (16-bit range)
   1.682 + * \param upshift scaling for input coordinates to increase precision
   1.683 + * \param normal  output normal vector (8-bit range!)
   1.684 + * \return a shift value for the output vertex (scale from integer)
   1.685 + */
   1.686 +static void m3gBlendNormal(const SkinnedMesh *mesh,
   1.687 +                           M3Gint vidx,
   1.688 +                           M3Gint nx, M3Gint ny, M3Gint nz,
   1.689 +                           M3Gint upshift,
   1.690 +                           M3Gbyte *normal)
   1.691 +{
   1.692 +    const M3Gint boneCount = mesh->bonesPerVertex;
   1.693 +    const PointerArray *boneArray = &mesh->bones;
   1.694 +    M3Gint i;
   1.695 +    
   1.696 +    M3Gint outExp = -128;
   1.697 +    M3Gint sumWeights = 0;
   1.698 +    
   1.699 +    M3Gint ox = 0, oy = 0, oz = 0;
   1.700 +
   1.701 +    nx <<= upshift;
   1.702 +    ny <<= upshift;
   1.703 +    nz <<= upshift;
   1.704 +    
   1.705 +    M3G_ASSERT(m3gInRange(nx, -1 << 15, (1 << 15) - 1));
   1.706 +    M3G_ASSERT(m3gInRange(ny, -1 << 15, (1 << 15) - 1));
   1.707 +    M3G_ASSERT(m3gInRange(nz, -1 << 15, (1 << 15) - 1));
   1.708 +
   1.709 +    /* Loop over the bones and sum the contribution from each */
   1.710 +    
   1.711 +    for (i = 0; i < boneCount; ++i) {
   1.712 +        
   1.713 +        M3Gint weight = (M3Gint) mesh->normalizedWeights[i][vidx];
   1.714 +        sumWeights += weight;
   1.715 +
   1.716 +        /* Skip bones with zero weights */
   1.717 +        
   1.718 +        if (weight > 0) {
   1.719 +            
   1.720 +            const Bone *bone = (const Bone *)
   1.721 +                m3gGetArrayElement(boneArray, mesh->boneIndices[i][vidx]);
   1.722 +            M3Gint temp[3];
   1.723 +            M3Gint shift;
   1.724 +
   1.725 +            shift = m3gFixedPointTransform(bone->normalMatrix, 0,
   1.726 +                                           NULL, 0,
   1.727 +                                           0,
   1.728 +                                           nx, ny, nz,
   1.729 +                                           temp);
   1.730 +
   1.731 +            shift = outExp - shift;
   1.732 +            if (shift < 0) {
   1.733 +                shift = -shift;
   1.734 +                if (shift < 31) {
   1.735 +                    ox >>= shift;
   1.736 +                    oy >>= shift;
   1.737 +                    oz >>= shift;
   1.738 +                }
   1.739 +                else {
   1.740 +                    ox = oy = oz = 0;
   1.741 +                }
   1.742 +                outExp += shift;
   1.743 +                shift = 0;
   1.744 +            }
   1.745 +
   1.746 +            /* Apply the vertex weights: 1.7 * 25.0 -> 26.7, but since
   1.747 +             * the weights are positive and sum to 1, we should stay
   1.748 +             * within the 32-bit range */
   1.749 +            
   1.750 +            if (shift < 31) {
   1.751 +                
   1.752 +                M3G_ASSERT(m3gInRange(temp[0], -1 << 24, (1 << 24) - 1));
   1.753 +                M3G_ASSERT(m3gInRange(temp[1], -1 << 24, (1 << 24) - 1));
   1.754 +                M3G_ASSERT(m3gInRange(temp[2], -1 << 24, (1 << 24) - 1));
   1.755 +                
   1.756 +                ox += (weight * temp[0]) >> shift;
   1.757 +                oy += (weight * temp[1]) >> shift;
   1.758 +                oz += (weight * temp[2]) >> shift;
   1.759 +            }
   1.760 +        }
   1.761 +    }
   1.762 +
   1.763 +    /* Before returning, we still need to check for the special case
   1.764 +     * of all-zero weights, and shift the values from the post-scaling
   1.765 +     * 32-bit precision down into the 8-bit range */
   1.766 +
   1.767 +    if (sumWeights > 0) {
   1.768 +        normal[0] = (M3Gbyte)(ox >> 24);
   1.769 +        normal[1] = (M3Gbyte)(oy >> 24);
   1.770 +        normal[2] = (M3Gbyte)(oz >> 24);
   1.771 +    }
   1.772 +    else {
   1.773 +        normal[0] = (M3Gbyte)(ox >> 8);
   1.774 +        normal[1] = (M3Gbyte)(oy >> 8);
   1.775 +        normal[2] = (M3Gbyte)(oz >> 8);
   1.776 +    }
   1.777 +}
   1.778 +
   1.779 +/*!
   1.780 + * \internal
   1.781 + * \brief Updates internal vertex buffer
   1.782 + *
   1.783 + * \param mesh SkinnedMesh object
   1.784 + *
   1.785 + * \retval M3G_TRUE VertexBuffer is up to date
   1.786 + * \retval M3G_FALSE Failed to update VertexBuffer, out of memory exception raised
   1.787 + */
   1.788 +static M3Gbool m3gSkinnedMeshUpdateVB(SkinnedMesh *mesh)
   1.789 +{
   1.790 +    M3Gint vbTimestamp;
   1.791 +    M3G_ASSERT(mesh->mesh.vertexBuffer != NULL);
   1.792 +    M3G_ASSERT(mesh->morphedVB != NULL);
   1.793 +    
   1.794 +    /* Source vertex buffer array configuration changed since last
   1.795 +     * update? */
   1.796 +
   1.797 +    vbTimestamp = m3gGetTimestamp(mesh->mesh.vertexBuffer);
   1.798 +    
   1.799 +    if (mesh->vbTimestamp != vbTimestamp) {
   1.800 +        Interface *m3g = M3G_INTERFACE(mesh);
   1.801 +        VertexArray *array;
   1.802 +        M3Gint vcount = m3gGetVertexCount(mesh->mesh.vertexBuffer);
   1.803 +
   1.804 +        /* Must ensure that our internal morphing buffer matches the
   1.805 +         * configuration of the source buffer, with dedicated arrays
   1.806 +         * for the morphed positions and normals */
   1.807 +        
   1.808 +        if (!m3gMakeModifiedVertexBuffer(mesh->morphedVB,
   1.809 +                                         mesh->mesh.vertexBuffer,
   1.810 +                                         M3G_POSITION_BIT|M3G_NORMAL_BIT,
   1.811 +                                         M3G_FALSE)) {
   1.812 +            return M3G_FALSE; /* out of memory */
   1.813 +        }
   1.814 +
   1.815 +        /* We always have the vertex positions as shorts, but the
   1.816 +         * array may not be actually initialized yet, so we must check
   1.817 +         * whether to create a copy or not */
   1.818 +
   1.819 +        if (mesh->mesh.vertexBuffer->vertices) {
   1.820 +            array = m3gCreateVertexArray(m3g, vcount, 3, M3G_SHORT);
   1.821 +            if (!array) {
   1.822 +                return M3G_FALSE;
   1.823 +            }
   1.824 +            m3gSetVertexArray(mesh->morphedVB, array, 1.f, NULL, 0);
   1.825 +        }
   1.826 +
   1.827 +        /* Normals (always bytes) only exist if in the original VB */
   1.828 +        
   1.829 +        if (mesh->mesh.vertexBuffer->normals) {
   1.830 +            array = m3gCreateVertexArray(m3g, vcount, 3, M3G_BYTE);
   1.831 +            if (!array) {
   1.832 +                return M3G_FALSE;
   1.833 +            }
   1.834 +            m3gSetNormalArray(mesh->morphedVB, array);
   1.835 +        }
   1.836 +    
   1.837 +        mesh->vbTimestamp = vbTimestamp;
   1.838 +    }
   1.839 +    
   1.840 +    /* The default color must always be updated, because it can be
   1.841 +     * animated (doesn't affect timestamp) */
   1.842 +    
   1.843 +    mesh->morphedVB->defaultColor = mesh->mesh.vertexBuffer->defaultColor;
   1.844 +    return M3G_TRUE;
   1.845 +}
   1.846 +
   1.847 +
   1.848 +/*!
   1.849 + * \internal
   1.850 + * \brief Gets the transformation(s) for a single bone record
   1.851 + *
   1.852 + * Also stores the normal transformation matrix if needed.
   1.853 + *
   1.854 + * \param mesh       pointer to the mesh object
   1.855 + * \param bone       pointer to the bone record
   1.856 + * \param hasNormals flag indicating whether the normals transformation
   1.857 + *                   should be computed and cached in the bone record
   1.858 + * \param mtx        matrix to store the vertex transformation in
   1.859 + */
   1.860 +static M3G_INLINE M3Gbool m3gGetBoneTransformInternal(SkinnedMesh *mesh,
   1.861 +                                              Bone *bone,
   1.862 +                                              M3Gbool hasNormals,
   1.863 +                                              Matrix *mtx)
   1.864 +{
   1.865 +    const VertexBuffer *vb = mesh->mesh.vertexBuffer;
   1.866 +
   1.867 +    /* Get the vertex transformation and concatenate it with the
   1.868 +     * at-rest matrix and the vertex scale and bias transformations.
   1.869 +     * The resulting 3x4 transformation matrix is then split into a
   1.870 +     * fixed point 3x3 matrix and translation vector */
   1.871 +    
   1.872 +    if (!m3gGetTransformTo(bone->node, (Node*) mesh, mtx)) {
   1.873 +        return M3G_FALSE; /* no path or singular transform */
   1.874 +    }
   1.875 +    m3gMulMatrix(mtx, &bone->toBone);
   1.876 +
   1.877 +    /* If normals are enabled, compute and store the inverse transpose
   1.878 +     * matrix for transforming normals at this stage */
   1.879 +    
   1.880 +    if (hasNormals) {
   1.881 +        Matrix t;
   1.882 +        if (!m3gInverseTranspose(&t, mtx)) {
   1.883 +            m3gRaiseError(M3G_INTERFACE(mesh), M3G_ARITHMETIC_ERROR);
   1.884 +            return M3G_FALSE; /* singular transform */
   1.885 +        }
   1.886 +        m3gGetFixedPoint3x3Basis(&t, bone->normalMatrix);
   1.887 +    }
   1.888 +
   1.889 +    /* Apply the vertex bias and scale to the transformation */
   1.890 +    
   1.891 +    m3gTranslateMatrix(
   1.892 +        mtx, vb->vertexBias[0], vb->vertexBias[1], vb->vertexBias[2]);
   1.893 +    m3gScaleMatrix(mtx, vb->vertexScale, vb->vertexScale, vb->vertexScale);
   1.894 +    
   1.895 +    return M3G_TRUE;
   1.896 +}
   1.897 +
   1.898 +/*!
   1.899 + * \internal
   1.900 + * \brief Compute and cache the bone transformations for morphing
   1.901 + *
   1.902 + * \param mesh     the SkinnedMesh object
   1.903 + * \param posShift vertex position value "gain"
   1.904 + */
   1.905 +static M3Gbool m3gPreComputeTransformations(SkinnedMesh *mesh,
   1.906 +                                            M3Gint posShift,
   1.907 +                                            M3Gbool hasNormals)
   1.908 +{
   1.909 +    M3Gint boneCount = m3gArraySize(&mesh->bones);
   1.910 +    M3Gint i;
   1.911 +    Matrix *tBone = NULL;
   1.912 +
   1.913 +    /* First, just compute the floating point transformation matrices
   1.914 +     * for the bones, caching them in a temp array */
   1.915 +
   1.916 +    if (boneCount > 0) {
   1.917 +        tBone = m3gAllocTemp(M3G_INTERFACE(mesh), boneCount * sizeof(Matrix));
   1.918 +        if (!tBone) {
   1.919 +            return M3G_FALSE; /* out of memory */
   1.920 +        }    
   1.921 +        for (i = 0; i < boneCount; ++i) {
   1.922 +            Bone *bone = m3gGetArrayElement(&mesh->bones, i);
   1.923 +            if (!m3gGetBoneTransformInternal(mesh, bone, hasNormals, &tBone[i])) {
   1.924 +                return M3G_FALSE;
   1.925 +            }
   1.926 +        }
   1.927 +    }
   1.928 +
   1.929 +    /* Find the value range of the bone translations, and offset the
   1.930 +     * bones to center output vertex values (roughly) around the
   1.931 +     * origin */
   1.932 +    {
   1.933 +        const VertexBuffer *vb = mesh->mesh.vertexBuffer;
   1.934 +        M3Gfloat min[3], max[3], bias[3];
   1.935 +        M3Gint maxExp;
   1.936 +        Vec4 t;
   1.937 +
   1.938 +        /* Find the minimum and maximum values; start with the plain
   1.939 +         * vertex bias for non-weighted bones */
   1.940 +        
   1.941 +        min[0] = max[0] = vb->vertexBias[0];
   1.942 +        min[1] = max[1] = vb->vertexBias[1];
   1.943 +        min[2] = max[2] = vb->vertexBias[2];
   1.944 +        
   1.945 +        for (i = 0; i < boneCount; ++i) {
   1.946 +            m3gGetMatrixColumn(&tBone[i], 3, &t); 
   1.947 +            min[0] = M3G_MIN(min[0], t.x);
   1.948 +            max[0] = M3G_MAX(max[0], t.x);
   1.949 +            min[1] = M3G_MIN(min[1], t.y);
   1.950 +            max[1] = M3G_MAX(max[1], t.y);
   1.951 +            min[2] = M3G_MIN(min[2], t.z);
   1.952 +            max[2] = M3G_MAX(max[2], t.z);
   1.953 +        }
   1.954 +        
   1.955 +        /* Divide to get the mean translation, store in the
   1.956 +         * destination VB, and invert for de-biasing the bones */
   1.957 +        
   1.958 +        for (i = 0; i < 3; ++i) {
   1.959 +            bias[i] = m3gMul(0.5f, m3gAdd(min[i], max[i]));
   1.960 +            mesh->morphedVB->vertexBias[i] = bias[i];
   1.961 +            bias[i] = m3gNegate(bias[i]);
   1.962 +        }
   1.963 +        
   1.964 +        /* Offset bones by the (now inverted) bias vector, and store
   1.965 +         * the fixed point matrix & vector parts in the bone record;
   1.966 +         * also set the maximum bone exponent into the mesh */
   1.967 +
   1.968 +        maxExp = -128;
   1.969 +        for (i = 0; i < boneCount; ++i) {
   1.970 +            Bone *bone = m3gGetArrayElement(&mesh->bones, i);
   1.971 +            m3gPreTranslateMatrix(&tBone[i], bias[0], bias[1], bias[2]); /*lint !e613 tBone not null if boneCount > 0 */
   1.972 +            
   1.973 +            bone->baseExp = (M3Gshort)
   1.974 +                m3gGetFixedPoint3x3Basis(&tBone[i], bone->baseMatrix); /*lint !e613 tBone not null if boneCount > 0 */
   1.975 +            bone->posExp = (M3Gshort)
   1.976 +                m3gGetFixedPointTranslation(&tBone[i], bone->posVec); /*lint !e613 tBone not null if boneCount > 0 */
   1.977 +            bone->maxExp = (M3Gshort)
   1.978 +                m3gOptimalExponent(bone->baseExp, bone->posExp + posShift);
   1.979 +
   1.980 +            maxExp = M3G_MAX(maxExp, bone->maxExp);
   1.981 +        }
   1.982 +
   1.983 +        /* Make a fixed-point matrix for applying the scale and bias as
   1.984 +         * well, for vertices not attached to any bone (this is not the
   1.985 +         * optimal way to store the information, but we can just reuse
   1.986 +         * existing code this way) */
   1.987 +        {
   1.988 +            Matrix sb;
   1.989 +            m3gTranslationMatrix(&sb,
   1.990 +                                 m3gAdd(bias[0], vb->vertexBias[0]),
   1.991 +                                 m3gAdd(bias[1], vb->vertexBias[1]),
   1.992 +                                 m3gAdd(bias[2], vb->vertexBias[2]));
   1.993 +            m3gScaleMatrix(&sb,
   1.994 +                           vb->vertexScale, vb->vertexScale, vb->vertexScale);
   1.995 +            
   1.996 +            mesh->scaleExp = (M3Gshort)
   1.997 +                m3gGetFixedPoint3x3Basis(&sb, mesh->scaleMatrix);
   1.998 +            mesh->biasExp = (M3Gshort)
   1.999 +                m3gGetFixedPointTranslation(&sb, mesh->biasVector);
  1.1000 +            mesh->scaleBiasExp = (M3Gshort)
  1.1001 +                m3gOptimalExponent(mesh->scaleExp, mesh->biasExp + posShift);
  1.1002 +        
  1.1003 +            maxExp = M3G_MAX(mesh->scaleBiasExp, maxExp);
  1.1004 +        }
  1.1005 +
  1.1006 +        /* Compute the maximum post-blending exponent and store it as the
  1.1007 +         * morphed vertex buffer scale -- this is dependent on the
  1.1008 +         * implementations of m3gBlendVertex, m3gScaleAndBiasVertex, and
  1.1009 +         * m3gFixedPointTransform! */
  1.1010 +
  1.1011 +        maxExp = maxExp + 16 - posShift;
  1.1012 +        M3G_ASSERT(m3gInRange(maxExp, -127, 127));
  1.1013 +        *(M3Gint*)&mesh->morphedVB->vertexScale = (maxExp + 127) << 23;
  1.1014 +        mesh->maxExp = (M3Gshort) maxExp;
  1.1015 +    }
  1.1016 +    
  1.1017 +    if (boneCount > 0) {
  1.1018 +        m3gFreeTemp(M3G_INTERFACE(mesh));
  1.1019 +    }
  1.1020 +    
  1.1021 +    return M3G_TRUE;
  1.1022 +}
  1.1023 +
  1.1024 +/*!
  1.1025 + * \internal
  1.1026 + * \brief Computes derived data required for bounding volumes and skinning
  1.1027 + */
  1.1028 +static M3Gbool m3gSkinnedMeshPreMorph(SkinnedMesh *mesh)
  1.1029 +{
  1.1030 +    const VertexBuffer *srcVB = mesh->mesh.vertexBuffer;
  1.1031 +    M3Gint posShift = 0, normalShift = 0;
  1.1032 +    
  1.1033 +    /* Compute upscaling shift values for positions and normals so
  1.1034 +     * that we can maximize precision even for absurdly small
  1.1035 +     * vertex values */
  1.1036 +    {
  1.1037 +        M3Gint minVal, maxVal;
  1.1038 +        
  1.1039 +        if (srcVB->normals) {
  1.1040 +            m3gGetArrayValueRange(srcVB->normals, &minVal, &maxVal);
  1.1041 +            maxVal = M3G_MAX(-minVal, maxVal);
  1.1042 +            M3G_ASSERT(maxVal >= 0);
  1.1043 +            if (maxVal) {
  1.1044 +                while ((maxVal << normalShift) < (1 << 14)) {
  1.1045 +                    ++normalShift;
  1.1046 +                }
  1.1047 +            }
  1.1048 +        }
  1.1049 +            
  1.1050 +        m3gGetArrayValueRange(srcVB->vertices, &minVal, &maxVal);
  1.1051 +        maxVal = M3G_MAX(-minVal, maxVal);
  1.1052 +        M3G_ASSERT(maxVal >= 0);
  1.1053 +        if (maxVal) {
  1.1054 +            while ((maxVal << posShift) < (1 << 14)) {
  1.1055 +                ++posShift;
  1.1056 +            }
  1.1057 +        }
  1.1058 +        
  1.1059 +        mesh->posShift    = (M3Gshort) posShift;
  1.1060 +        mesh->normalShift = (M3Gshort) normalShift;
  1.1061 +    }
  1.1062 +
  1.1063 +    /* Now that we can compute the optimized exponents for the
  1.1064 +     * transformations based on the position upshift value, let's
  1.1065 +     * resolve the bone transformations; this will also cache the
  1.1066 +     * maximum bone exponent in mesh->maxExp */
  1.1067 +
  1.1068 +    if (!m3gPreComputeTransformations(mesh,
  1.1069 +                                      posShift,
  1.1070 +                                      srcVB->normals != NULL)) { 
  1.1071 +        return M3G_FALSE; /* invalid transform */
  1.1072 +    }
  1.1073 +    
  1.1074 +    return M3G_TRUE;
  1.1075 +}
  1.1076 +
  1.1077 +/*!
  1.1078 + * \internal
  1.1079 + * \brief Does the actual vertex morphing into the internal vertex buffer
  1.1080 + *
  1.1081 + * \param mesh   SkinnedMesh object
  1.1082 + * \retval M3G_TRUE     skinning ok
  1.1083 + * \retval M3G_FALSE    skinning failed, exception raised
  1.1084 + */
  1.1085 +static void m3gSkinnedMeshMorph(SkinnedMesh *mesh)
  1.1086 +{
  1.1087 +    const VertexBuffer *srcVB = mesh->mesh.vertexBuffer;
  1.1088 +    const void *srcPositions;
  1.1089 +    const void *srcNormals = NULL;
  1.1090 +    VertexBuffer *dstVB = mesh->morphedVB;
  1.1091 +    M3Gshort *dstPositions;
  1.1092 +    M3Gbyte *dstNormals = NULL;
  1.1093 +    M3Gint vertexCount = mesh->weightedVertexCount;
  1.1094 +    M3Gint maxExp = mesh->maxExp;
  1.1095 +    M3Gint posShift = mesh->posShift, normShift = mesh->normalShift;
  1.1096 +    M3Gint i;
  1.1097 +
  1.1098 +    M3G_ASSERT(!((Node*) mesh)->dirtyBits);
  1.1099 +    
  1.1100 +    /* Let's update the vertex weights if we need to */
  1.1101 +        
  1.1102 +    if (mesh->weightsDirty) {
  1.1103 +        m3gNormalizeWeights(mesh);
  1.1104 +    }
  1.1105 +
  1.1106 +    /* Get pointers to source and destination position and normal
  1.1107 +     * data; the latter will always be shorts and bytes,
  1.1108 +     * respectively, while the former can be either */
  1.1109 +        
  1.1110 +    srcPositions = m3gMapVertexArrayReadOnly(srcVB->vertices);
  1.1111 +    dstPositions = (M3Gshort*) m3gMapVertexArray(dstVB->vertices);
  1.1112 +    if (srcVB->normals) {
  1.1113 +        srcNormals = m3gMapVertexArrayReadOnly(srcVB->normals);
  1.1114 +        dstNormals = (M3Gbyte*) m3gMapVertexArray(dstVB->normals);
  1.1115 +    }
  1.1116 +        
  1.1117 +    /* Transform the vertices that are affected by bones */
  1.1118 +    {
  1.1119 +        M3Gshort *dst = dstPositions;
  1.1120 +            
  1.1121 +        if (srcVB->vertices->elementType == GL_BYTE) {
  1.1122 +            const M3Gbyte *src = (const M3Gbyte*) srcPositions;
  1.1123 +            for (i = 0; i < vertexCount; ++i) {
  1.1124 +                M3Gint shift =
  1.1125 +                    maxExp - m3gBlendVertex(mesh, i,
  1.1126 +                                            src[0], src[1], src[2],
  1.1127 +                                            posShift,
  1.1128 +                                            dst);
  1.1129 +                if (shift > 31) {
  1.1130 +                    *dst++ = 0;
  1.1131 +                    *dst++ = 0;
  1.1132 +                    *dst++ = 0;
  1.1133 +                }
  1.1134 +                else {
  1.1135 +                    *dst++ >>= shift;
  1.1136 +                    *dst++ >>= shift;
  1.1137 +                    *dst++ >>= shift;
  1.1138 +                }
  1.1139 +            
  1.1140 +                src += 4; /* byte data always padded to 32 bits */
  1.1141 +            }
  1.1142 +        }
  1.1143 +        else {
  1.1144 +            const M3Gshort *src = (const M3Gshort*) srcPositions;
  1.1145 +            for (i = 0; i < vertexCount; ++i) {
  1.1146 +                M3Gint shift =
  1.1147 +                    maxExp - m3gBlendVertex(mesh, i,
  1.1148 +                                            src[0], src[1], src[2],
  1.1149 +                                            posShift,
  1.1150 +                                            dst);
  1.1151 +                if (shift > 31) {
  1.1152 +                    *dst++ = 0;
  1.1153 +                    *dst++ = 0;
  1.1154 +                    *dst++ = 0;
  1.1155 +                }
  1.1156 +                else {
  1.1157 +                    *dst++ >>= shift;
  1.1158 +                    *dst++ >>= shift;
  1.1159 +                    *dst++ >>= shift;
  1.1160 +                }
  1.1161 +                
  1.1162 +                src += 3;
  1.1163 +            }
  1.1164 +        }
  1.1165 +    }
  1.1166 +
  1.1167 +    /* Transform the normals (if enabled).  Normals will be
  1.1168 +     * normalized when rendering, so no need to keep track of
  1.1169 +     * scales here */
  1.1170 +        
  1.1171 +    if (srcNormals) {
  1.1172 +        M3Gbyte *dst = dstNormals;
  1.1173 +            
  1.1174 +        if (srcVB->normals->elementType == GL_BYTE) {
  1.1175 +            const M3Gbyte *src = (const M3Gbyte*) srcNormals;
  1.1176 +            for (i = 0; i < vertexCount; ++i) {
  1.1177 +                m3gBlendNormal(mesh, i,
  1.1178 +                               src[0], src[1], src[2],
  1.1179 +                               normShift,
  1.1180 +                               dst);
  1.1181 +                src += 4; /* byte data padded to 32 bits */
  1.1182 +                dst += 4; 
  1.1183 +            }
  1.1184 +        }
  1.1185 +        else {
  1.1186 +            const M3Gshort *src = (const M3Gshort*) srcNormals;
  1.1187 +            for (i = 0; i < vertexCount; ++i) {
  1.1188 +                m3gBlendNormal(mesh, i,
  1.1189 +                               src[0], src[1], src[2],
  1.1190 +                               normShift,
  1.1191 +                               dst);
  1.1192 +                src += 3;
  1.1193 +                dst += 4; 
  1.1194 +            }
  1.1195 +        }
  1.1196 +    }
  1.1197 +
  1.1198 +    /* Finally, handle the remaining vertices, which have no bones
  1.1199 +     * attached; these just need to have the scale and bias
  1.1200 +     * applied */
  1.1201 +
  1.1202 +    vertexCount = m3gGetNumVertices(srcVB);
  1.1203 +    if (i < vertexCount) {
  1.1204 +            
  1.1205 +        M3Gint startIndex = i;
  1.1206 +        M3Gshort *dstPos = dstPositions + startIndex * 3;
  1.1207 +        M3Gshort temp[3];
  1.1208 +            
  1.1209 +        if (srcVB->vertices->elementType == GL_BYTE) {
  1.1210 +            const M3Gbyte *src = ((const M3Gbyte*) srcPositions) + startIndex * 4;
  1.1211 +            for (i = startIndex ; i < vertexCount; ++i) {
  1.1212 +                M3Gint shift =
  1.1213 +                    maxExp - m3gScaleAndBiasVertex(mesh,
  1.1214 +                                                   src[0], src[1], src[2],
  1.1215 +                                                   posShift,
  1.1216 +                                                   temp);
  1.1217 +                *dstPos++ = (M3Gshort)(temp[0] >> shift);
  1.1218 +                *dstPos++ = (M3Gshort)(temp[1] >> shift);
  1.1219 +                *dstPos++ = (M3Gshort)(temp[2] >> shift);                    
  1.1220 +                src += 4; /* byte data, padded to 32 bits */
  1.1221 +            }
  1.1222 +        }
  1.1223 +        else {
  1.1224 +            const M3Gshort *src = ((const M3Gshort*) srcPositions) + startIndex * 3;
  1.1225 +            for (i = startIndex ; i < vertexCount; ++i) {
  1.1226 +                M3Gint shift =
  1.1227 +                    maxExp - m3gScaleAndBiasVertex(mesh,
  1.1228 +                                                   src[0], src[1], src[2],
  1.1229 +                                                   posShift,
  1.1230 +                                                   temp);
  1.1231 +                *dstPos++ = (M3Gshort)(temp[0] >> shift);
  1.1232 +                *dstPos++ = (M3Gshort)(temp[1] >> shift);
  1.1233 +                *dstPos++ = (M3Gshort)(temp[2] >> shift);                    
  1.1234 +                src += 3;
  1.1235 +            }
  1.1236 +        }
  1.1237 +            
  1.1238 +        /* Byte normals can just use a memcopy, as we don't have
  1.1239 +         * to scale them at all; shorts will require a conversion,
  1.1240 +         * after prescaling with the normal upshift to avoid
  1.1241 +         * underflowing to zero */
  1.1242 +                
  1.1243 +        if (srcNormals) {
  1.1244 +            M3Gbyte *dstNorm = dstNormals + startIndex * 4; 
  1.1245 +            if (srcVB->normals->elementType == GL_BYTE) {
  1.1246 +                const M3Gbyte *src =
  1.1247 +                    ((const M3Gbyte*) srcNormals) + startIndex * 4;
  1.1248 +                m3gCopy(dstNorm, src, (vertexCount - startIndex) * 4);
  1.1249 +            }
  1.1250 +            else {
  1.1251 +                const M3Gshort *src =
  1.1252 +                    ((const M3Gshort*) srcNormals) + startIndex * 3;
  1.1253 +                for (i = startIndex ; i < vertexCount; ++i) {
  1.1254 +                    *dstNorm++ = (M3Gbyte)((*src++ << normShift) >> 8);
  1.1255 +                    *dstNorm++ = (M3Gbyte)((*src++ << normShift) >> 8);
  1.1256 +                    *dstNorm++ = (M3Gbyte)((*src++ << normShift) >> 8);
  1.1257 +                    ++dstNorm; /* again, padding for byte values */
  1.1258 +                }
  1.1259 +            }
  1.1260 +        }
  1.1261 +    }
  1.1262 +        
  1.1263 +    /* All done! Clean up and exit */
  1.1264 +
  1.1265 +    m3gUnmapVertexArray(srcVB->vertices);
  1.1266 +    m3gUnmapVertexArray(dstVB->vertices);
  1.1267 +    if (srcNormals) {
  1.1268 +        m3gUnmapVertexArray(srcVB->normals);
  1.1269 +        m3gUnmapVertexArray(dstVB->normals);
  1.1270 +    }
  1.1271 +}
  1.1272 +
  1.1273 +/*!
  1.1274 + * \internal
  1.1275 + * \brief Overloaded Node method.
  1.1276 + *
  1.1277 + * Setup skinned mesh render. Call mesh render setup,
  1.1278 + * do skinning calculations and traverse into the skeleton or the parent
  1.1279 + *
  1.1280 + * \param self SkinnedMesh object
  1.1281 + * \param toCamera transform to camera
  1.1282 + * \param alphaFactor total alpha factor
  1.1283 + * \param caller caller node
  1.1284 + * \param renderQueue RenderQueue
  1.1285 + *
  1.1286 + * \retval M3G_TRUE continue render setup
  1.1287 + * \retval M3G_FALSE abort render setup
  1.1288 + */
  1.1289 +static M3Gbool m3gSkinnedMeshSetupRender(Node *self,
  1.1290 +                                         const Node *caller,
  1.1291 +                                         SetupRenderState *s,
  1.1292 +                                         RenderQueue *renderQueue)
  1.1293 +{
  1.1294 +    SkinnedMesh *mesh = (SkinnedMesh *)self;
  1.1295 +    Node *skeleton = (Node*) mesh->skeleton;
  1.1296 +    M3Gbool enabled, success = M3G_TRUE;
  1.1297 +    m3gIncStat(M3G_INTERFACE(self), M3G_STAT_RENDER_NODES, 1);
  1.1298 +    
  1.1299 +    /* Optimize the rendering-enable checking for top-down traversal */
  1.1300 +
  1.1301 +    enabled = (self->enableBits & NODE_RENDER_BIT) != 0;
  1.1302 +    if (caller != self->parent) {
  1.1303 +        enabled = m3gHasEnabledPath(self, renderQueue->root);
  1.1304 +        s->cullMask = CULLMASK_ALL;
  1.1305 +    }
  1.1306 +
  1.1307 +    /* Handle self and the skeleton if enabled */
  1.1308 +
  1.1309 +    if (enabled) {
  1.1310 +        
  1.1311 +        /* Traverse into the skeleton unless coming from there */
  1.1312 +    
  1.1313 +        if (skeleton != caller) {
  1.1314 +            SetupRenderState cs;
  1.1315 +            cs.cullMask = s->cullMask;
  1.1316 +        
  1.1317 +            M3G_BEGIN_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
  1.1318 +            m3gGetCompositeNodeTransform(skeleton, &cs.toCamera);
  1.1319 +            m3gPreMultiplyMatrix(&cs.toCamera, &s->toCamera);
  1.1320 +            M3G_END_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
  1.1321 +        
  1.1322 +            success = M3G_VFUNC(Node, skeleton, setupRender)(skeleton,
  1.1323 +                                                             self,
  1.1324 +                                                             &cs,
  1.1325 +                                                             renderQueue);
  1.1326 +        }
  1.1327 +
  1.1328 +        /* Handle self if in scope */
  1.1329 +        
  1.1330 +        if ((self->scope & renderQueue->scope) != 0) {
  1.1331 +
  1.1332 +            /* Try view frustum culling */
  1.1333 +
  1.1334 +#           if defined(M3G_ENABLE_VF_CULLING)
  1.1335 +            m3gUpdateCullingMask(s, renderQueue->camera, &mesh->bbox);
  1.1336 +#           endif
  1.1337 +
  1.1338 +            if (s->cullMask == 0) {
  1.1339 +                m3gIncStat(M3G_INTERFACE(self),
  1.1340 +                           M3G_STAT_RENDER_NODES_CULLED, 1);
  1.1341 +            }
  1.1342 +            else {
  1.1343 +                success &= m3gQueueMesh((Mesh*) self, &s->toCamera, renderQueue);
  1.1344 +                
  1.1345 +                if (success) {
  1.1346 +                    M3G_BEGIN_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SKIN);
  1.1347 +                    m3gSkinnedMeshMorph(mesh);
  1.1348 +                    M3G_END_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SKIN);
  1.1349 +                }
  1.1350 +            }
  1.1351 +        }
  1.1352 +    }
  1.1353 +
  1.1354 +    /* Traverse into the parent node unless coming from there.  Again,
  1.1355 +     * discard the old traversal state at this point, as we're not
  1.1356 +     * coming back. */
  1.1357 +    
  1.1358 +    if (success && self != renderQueue->root) {
  1.1359 +        Node *parent = self->parent;
  1.1360 +        if (parent != NULL && parent != caller) {
  1.1361 +            Matrix t;
  1.1362 +            
  1.1363 +            M3G_BEGIN_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
  1.1364 +            if (!m3gGetInverseNodeTransform(self, &t)) {
  1.1365 +                return M3G_FALSE;
  1.1366 +            }
  1.1367 +            m3gMulMatrix(&s->toCamera, &t);
  1.1368 +            M3G_END_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SETUP_TRANSFORMS);
  1.1369 +
  1.1370 +            success = M3G_VFUNC(Node, parent, setupRender)(parent,
  1.1371 +                                                           self,
  1.1372 +                                                           s,
  1.1373 +                                                           renderQueue);
  1.1374 +        }
  1.1375 +    }
  1.1376 +
  1.1377 +    return success;
  1.1378 +}
  1.1379 +
  1.1380 +/*!
  1.1381 + * \internal
  1.1382 + * \brief Overloaded Node method.
  1.1383 + *
  1.1384 + * Renders one skinned submesh.
  1.1385 + *
  1.1386 + * \param self SkinnedMesh object
  1.1387 + * \param ctx current render context
  1.1388 + * \param patchIndex submesh index
  1.1389 + */
  1.1390 +static void m3gSkinnedMeshDoRender(Node *self,
  1.1391 +                                   RenderContext *ctx,
  1.1392 +                                   const Matrix *toCamera,
  1.1393 +                                   int patchIndex)
  1.1394 +{
  1.1395 +    SkinnedMesh *mesh = (SkinnedMesh *)self;
  1.1396 +    IndexBuffer *indexBuffer = mesh->mesh.indexBuffers[patchIndex];
  1.1397 +    Appearance *appearance = mesh->mesh.appearances[patchIndex];
  1.1398 +
  1.1399 +    if (indexBuffer == NULL || appearance == NULL)
  1.1400 +        return;
  1.1401 +
  1.1402 +    m3gDrawMesh(ctx,
  1.1403 +                mesh->morphedVB,
  1.1404 +                indexBuffer,
  1.1405 +                appearance,
  1.1406 +                toCamera,
  1.1407 +                mesh->mesh.totalAlphaFactor + 1,
  1.1408 +                mesh->mesh.node.scope);
  1.1409 +}
  1.1410 +
  1.1411 +/*!
  1.1412 + * \internal
  1.1413 + * \brief Overloaded Node method.
  1.1414 + *
  1.1415 + * Do skinning calculations and forward to Mesh internal ray intersect.
  1.1416 + *
  1.1417 + * \param self      SkinnedMesh object
  1.1418 + * \param mask      pick scope mask
  1.1419 + * \param ray       pick ray
  1.1420 + * \param ri        RayIntersection object
  1.1421 + * \param toGroup   transform to originating group
  1.1422 + * \retval          M3G_TRUE    continue pick
  1.1423 + * \retval          M3G_FALSE   abort pick
  1.1424 + */
  1.1425 +static M3Gbool m3gSkinnedMeshRayIntersect(  Node *self,
  1.1426 +                                            M3Gint mask,
  1.1427 +                                            M3Gfloat *ray,
  1.1428 +                                            RayIntersection *ri,
  1.1429 +                                            Matrix *toGroup)
  1.1430 +{
  1.1431 +    SkinnedMesh *mesh = (SkinnedMesh *)self;
  1.1432 +    M3G_BEGIN_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SKIN);
  1.1433 +
  1.1434 +    if ((((Node *)mesh)->scope & mask) == 0) {
  1.1435 +        return M3G_TRUE;
  1.1436 +    }
  1.1437 +
  1.1438 +    if (!m3gSkinnedMeshPreMorph(mesh)) {
  1.1439 +        return M3G_FALSE;
  1.1440 +    }
  1.1441 +    m3gSkinnedMeshMorph(mesh);
  1.1442 +    M3G_END_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_SKIN);
  1.1443 +    return m3gMeshRayIntersectInternal( &mesh->mesh,
  1.1444 +                                        mesh->morphedVB,
  1.1445 +                                        mask,
  1.1446 +                                        ray,
  1.1447 +                                        ri,
  1.1448 +                                        toGroup);
  1.1449 +}
  1.1450 +
  1.1451 +/*!
  1.1452 + * \internal
  1.1453 + * \brief Overloaded Object3D method.
  1.1454 + *
  1.1455 + * \param self SkinnedMesh object
  1.1456 + * \param time current world time
  1.1457 + * \return minimum validity
  1.1458 + */
  1.1459 +static M3Gint m3gSkinnedMeshApplyAnimation(Object *self, M3Gint time)
  1.1460 +{
  1.1461 +    SkinnedMesh *mesh = (SkinnedMesh *)self;
  1.1462 +    
  1.1463 +    M3Gint validity = m3gMeshApplyAnimation((Object*) &mesh->mesh, time);
  1.1464 +    
  1.1465 +    if (validity > 0) {
  1.1466 +        M3Gint validity2 =
  1.1467 +            M3G_VFUNC(Object, mesh->skeleton, applyAnimation)(
  1.1468 +                (Object *)mesh->skeleton, time);
  1.1469 +        return (validity < validity2 ? validity : validity2);
  1.1470 +    }
  1.1471 +    return 0;
  1.1472 +}
  1.1473 +
  1.1474 +/*!
  1.1475 + * \internal
  1.1476 + * \brief Overloaded Object3D method.
  1.1477 + *
  1.1478 + * \param self SkinnedMesh object
  1.1479 + * \param references array of reference objects
  1.1480 + * \return number of references
  1.1481 + */
  1.1482 +static M3Gint m3gSkinnedMeshDoGetReferences(Object *self, Object **references)
  1.1483 +{
  1.1484 +    SkinnedMesh *smesh = (SkinnedMesh *)self;
  1.1485 +    M3Gint num = m3gMeshDoGetReferences(self, references);
  1.1486 +    if (smesh->skeleton != NULL)
  1.1487 +    {
  1.1488 +        if (references != NULL)
  1.1489 +            references[num] = (Object *)smesh->skeleton;
  1.1490 +        num++;
  1.1491 +    }
  1.1492 +    return num;
  1.1493 +}
  1.1494 +
  1.1495 +/*!
  1.1496 + * \internal
  1.1497 + * \brief Overloaded Object3D method.
  1.1498 + */
  1.1499 +static Object *m3gSkinnedMeshFindID(Object *self, M3Gint userID)
  1.1500 +{
  1.1501 +    SkinnedMesh *smesh = (SkinnedMesh *)self;
  1.1502 +    Object *found = m3gMeshFindID(self, userID);
  1.1503 +    
  1.1504 +    if (!found && smesh->skeleton != NULL) {
  1.1505 +        found = m3gFindID((Object*) smesh->skeleton, userID);
  1.1506 +    }
  1.1507 +    return found;
  1.1508 +}
  1.1509 +
  1.1510 +/*!
  1.1511 + * \internal
  1.1512 + * \brief Overloaded Object3D method.
  1.1513 + *
  1.1514 + * \param originalObj original SkinnedMesh object
  1.1515 + * \param cloneObj pointer to cloned SkinnedMesh object
  1.1516 + * \param pairs array for all object-duplicate pairs
  1.1517 + * \param numPairs number of pairs
  1.1518 + */
  1.1519 +static M3Gbool m3gSkinnedMeshDuplicate(const Object *originalObj,
  1.1520 +                                       Object **cloneObj,
  1.1521 +                                       Object **pairs,
  1.1522 +                                       M3Gint *numPairs)
  1.1523 +{
  1.1524 +    M3Gint i;
  1.1525 +    SkinnedMesh *original = (SkinnedMesh *)originalObj;
  1.1526 +    Group *skeleton = NULL;
  1.1527 +    SkinnedMesh *clone;
  1.1528 +    M3G_ASSERT(*cloneObj == NULL); /* no derived classes */
  1.1529 +    
  1.1530 +    /* Duplicate the skeleton group first, as this is a prerequisite
  1.1531 +     * for creating the clone SkinnedMesh.  If this fails, we must
  1.1532 +     * manually delete the skeleton, as no record of it will be stored
  1.1533 +     * anywhere else; we also need to hold a reference until ownership
  1.1534 +     * of the skeleton transfers to the clone SkinnedMesh. */
  1.1535 +    
  1.1536 +    if (!M3G_VFUNC(Object, original->skeleton, duplicate)(
  1.1537 +            (Object*) original->skeleton,
  1.1538 +            (Object**) &skeleton, pairs, numPairs)) {
  1.1539 +        m3gDeleteObject((Object*) skeleton);
  1.1540 +        return M3G_FALSE;
  1.1541 +    }
  1.1542 +    m3gAddRef((Object*) skeleton); /* don't leave this floating */
  1.1543 +
  1.1544 +    /* Create the actual clone object */
  1.1545 +    
  1.1546 +    clone = (SkinnedMesh*)
  1.1547 +        m3gCreateSkinnedMesh(originalObj->interface,
  1.1548 +                             original->mesh.vertexBuffer,
  1.1549 +                             original->mesh.indexBuffers,
  1.1550 +                             original->mesh.appearances,
  1.1551 +                             original->mesh.trianglePatchCount,
  1.1552 +                             skeleton);    
  1.1553 +    m3gDeleteRef((Object*) skeleton); /* ownership transferred to clone */
  1.1554 +    if (!clone) {
  1.1555 +        return M3G_FALSE;
  1.1556 +    }
  1.1557 +    *cloneObj = (Object *)clone;
  1.1558 +
  1.1559 +    /* Duplicate base class data; we're OK for normal deletion at this
  1.1560 +     * point, so can just leave it up to the caller on failure */
  1.1561 +    
  1.1562 +    if (!m3gMeshDuplicate(originalObj, cloneObj, pairs, numPairs)) {
  1.1563 +        return M3G_FALSE;
  1.1564 +    }
  1.1565 +
  1.1566 +    /* Duplicate the rest of our own data */
  1.1567 +
  1.1568 +    if (!m3gEnsureVertexCount(clone, original->weightedVertexCount) ||
  1.1569 +        !m3gEnsureBonesPerVertex(clone, original->bonesPerVertex)) {
  1.1570 +        return M3G_FALSE; /* out of memory */
  1.1571 +    }
  1.1572 +    
  1.1573 +    for (i = 0; i < clone->bonesPerVertex; i++) {
  1.1574 +        m3gCopy(clone->boneIndices[i], original->boneIndices[i],
  1.1575 +                clone->weightedVertexCount);
  1.1576 +        m3gCopy(clone->boneWeights[i], original->boneWeights[i],
  1.1577 +                clone->weightedVertexCount);
  1.1578 +        m3gCopy(clone->normalizedWeights[i], original->normalizedWeights[i],
  1.1579 +                clone->weightedVertexCount);
  1.1580 +    }
  1.1581 +    clone->weightsDirty = original->weightsDirty;
  1.1582 +    m3gCopy(clone->weightShifts, original->weightShifts,
  1.1583 +            clone->weightedVertexCount);
  1.1584 +
  1.1585 +    for (i = 0; i < m3gArraySize(&original->bones); i++) {
  1.1586 +        Bone *cloneBone = (Bone*) m3gAllocZ(originalObj->interface,
  1.1587 +                                            sizeof(Bone));
  1.1588 +        if (!cloneBone) {
  1.1589 +            return M3G_FALSE; /* out of memory */
  1.1590 +        }
  1.1591 +        /* this line looks odd, but really just copies the *contents*
  1.1592 +         * of the bone structure... */
  1.1593 +        *cloneBone = *(Bone*)m3gGetArrayElement(&original->bones, i);
  1.1594 +
  1.1595 +        if (m3gArrayAppend(&clone->bones, cloneBone, originalObj->interface) < 0) {
  1.1596 +            m3gFree(originalObj->interface, cloneBone);
  1.1597 +            return M3G_FALSE; /* out of memory */
  1.1598 +        }
  1.1599 +    }
  1.1600 +    
  1.1601 +    return M3G_TRUE;
  1.1602 +}
  1.1603 +
  1.1604 +/*!
  1.1605 + * \internal
  1.1606 + * \brief Overloaded Node method
  1.1607 + */
  1.1608 +static M3Gint m3gSkinnedMeshGetBBox(Node *self, AABB *bbox)
  1.1609 +{
  1.1610 +    SkinnedMesh *mesh = (SkinnedMesh*) self;
  1.1611 +    Node *skeleton = (Node*) mesh->skeleton;
  1.1612 +
  1.1613 +    /* First update our local bounding box if necessary */
  1.1614 +    
  1.1615 +    if (self->dirtyBits & NODE_BBOX_BIT) {
  1.1616 +        
  1.1617 +        /* Compute an estimated bounding box from the morphed vertex
  1.1618 +         * buffer scale and bias (from PreComputeTransformations).
  1.1619 +         * The morphed vertex array is always scaled to utilize most
  1.1620 +         * of the 16-bit short range, so we just use that as the
  1.1621 +         * extents. */
  1.1622 +        {
  1.1623 +            const GLfloat scale = mesh->morphedVB->vertexScale;
  1.1624 +            const GLfloat *bias = mesh->morphedVB->vertexBias;
  1.1625 +            int i;
  1.1626 +            
  1.1627 +            for (i = 0; i < 3; ++i) {
  1.1628 +                mesh->bbox.min[i] = m3gMadd(scale, -1 << 15, bias[i]);
  1.1629 +                mesh->bbox.max[i] = m3gMadd(scale, (1 << 15) - 1, bias[i]);
  1.1630 +            }
  1.1631 +        }
  1.1632 +    }
  1.1633 +    *bbox = mesh->bbox;
  1.1634 +    
  1.1635 +    /* Mix in the skeleton bounding box if we need to -- but only into
  1.1636 +     * the output bbox, as we're handling the local mesh bbox
  1.1637 +     * specially in SetupRender! */
  1.1638 +        
  1.1639 +    if (skeleton->hasRenderables && skeleton->enableBits) {
  1.1640 +        AABB skeletonBBox;
  1.1641 +        if (m3gGetNodeBBox(skeleton, &skeletonBBox)) {
  1.1642 +            Matrix t;
  1.1643 +            m3gGetCompositeNodeTransform(self, &t);
  1.1644 +            m3gTransformAABB(&skeletonBBox, &t);
  1.1645 +            m3gFitAABB(bbox, &skeletonBBox, bbox);
  1.1646 +        }
  1.1647 +    }    
  1.1648 +    return m3gArraySize(&mesh->bones) * VFC_NODE_OVERHEAD;
  1.1649 +}
  1.1650 +
  1.1651 +/*!
  1.1652 + * \internal
  1.1653 + * \brief Overloaded Node method
  1.1654 + */
  1.1655 +static M3Gbool m3gSkinnedMeshValidate(Node *self, M3Gbitmask stateBits, M3Gint scope)
  1.1656 +{
  1.1657 +    SkinnedMesh *mesh = (SkinnedMesh*) self;
  1.1658 +    Interface *m3g = M3G_INTERFACE(mesh);
  1.1659 +    Node *skeleton = (Node*) mesh->skeleton;
  1.1660 +    const VertexBuffer *srcVB = mesh->mesh.vertexBuffer;
  1.1661 +    M3Gint vertexCount = mesh->weightedVertexCount;
  1.1662 +
  1.1663 +    if ((scope & self->scope) != 0) {
  1.1664 +        if (stateBits & self->enableBits) {
  1.1665 +            
  1.1666 +            /* Check for invalid SkinnedMesh state */
  1.1667 +        
  1.1668 +            if (srcVB->vertices == NULL || vertexCount > srcVB->vertexCount) {
  1.1669 +                m3gRaiseError(m3g, M3G_INVALID_OPERATION);
  1.1670 +                return M3G_FALSE;
  1.1671 +            }
  1.1672 +            if (!m3gSkinnedMeshUpdateVB(mesh)) { /* Memory allocation failed */
  1.1673 +                return M3G_FALSE;
  1.1674 +            }
  1.1675 +        
  1.1676 +            /* Validate the skeleton */
  1.1677 +        
  1.1678 +            if (!m3gValidateNode(skeleton, stateBits, scope)) {
  1.1679 +                return M3G_FALSE;
  1.1680 +            }
  1.1681 +    
  1.1682 +            /* Validate our local state */
  1.1683 +    
  1.1684 +            if ((self->dirtyBits & NODE_TRANSFORMS_BIT) != 0 || 
  1.1685 +                m3gGetTimestamp(srcVB) != mesh->mesh.vbTimestamp) {
  1.1686 +                if (!m3gSkinnedMeshPreMorph((SkinnedMesh*) self)) {
  1.1687 +                    return M3G_FALSE;
  1.1688 +                }
  1.1689 +            }
  1.1690 +            if (self->dirtyBits & NODE_BBOX_BIT) {
  1.1691 +                M3G_BEGIN_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_VFC_UPDATE);
  1.1692 +                m3gGetNodeBBox(self, &mesh->bbox);
  1.1693 +                M3G_END_PROFILE(M3G_INTERFACE(self), M3G_PROFILE_VFC_UPDATE);
  1.1694 +            }
  1.1695 +    
  1.1696 +            return m3gMeshValidate(self, stateBits, scope);
  1.1697 +        }
  1.1698 +    }
  1.1699 +    return M3G_TRUE;
  1.1700 +}
  1.1701 +
  1.1702 +/*!
  1.1703 + * \internal
  1.1704 + * \brief Overloaded Object3D method.
  1.1705 + *
  1.1706 + * \param self SkinnedMesh object
  1.1707 + * \param pairs array for all object-duplicate pairs
  1.1708 + * \param numPairs number of pairs
  1.1709 + */
  1.1710 +static void m3gSkinnedMeshUpdateDuplicateReferences(Node *self, Object **pairs, M3Gint numPairs)
  1.1711 +{
  1.1712 +    SkinnedMesh *skinned = (SkinnedMesh *)self;
  1.1713 +    SkinnedMesh *duplicate = (SkinnedMesh *)m3gGetDuplicatedInstance(self, pairs, numPairs);
  1.1714 +    M3Gint i, n;
  1.1715 +    
  1.1716 +    m3gNodeUpdateDuplicateReferences(self, pairs, numPairs);
  1.1717 +    
  1.1718 +    n = m3gArraySize(&duplicate->bones);
  1.1719 +    for (i = 0; i < n; i++) {
  1.1720 +        Bone *bone = (Bone*) m3gGetArrayElement(&duplicate->bones, i);
  1.1721 +        Node *boneDuplicate = m3gGetDuplicatedInstance(bone->node, pairs, numPairs);
  1.1722 +        if (boneDuplicate != NULL) {
  1.1723 +            bone->node = boneDuplicate;
  1.1724 +        }
  1.1725 +    }
  1.1726 +    
  1.1727 +    M3G_VFUNC(Node, skinned->skeleton, updateDuplicateReferences)(
  1.1728 +        (Node *)skinned->skeleton, pairs, numPairs);
  1.1729 +}
  1.1730 +
  1.1731 +/*!
  1.1732 + * \internal
  1.1733 + * \brief Initializes a SkinnedMesh object. See specification
  1.1734 + * for default values.
  1.1735 + *
  1.1736 + * \param m3g                   M3G interface
  1.1737 + * \param mesh           SkinnedMesh object
  1.1738 + * \param hVertices             VertexBuffer object
  1.1739 + * \param hTriangles            array of IndexBuffer objects
  1.1740 + * \param hAppearances          array of Appearance objects
  1.1741 + * \param trianglePatchCount    number of submeshes
  1.1742 + * \param hSkeleton             Group containing the skeleton
  1.1743 + * \retval                      M3G_TRUE success
  1.1744 + * \retval                      M3G_FALSE failure
  1.1745 + */
  1.1746 +static M3Gbool m3gInitSkinnedMesh(Interface *m3g,
  1.1747 +                                  SkinnedMesh *mesh,
  1.1748 +                                  M3GVertexBuffer hVertices,
  1.1749 +                                  M3GIndexBuffer *hTriangles,
  1.1750 +                                  M3GAppearance *hAppearances,
  1.1751 +                                  M3Gint trianglePatchCount,
  1.1752 +                                  M3GGroup hSkeleton)
  1.1753 +{
  1.1754 +    /* SkinnedMesh is derived from Mesh */
  1.1755 +    if (!m3gInitMesh(m3g, &mesh->mesh,
  1.1756 +                     hVertices, hTriangles, hAppearances,
  1.1757 +                     trianglePatchCount,
  1.1758 +                     M3G_CLASS_SKINNED_MESH))
  1.1759 +    {
  1.1760 +        return M3G_FALSE;
  1.1761 +    }
  1.1762 +
  1.1763 +    /* Make sure our mesh gets blended even if no bones are added */    
  1.1764 +    ((Node*)mesh)->dirtyBits |= NODE_TRANSFORMS_BIT;
  1.1765 +        
  1.1766 +    /* Set default values, see RI SkinnedMesh.java for reference */
  1.1767 +    m3gSetParent(&((Group *)hSkeleton)->node, &mesh->mesh.node);
  1.1768 +    M3G_ASSIGN_REF(mesh->skeleton, (Group *)hSkeleton);
  1.1769 +
  1.1770 +    m3gInitArray(&mesh->bones);
  1.1771 +    
  1.1772 +    mesh->morphedVB = (VertexBuffer *)m3gCreateVertexBuffer(m3g);
  1.1773 +    if (mesh->morphedVB == NULL
  1.1774 +        || m3gSkinnedMeshUpdateVB(mesh) == M3G_FALSE) {
  1.1775 +        
  1.1776 +        /* We're sufficiently initialized at this point that the
  1.1777 +         * destructor can be called for cleaning up */
  1.1778 +        
  1.1779 +        m3gDestroySkinnedMesh((Object *)mesh);
  1.1780 +        return M3G_FALSE;
  1.1781 +    }
  1.1782 +    return M3G_TRUE;
  1.1783 +}
  1.1784 +
  1.1785 +/*----------------------------------------------------------------------
  1.1786 + * Virtual function table
  1.1787 + *--------------------------------------------------------------------*/
  1.1788 +
  1.1789 +static const NodeVFTable m3gvf_SkinnedMesh = {
  1.1790 +    {
  1.1791 +        {
  1.1792 +            m3gSkinnedMeshApplyAnimation,
  1.1793 +            m3gNodeIsCompatible,
  1.1794 +            m3gNodeUpdateProperty,
  1.1795 +            m3gSkinnedMeshDoGetReferences,
  1.1796 +            m3gSkinnedMeshFindID,
  1.1797 +            m3gSkinnedMeshDuplicate,
  1.1798 +            m3gDestroySkinnedMesh
  1.1799 +        }
  1.1800 +    },
  1.1801 +    m3gNodeAlign,
  1.1802 +    m3gSkinnedMeshDoRender,
  1.1803 +    m3gSkinnedMeshGetBBox,
  1.1804 +    m3gSkinnedMeshRayIntersect,
  1.1805 +    m3gSkinnedMeshSetupRender,
  1.1806 +    m3gSkinnedMeshUpdateDuplicateReferences,
  1.1807 +    m3gSkinnedMeshValidate
  1.1808 +};
  1.1809 +
  1.1810 +
  1.1811 +/*----------------------------------------------------------------------
  1.1812 + * Public API functions
  1.1813 + *--------------------------------------------------------------------*/
  1.1814 +
  1.1815 +/*!
  1.1816 + * \brief Creates a SkinnedMesh object.
  1.1817 + *
  1.1818 + * \param interface             M3G interface
  1.1819 + * \param hVertices             VertexBuffer object
  1.1820 + * \param hTriangles            array of IndexBuffer objects
  1.1821 + * \param hAppearances          array of Appearance objects
  1.1822 + * \param trianglePatchCount    number of submeshes
  1.1823 + * \param hSkeleton             Group containing the skeleton
  1.1824 + * \retval                      SkinnedMesh new SkinnedMesh object
  1.1825 + * \retval                      NULL SkinnedMesh creating failed
  1.1826 + */
  1.1827 +M3G_API M3GSkinnedMesh m3gCreateSkinnedMesh(M3GInterface interface,
  1.1828 +                                            M3GVertexBuffer hVertices,
  1.1829 +                                            M3GIndexBuffer *hTriangles,
  1.1830 +                                            M3GAppearance *hAppearances,
  1.1831 +                                            M3Gint trianglePatchCount,
  1.1832 +                                            M3GGroup hSkeleton)
  1.1833 +{
  1.1834 +    Interface *m3g = (Interface *) interface;
  1.1835 +    M3G_VALIDATE_INTERFACE(m3g);
  1.1836 +    {
  1.1837 +        SkinnedMesh *mesh = NULL;
  1.1838 +        Group *skeleton = (Group *) hSkeleton;
  1.1839 +        if (skeleton == NULL) {
  1.1840 +            m3gRaiseError(m3g, M3G_NULL_POINTER);
  1.1841 +            return NULL;
  1.1842 +        }
  1.1843 +        if (skeleton->node.parent != NULL ||
  1.1844 +            M3G_CLASS(skeleton) == M3G_CLASS_WORLD) {
  1.1845 +            m3gRaiseError(m3g, M3G_INVALID_VALUE);
  1.1846 +            return NULL;
  1.1847 +        }
  1.1848 +        
  1.1849 +        mesh = m3gAllocZ(m3g, sizeof(SkinnedMesh));
  1.1850 +        if (mesh) {
  1.1851 +            if (!m3gInitSkinnedMesh(m3g, mesh,
  1.1852 +                                    hVertices, hTriangles, hAppearances,
  1.1853 +                                    trianglePatchCount,
  1.1854 +                                    hSkeleton)) {
  1.1855 +                m3gFree(m3g, mesh);
  1.1856 +                return NULL;
  1.1857 +            }
  1.1858 +        }
  1.1859 +        return (M3GSkinnedMesh)mesh;
  1.1860 +    }
  1.1861 +}
  1.1862 +
  1.1863 +/*!
  1.1864 + * \brief Add new weighted transformation (bone) to range of vertices
  1.1865 + *
  1.1866 + * 
  1.1867 + * \param handle        SkinnedMesh object
  1.1868 + * \param hNode         bone to transform the vertices with
  1.1869 + * \param weight        weight of the bone
  1.1870 + * \param firstVertex   index to the first affected vertex
  1.1871 + * \param numVertices   number of affected vertices
  1.1872 + */
  1.1873 +M3G_API void m3gAddTransform(M3GSkinnedMesh handle,
  1.1874 +                             M3GNode hNode,
  1.1875 +                             M3Gint weight,
  1.1876 +                             M3Gint firstVertex, M3Gint numVertices)
  1.1877 +{
  1.1878 +    SkinnedMesh *mesh = (SkinnedMesh *)handle;
  1.1879 +    Node *boneNode = (Node *)hNode;
  1.1880 +    Interface *m3g = M3G_INTERFACE(mesh);
  1.1881 +    
  1.1882 +    M3Gint lastVertex = firstVertex + numVertices;
  1.1883 +    M3G_VALIDATE_OBJECT(mesh);
  1.1884 +
  1.1885 +    /* Check for errors */
  1.1886 +    
  1.1887 +    if (!boneNode) {
  1.1888 +        m3gRaiseError(m3g, M3G_NULL_POINTER);
  1.1889 +        return;
  1.1890 +    }
  1.1891 +    M3G_VALIDATE_OBJECT(boneNode);
  1.1892 +    if (!m3gIsChildOf((const Node*) mesh, boneNode)
  1.1893 +        || numVertices <= 0 || weight <= 0) {
  1.1894 +        m3gRaiseError(m3g, M3G_INVALID_VALUE);
  1.1895 +        return;
  1.1896 +    }
  1.1897 +    if (firstVertex < 0 || lastVertex > 65535) {
  1.1898 +        m3gRaiseError(m3g, M3G_INVALID_INDEX);
  1.1899 +        return;
  1.1900 +    }
  1.1901 +
  1.1902 +    /* Make sure we have enough per-vertex data */
  1.1903 +    
  1.1904 +    if (!m3gEnsureVertexCount(mesh, lastVertex)) {
  1.1905 +        return; /* out of memory */
  1.1906 +    }
  1.1907 +    
  1.1908 +    /* Check whether we may need to increase the number of bone
  1.1909 +     * entries per vertex, or whether we're already maxed out */
  1.1910 +    
  1.1911 +    if (mesh->bonesPerVertex < M3G_MAX_VERTEX_TRANSFORMS) {
  1.1912 +
  1.1913 +        /* Scan the input vertex range to find the maximum number of
  1.1914 +         * transforms per vertex (with non-zero weights) already in
  1.1915 +         * use, then make sure we can fit one more */
  1.1916 +        
  1.1917 +        int numBones = mesh->bonesPerVertex;
  1.1918 +        int maxBones = 0;
  1.1919 +        
  1.1920 +        int vertex;
  1.1921 +        for (vertex = firstVertex; vertex < lastVertex; ++vertex) {
  1.1922 +            int k;
  1.1923 +            for (k = numBones; k > 0; --k) {
  1.1924 +                if (mesh->boneWeights[k-1][vertex] > 0) {
  1.1925 +                    maxBones = M3G_MAX(maxBones,  k);
  1.1926 +                    break;
  1.1927 +                }
  1.1928 +            }
  1.1929 +        }
  1.1930 +        if (!m3gEnsureBonesPerVertex(mesh, maxBones + 1)) {
  1.1931 +            return; /* out of memory */
  1.1932 +        }
  1.1933 +    }
  1.1934 +    
  1.1935 +    /* Get a bone record for the bone node, and add the bone influence
  1.1936 +     * to all affected vertices */
  1.1937 +    {
  1.1938 +        int i;
  1.1939 +        
  1.1940 +        M3Gint boneIndex = m3gBoneIndex(mesh, boneNode);
  1.1941 +        if (boneIndex < 0) {
  1.1942 +            return; /* out of memory */
  1.1943 +        }
  1.1944 +        
  1.1945 +        for (i = firstVertex; i < lastVertex; i++) {
  1.1946 +            m3gAddInfluence(mesh, i, boneIndex, weight);
  1.1947 +        }
  1.1948 +    }
  1.1949 +    
  1.1950 +    /* Update the bone flag for the bone node and its parents up to
  1.1951 +     * the SkinnedMesh node */
  1.1952 +    
  1.1953 +    while (boneNode != (Node*) mesh) { /* boneNode must be a child of ours */
  1.1954 +        M3G_ASSERT(boneNode);
  1.1955 +        boneNode->hasBones = M3G_TRUE;
  1.1956 +        boneNode = boneNode->parent;
  1.1957 +    }
  1.1958 +}
  1.1959 +
  1.1960 +/*!
  1.1961 + * \brief Getter for skeleton.
  1.1962 + *
  1.1963 + * \param handle                SkinnedMesh object
  1.1964 + * \return                      Group object
  1.1965 + */
  1.1966 +M3G_API M3GGroup m3gGetSkeleton(M3GSkinnedMesh handle)
  1.1967 +{
  1.1968 +    SkinnedMesh *mesh = (SkinnedMesh *)handle;
  1.1969 +    M3G_VALIDATE_OBJECT(mesh);
  1.1970 +
  1.1971 +    return mesh->skeleton;
  1.1972 +}
  1.1973 +
  1.1974 +/*!
  1.1975 + * \brief Getter for bone transform.
  1.1976 + *
  1.1977 + * \param handle                SkinnedMesh object
  1.1978 + * \param hBone                 Bone
  1.1979 + * \param transform             Transform
  1.1980 + */
  1.1981 +M3G_API void m3gGetBoneTransform(M3GSkinnedMesh handle,
  1.1982 +                                 M3GNode hBone,
  1.1983 +                                 M3GMatrix *transform)
  1.1984 +{
  1.1985 +    SkinnedMesh *mesh = (SkinnedMesh *)handle;
  1.1986 +    Node *node = (Node *)hBone;
  1.1987 +    M3Gint i;
  1.1988 +    M3Gint boneCount;
  1.1989 +
  1.1990 +    M3G_VALIDATE_OBJECT(mesh);
  1.1991 +    M3G_VALIDATE_OBJECT(node);
  1.1992 +
  1.1993 +    if (!m3gIsChildOf((Node*) mesh->skeleton, node)) {
  1.1994 +        m3gRaiseError(M3G_INTERFACE(mesh), M3G_INVALID_VALUE);
  1.1995 +        return;
  1.1996 +    }   
  1.1997 +    
  1.1998 +    boneCount = m3gArraySize(&mesh->bones);
  1.1999 +
  1.2000 +    for (i = 0; i < boneCount; ++i) {
  1.2001 +        Bone *bone = m3gGetArrayElement(&mesh->bones, i);
  1.2002 +
  1.2003 +        if (bone->node == node) {
  1.2004 +            m3gCopyMatrix(transform, &bone->toBone);
  1.2005 +            break;
  1.2006 +        }
  1.2007 +    }
  1.2008 +}
  1.2009 +
  1.2010 +/*!
  1.2011 + * \brief Getter for bone vertices.
  1.2012 + *
  1.2013 + * \param handle                SkinnedMesh object
  1.2014 + * \param hBone                 Bone
  1.2015 + * \param indices               Influenced indices
  1.2016 + * \param weights               Weights
  1.2017 + * \return                      Number of influenced vertices
  1.2018 + */
  1.2019 +M3G_API M3Gint m3gGetBoneVertices(M3GSkinnedMesh handle,
  1.2020 +                                  M3GNode hBone,
  1.2021 +                                  M3Gint *indices, M3Gfloat *weights)
  1.2022 +{
  1.2023 +    SkinnedMesh *mesh = (SkinnedMesh *)handle;
  1.2024 +    Node *node = (Node *)hBone;
  1.2025 +    M3Gint boneIndex, boneCount, count = 0;
  1.2026 +
  1.2027 +    M3G_VALIDATE_OBJECT(mesh);
  1.2028 +    M3G_VALIDATE_OBJECT(node);
  1.2029 +
  1.2030 +    /* Check for errors */
  1.2031 +
  1.2032 +    if (!m3gIsChildOf((Node*) mesh->skeleton, node)) {
  1.2033 +        m3gRaiseError(M3G_INTERFACE(mesh), M3G_INVALID_VALUE);
  1.2034 +        return 0;
  1.2035 +    }   
  1.2036 +        
  1.2037 +    /* Find the bone index corresponding to our bone node */
  1.2038 +    
  1.2039 +    boneCount = m3gArraySize(&mesh->bones);
  1.2040 +
  1.2041 +    for (boneIndex = 0; boneIndex < boneCount; ++boneIndex) {
  1.2042 +        Bone *bone = m3gGetArrayElement(&mesh->bones, boneIndex);
  1.2043 +        if (bone->node == node) {
  1.2044 +            break;
  1.2045 +        }
  1.2046 +    }
  1.2047 +
  1.2048 +    /* Loop over the vertices, outputting index-weight pairs for each
  1.2049 +     * vertex influenced by the bone */
  1.2050 +
  1.2051 +    if (boneIndex < boneCount) {
  1.2052 +        M3Gint i, j;
  1.2053 +
  1.2054 +        for (i = 0; i < mesh->weightedVertexCount; ++i) {
  1.2055 +            for (j = 0; j < mesh->bonesPerVertex; ++j) {
  1.2056 +                if (mesh->boneIndices[j][i] == boneIndex && mesh->boneWeights[j][i] > 0) {
  1.2057 +                    if (indices != NULL && weights != NULL) {
  1.2058 +                        M3Gint k, sum = 0;
  1.2059 +                        for (k = 0; k < mesh->bonesPerVertex; ++k) {
  1.2060 +                            sum += mesh->boneWeights[k][i];
  1.2061 +                        }
  1.2062 +                        indices[count] = i;
  1.2063 +                        if (sum != 0) {
  1.2064 +                            weights[count] = ((M3Gfloat) mesh->boneWeights[j][i]) / sum;
  1.2065 +                        }
  1.2066 +                        else {
  1.2067 +                            weights[count] = 0;
  1.2068 +                        }
  1.2069 +                    }
  1.2070 +                    ++count;
  1.2071 +                }
  1.2072 +            }                    
  1.2073 +        }
  1.2074 +    }
  1.2075 +    return count;
  1.2076 +}
  1.2077 +