bool AreSkelMeshVerticesEqual( const FSoftSkinBuildVertex& V1, const FSoftSkinBuildVertex& V2 ) { if(!PointsEqual(V1.Position, V2.Position)) { return false; } bool bUVsEqual = true; for(int32 UVIdx = 0; UVIdx < MAX_TEXCOORDS; ++UVIdx) { if(FMath::Abs(V1.UVs[UVIdx].X - V2.UVs[UVIdx].X) >(1.0f / 1024.0f)) { bUVsEqual = false; }; if(FMath::Abs(V1.UVs[UVIdx].Y - V2.UVs[UVIdx].Y) > (1.0f / 1024.0f)) { bUVsEqual = false; } } if(!bUVsEqual) { return false; } if(!NormalsEqual(V1.TangentX, V2.TangentX)) { return false; } if(!NormalsEqual(V1.TangentY, V2.TangentY)) { return false; } if(!NormalsEqual(V1.TangentZ, V2.TangentZ)) { return false; } bool InfluencesMatch = 1; for(uint32 InfluenceIndex = 0; InfluenceIndex < MAX_TOTAL_INFLUENCES; InfluenceIndex++) { if(V1.InfluenceBones[InfluenceIndex] != V2.InfluenceBones[InfluenceIndex] || V1.InfluenceWeights[InfluenceIndex] != V2.InfluenceWeights[InfluenceIndex]) { InfluencesMatch = 0; break; } } if(!InfluencesMatch) { return false; } return true; }
int32 AddSkinVertex(TArray<FSoftSkinBuildVertex>& Vertices,FSoftSkinBuildVertex& Vertex, bool bKeepOverlappingVertices ) { if (!bKeepOverlappingVertices) { for(uint32 VertexIndex = 0;VertexIndex < (uint32)Vertices.Num();VertexIndex++) { FSoftSkinBuildVertex& OtherVertex = Vertices[VertexIndex]; if(!PointsEqual(OtherVertex.Position,Vertex.Position)) continue; bool bUVsEqual = true; for( int32 UVIdx = 0; UVIdx < MAX_TEXCOORDS; ++UVIdx ) { if(FMath::Abs(Vertex.UVs[UVIdx].X - OtherVertex.UVs[UVIdx].X) > (1.0f / 1024.0f)) { bUVsEqual = false; }; if(FMath::Abs(Vertex.UVs[UVIdx].Y - OtherVertex.UVs[UVIdx].Y) > (1.0f / 1024.0f)) { bUVsEqual = false; } } if( !bUVsEqual ) continue; if(!NormalsEqual( OtherVertex.TangentX, Vertex.TangentX)) continue; if(!NormalsEqual(OtherVertex.TangentY, Vertex.TangentY)) continue; if(!NormalsEqual(OtherVertex.TangentZ, Vertex.TangentZ)) continue; bool InfluencesMatch = 1; for(uint32 InfluenceIndex = 0;InfluenceIndex < MAX_TOTAL_INFLUENCES;InfluenceIndex++) { if( Vertex.InfluenceBones[InfluenceIndex] != OtherVertex.InfluenceBones[InfluenceIndex] || Vertex.InfluenceWeights[InfluenceIndex] != OtherVertex.InfluenceWeights[InfluenceIndex]) { InfluencesMatch = 0; break; } } if(!InfluencesMatch) continue; return VertexIndex; } } return Vertices.Add(Vertex); }
void BuildSkeletalMeshChunks( const TArray<FMeshFace>& Faces, const TArray<FSoftSkinBuildVertex>& RawVertices, TArray<FSkeletalMeshVertIndexAndZ>& RawVertIndexAndZ, bool bKeepOverlappingVertices, TArray<FSkinnedMeshChunk*>& OutChunks, bool& bOutTooManyVerts ) { TArray<int32> DupVerts; TMultiMap<int32, int32> RawVerts2Dupes; { // Sorting function for vertex Z/index pairs struct FCompareFSkeletalMeshVertIndexAndZ { FORCEINLINE bool operator()(const FSkeletalMeshVertIndexAndZ& A, const FSkeletalMeshVertIndexAndZ& B) const { return A.Z < B.Z; } }; // Sort the vertices by z value RawVertIndexAndZ.Sort(FCompareFSkeletalMeshVertIndexAndZ()); // Search for duplicates, quickly! for(int32 i = 0; i < RawVertIndexAndZ.Num(); i++) { // only need to search forward, since we add pairs both ways for(int32 j = i + 1; j < RawVertIndexAndZ.Num(); j++) { if(FMath::Abs(RawVertIndexAndZ[j].Z - RawVertIndexAndZ[i].Z) > THRESH_POINTS_ARE_SAME) { // our list is sorted, so there can't be any more dupes break; } // check to see if the points are really overlapping if(PointsEqual( RawVertices[RawVertIndexAndZ[i].Index].Position, RawVertices[RawVertIndexAndZ[j].Index].Position)) { RawVerts2Dupes.Add(RawVertIndexAndZ[i].Index, RawVertIndexAndZ[j].Index); RawVerts2Dupes.Add(RawVertIndexAndZ[j].Index, RawVertIndexAndZ[i].Index); } } } } TMap<FSkinnedMeshChunk* , TMap<int32, int32> > ChunkToFinalVerts; uint32 TriangleIndices[3]; for(int32 FaceIndex = 0; FaceIndex < Faces.Num(); FaceIndex++) { const FMeshFace& Face = Faces[FaceIndex]; // Find a chunk which matches this triangle. FSkinnedMeshChunk* Chunk = NULL; for(int32 i = 0; i < OutChunks.Num(); ++i) { if(OutChunks[i]->MaterialIndex == Face.MeshMaterialIndex) { Chunk = OutChunks[i]; break; } } if(Chunk == NULL) { Chunk = new FSkinnedMeshChunk(); Chunk->MaterialIndex = Face.MeshMaterialIndex; Chunk->OriginalSectionIndex = OutChunks.Num(); OutChunks.Add(Chunk); } TMap<int32, int32>& FinalVerts = ChunkToFinalVerts.FindOrAdd( Chunk ); for(int32 VertexIndex = 0; VertexIndex < 3; ++VertexIndex) { int32 WedgeIndex = FaceIndex * 3 + VertexIndex; const FSoftSkinBuildVertex& Vertex = RawVertices[WedgeIndex]; int32 FinalVertIndex = INDEX_NONE; if(bKeepOverlappingVertices) { FinalVertIndex = Chunk->Vertices.Add(RawVertices[WedgeIndex]); } else { DupVerts.Reset(); RawVerts2Dupes.MultiFind(WedgeIndex, DupVerts); DupVerts.Sort(); for(int32 k = 0; k < DupVerts.Num(); k++) { if(DupVerts[k] >= WedgeIndex) { // the verts beyond me haven't been placed yet, so these duplicates are not relevant break; } int32 *Location = FinalVerts.Find(DupVerts[k]); if(Location != NULL) { if(SkeletalMeshTools::AreSkelMeshVerticesEqual(Vertex, Chunk->Vertices[*Location])) { FinalVertIndex = *Location; break; } } } if(FinalVertIndex == INDEX_NONE) { FinalVertIndex = Chunk->Vertices.Add(Vertex); FinalVerts.Add(WedgeIndex, FinalVertIndex); } } // set the index entry for the newly added vertex #if DISALLOW_32BIT_INDICES if(FinalVertIndex > MAX_uint16) { bOutTooManyVerts = true; } TriangleIndices[VertexIndex] = (uint16)FinalVertIndex; #else // TArray internally has int32 for capacity, so no need to test for uint32 as it's larger than int32 TriangleIndices[VertexIndex] = (uint32)FinalVertIndex; #endif } if(TriangleIndices[0] != TriangleIndices[1] && TriangleIndices[0] != TriangleIndices[2] && TriangleIndices[1] != TriangleIndices[2]) { for(uint32 VertexIndex = 0; VertexIndex < 3; VertexIndex++) { Chunk->Indices.Add(TriangleIndices[VertexIndex]); } } } }