예제 #1
0
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);
   }
}