Browse Source

Fix for Issue #66 for Skinned mesh crash

David Wyand 13 years ago
parent
commit
75d9470029
1 changed files with 53 additions and 2 deletions
  1. 53 2
      Engine/source/ts/tsMesh.cpp

+ 53 - 2
Engine/source/ts/tsMesh.cpp

@@ -1301,6 +1301,8 @@ void TSSkinMesh::createBatchData()
    // Temp vector to build batch operations
    // Temp vector to build batch operations
    Vector<BatchData::BatchedVertex> batchOperations;
    Vector<BatchData::BatchedVertex> batchOperations;
 
 
+   bool issuedWeightWarning = false;
+
    // Build the batch operations
    // Build the batch operations
    while( curVtx != endVtx )
    while( curVtx != endVtx )
    {
    {
@@ -1313,13 +1315,43 @@ void TSSkinMesh::createBatchData()
       const F32 w = *curWeight;
       const F32 w = *curWeight;
       ++curWeight;
       ++curWeight;
 
 
+      // Ignore empty weights
+      if ( vidx < 0 || midx < 0 || w == 0 )
+         continue;
+
       if( !batchOperations.empty() &&
       if( !batchOperations.empty() &&
          batchOperations.last().vertexIndex == vidx )
          batchOperations.last().vertexIndex == vidx )
       {
       {
          AssertFatal( batchOperations.last().transformCount > 0, "Not sure how this happened!" );
          AssertFatal( batchOperations.last().transformCount > 0, "Not sure how this happened!" );
 
 
-         const int opIdx = batchOperations.last().transformCount++;
-         AssertISV( BatchData::maxBonePerVert > opIdx, "Too many bones affecting the same vertex, increase the size of 'TSMesh::BatchData::maxBonePerVert'" );
+         S32 opIdx = batchOperations.last().transformCount++;
+
+         // Limit the number of weights per bone (keep the N largest influences)
+         if ( opIdx >= TSSkinMesh::BatchData::maxBonePerVert )
+         {
+            if ( !issuedWeightWarning )
+            {
+               issuedWeightWarning = true;
+               Con::warnf( "At least one vertex has too many bone weights - limiting "
+                  "to the largest %d influences (see maxBonePerVert in tsMesh.h).",
+                  TSSkinMesh::BatchData::maxBonePerVert );
+            }
+
+            // Too many weights => find and replace the smallest one
+            S32 minIndex = 0;
+            F32 minWeight = batchOperations.last().transform[0].weight;
+            for ( S32 i = 1; i < batchOperations.last().transformCount; i++ )
+            {
+               if ( batchOperations.last().transform[i].weight < minWeight )
+               {
+                  minWeight = batchOperations.last().transform[i].weight;
+                  minIndex = i;
+               }
+            }
+
+            opIdx = minIndex;
+            batchOperations.last().transformCount = TSSkinMesh::BatchData::maxBonePerVert;
+         }
 
 
          batchOperations.last().transform[opIdx].transformIndex = midx;
          batchOperations.last().transform[opIdx].transformIndex = midx;
          batchOperations.last().transform[opIdx].weight = w;
          batchOperations.last().transform[opIdx].weight = w;
@@ -1337,6 +1369,25 @@ void TSSkinMesh::createBatchData()
    }
    }
    //Con::printf("End skin update");
    //Con::printf("End skin update");
 
 
+   // Normalize vertex weights (force weights for each vert to sum to 1)
+   if ( issuedWeightWarning )
+   {
+      for ( S32 i = 0; i < batchOperations.size(); i++ )
+      {
+         BatchData::BatchedVertex& batchOp = batchOperations[i];
+
+         // Sum weights for this vertex
+         F32 invTotalWeight = 0;
+         for ( S32 j = 0; j < batchOp.transformCount; j++ )
+            invTotalWeight += batchOp.transform[j].weight;
+
+         // Then normalize the vertex weights
+         invTotalWeight = 1.0f / invTotalWeight;
+         for ( S32 j = 0; j < batchOp.transformCount; j++ )
+            batchOp.transform[j].weight *= invTotalWeight;
+      }
+   }
+
 #ifdef _BATCH_BY_VERTEX
 #ifdef _BATCH_BY_VERTEX
    // Copy data to member, and be done
    // Copy data to member, and be done
    batchData.vertexBatchOperations.set(batchOperations.address(), batchOperations.size());
    batchData.vertexBatchOperations.set(batchOperations.address(), batchOperations.size());