void TSShapeLoader::zapScale(MatrixF& mat) { Point3F invScale = mat.getScale(); invScale.x = invScale.x ? (1.0f / invScale.x) : 0; invScale.y = invScale.y ? (1.0f / invScale.y) : 0; invScale.z = invScale.z ? (1.0f / invScale.z) : 0; mat.scale(invScale); }
void ColladaAppMesh::lookupSkinData() { // Only lookup skin data once if (!isSkin() || weight.size()) return; // Get the skin and vertex weight data const domSkin* skin = daeSafeCast<domController>(instanceCtrl->getUrl().getElement())->getSkin(); const domSkin::domVertex_weights& weightIndices = *(skin->getVertex_weights()); const domListOfInts& weights_v = weightIndices.getV()->getValue(); const domListOfUInts& weights_vcount = weightIndices.getVcount()->getValue(); MeshStreams streams; streams.readInputs(skin->getJoints()->getInput_array()); streams.readInputs(weightIndices.getInput_array()); MatrixF invObjOffset(objectOffset); invObjOffset.inverse(); // Get the bind shape matrix MatrixF bindShapeMatrix(true); if (skin->getBind_shape_matrix()) bindShapeMatrix = vecToMatrixF<domMatrix>(skin->getBind_shape_matrix()->getValue()); bindShapeMatrix.mul(invObjOffset); // Determine the offset into the vindices array for each vertex (since each // vertex may have multiple [bone, weight] pairs in the array) Vector<U32> vindicesOffset; const domInt* vindices = (domInt*)weights_v.getRaw(0); for (int iWeight = 0; iWeight < weights_vcount.getCount(); iWeight++) { // Store the offset into the vindices array for this vertex vindicesOffset.push_back(vindices - (domInt*)weights_v.getRaw(0)); vindices += (weights_vcount[iWeight]*2); // 2 indices [bone, weight] per vert } // Set vertex weights bool tooManyWeightsWarning = false; for (int iVert = 0; iVert < vertsPerFrame; iVert++) { const domUint* vcount = (domUint*)weights_vcount.getRaw(0); const domInt* vindices = (domInt*)weights_v.getRaw(0); vindices += vindicesOffset[vertTuples[iVert].vertex]; S32 nonZeroWeightCount = 0; for (int iWeight = 0; iWeight < vcount[vertTuples[iVert].vertex]; iWeight++) { S32 bIndex = vindices[iWeight*2]; F32 bWeight = streams.weights.getFloatValue( vindices[iWeight*2 + 1] ); // Ignore empty weights if ( bIndex < 0 || bWeight == 0 ) continue; // Limit the number of weights per bone (keep the N largest influences) if ( nonZeroWeightCount >= TSSkinMesh::BatchData::maxBonePerVert ) { if (vcount[vertTuples[iVert].vertex] > TSSkinMesh::BatchData::maxBonePerVert) { if (!tooManyWeightsWarning) { tooManyWeightsWarning = true; daeErrorHandler::get()->handleWarning(avar("At least one vertex has " "too many bone weights. Limiting to the largest %d influences.", TSSkinMesh::BatchData::maxBonePerVert)); } } // Too many weights => find and replace the smallest one S32 minIndex = weight.size() - TSSkinMesh::BatchData::maxBonePerVert; F32 minWeight = weight[minIndex]; for (S32 i = minIndex + 1; i < weight.size(); i++) { if (weight[i] < minWeight) { minWeight = weight[i]; minIndex = i; } } boneIndex[minIndex] = bIndex; weight[minIndex] = bWeight; } else { vertexIndex.push_back( iVert ); boneIndex.push_back( bIndex ); weight.push_back( bWeight ); nonZeroWeightCount++; } } } // Normalize vertex weights (force weights for each vert to sum to 1) int iWeight = 0; while (iWeight < weight.size()) { // Find the last weight with the same vertex number, and sum all weights for // that vertex F32 invTotalWeight = 0; int iLast; for (iLast = iWeight; iLast < weight.size(); iLast++) { if (vertexIndex[iLast] != vertexIndex[iWeight]) break; invTotalWeight += weight[iLast]; } // Then normalize the vertex weights invTotalWeight = 1.0f / invTotalWeight; for (; iWeight < iLast; iWeight++) weight[iWeight] *= invTotalWeight; } // Add dummy AppNodes to allow Collada joints to be mapped to 3space nodes bones.setSize(streams.joints.size()); initialTransforms.setSize(streams.joints.size()); for (int iJoint = 0; iJoint < streams.joints.size(); iJoint++) { const char* jointName = streams.joints.getStringValue(iJoint); // Lookup the joint element const domNode* joint = 0; if (instanceCtrl->getSkeleton_array().getCount()) { // Search for the node using the <skeleton> as the base element for (int iSkel = 0; iSkel < instanceCtrl->getSkeleton_array().getCount(); iSkel++) { xsAnyURI skeleton = instanceCtrl->getSkeleton_array()[iSkel]->getValue(); daeSIDResolver resolver(skeleton.getElement(), jointName); joint = daeSafeCast<domNode>(resolver.getElement()); if (joint) break; } } else { // Search for the node from the root level daeSIDResolver resolver(skin->getDocument()->getDomRoot(), jointName); joint = daeSafeCast<domNode>(resolver.getElement()); } if (!joint) { daeErrorHandler::get()->handleWarning(avar("Failed to find bone '%s', " "defaulting to instance_controller parent node '%s'", jointName, appNode->getName())); joint = appNode->getDomNode(); } bones[iJoint] = new ColladaAppNode(joint); initialTransforms[iJoint] = objectOffset; // Bone scaling is generally ignored during import, since 3space only // stores default node transform and rotation. Compensate for this by // removing the scaling from the inverse bind transform as well MatrixF invBind = streams.invBindMatrices.getMatrixFValue(iJoint); if (!ColladaUtils::getOptions().ignoreNodeScale) { Point3F invScale = invBind.getScale(); invScale.x = invScale.x ? (1.0f / invScale.x) : 0; invScale.y = invScale.y ? (1.0f / invScale.y) : 0; invScale.z = invScale.z ? (1.0f / invScale.z) : 0; initialTransforms[iJoint].scale(invScale); } // Inverted node coordinate spaces (negative scale factor) are corrected // in ColladaAppNode::getNodeTransform, so need to apply the same operation // here to match if (m_matF_determinant(invBind) < 0.0f) initialTransforms[iJoint].scale(Point3F(1, 1, -1)); initialTransforms[iJoint].mul(invBind); initialTransforms[iJoint].mul(bindShapeMatrix); } }