diff --git a/Engine/source/ts/tsMesh.cpp b/Engine/source/ts/tsMesh.cpp index 54c4beeaa..4249efee1 100644 --- a/Engine/source/ts/tsMesh.cpp +++ b/Engine/source/ts/tsMesh.cpp @@ -1301,6 +1301,8 @@ void TSSkinMesh::createBatchData() // Temp vector to build batch operations Vector batchOperations; + bool issuedWeightWarning = false; + // Build the batch operations while( curVtx != endVtx ) { @@ -1313,13 +1315,43 @@ void TSSkinMesh::createBatchData() const F32 w = *curWeight; ++curWeight; + // Ignore empty weights + if ( vidx < 0 || midx < 0 || w == 0 ) + continue; + if( !batchOperations.empty() && batchOperations.last().vertexIndex == vidx ) { 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].weight = w; @@ -1337,6 +1369,25 @@ void TSSkinMesh::createBatchData() } //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 // Copy data to member, and be done batchData.vertexBatchOperations.set(batchOperations.address(), batchOperations.size());