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 +