void SortTriangles_CenterRadialDistance( FVector SortCenter, int32 NumTriangles, const FSoftSkinVertex* Vertices, uint32* Indices ) { // Get the set number for each triangle TArray<uint32> TriSet; int32 MaxTriSet = GetConnectedTriangleSets( NumTriangles, Indices, TriSet ); TArray<FTriStripSortInfo> Strips; Strips.AddZeroed(MaxTriSet); for( int32 TriIndex=0;TriIndex<TriSet.Num();TriIndex++ ) { Strips[TriSet[TriIndex]].Triangles.Add(TriIndex); } for( int32 s=0;s<Strips.Num();s++ ) { if( Strips[s].Triangles.Num() == 0 ) { Strips.RemoveAt(s); s--; continue; } FVector StripCenter(0,0,0); for( int32 TriIndex=0;TriIndex<Strips[s].Triangles.Num();TriIndex++ ) { int32 tri = Strips[s].Triangles[TriIndex]; uint32 i1 = Indices[tri*3+0]; uint32 i2 = Indices[tri*3+1]; uint32 i3 = Indices[tri*3+2]; FVector TriCenter = (Vertices[i1].Position + Vertices[i2].Position + Vertices[i3].Position) / 3.f; StripCenter += TriCenter; } StripCenter /= Strips[s].Triangles.Num(); Strips[s].SortKey = (StripCenter-SortCenter).SizeSquared(); } struct FCompareSortKey { FORCEINLINE bool operator()( const FTriStripSortInfo& A, const FTriStripSortInfo& B ) const { return A.SortKey < B.SortKey; } }; Strips.Sort( FCompareSortKey() ); // export new draw order TArray<uint32> NewIndices; NewIndices.Empty(NumTriangles*3); for( int32 s=0;s<Strips.Num();s++ ) { int32 StripStartIndex = NewIndices.Num(); for( int32 TriIndex=0;TriIndex<Strips[s].Triangles.Num();TriIndex++ ) { int32 tri = Strips[s].Triangles[TriIndex]; NewIndices.Add(Indices[tri*3+0]); NewIndices.Add(Indices[tri*3+1]); NewIndices.Add(Indices[tri*3+2]); } CacheOptimizeSortStrip( &NewIndices[StripStartIndex], Strips[s].Triangles.Num()*3 ); } FMemory::Memcpy( Indices, NewIndices.GetData(), NewIndices.Num() * sizeof(uint32) ); }
void FSavedCustomSortSectionInfo::Restore(USkeletalMesh* NewSkelMesh, int32 LODModelIndex, TArray<int32>& UnmatchedSections) { FStaticLODModel& LODModel = NewSkelMesh->GetImportedResource()->LODModels[LODModelIndex]; FSkeletalMeshLODInfo& LODInfo = NewSkelMesh->LODInfo[LODModelIndex]; // Re-order the UnmatchedSections so the old section index from the previous model is tried first int32 PrevSectionIndex = UnmatchedSections.Find(SavedSectionIdx); if( PrevSectionIndex != 0 && PrevSectionIndex != INDEX_NONE ) { Exchange( UnmatchedSections[0], UnmatchedSections[PrevSectionIndex] ); } // Find the strips in the old triangle data. TArray< TArray<uint32> > OldStrips[2]; for( int32 IndexCopy=0; IndexCopy < (SavedSortOption==TRISORT_CustomLeftRight ? 2 : 1); IndexCopy++ ) { const uint32* OldIndices = &SavedIndices[(SavedIndices.Num()>>1)*IndexCopy]; TArray<uint32> OldTriSet; GetConnectedTriangleSets( SavedNumTriangles, OldIndices, OldTriSet ); // Convert to strips int32 PrevTriSet = MAX_int32; for( int32 TriIndex=0;TriIndex<SavedNumTriangles; TriIndex++ ) { if( OldTriSet[TriIndex] != PrevTriSet ) { OldStrips[IndexCopy].AddZeroed(); PrevTriSet = OldTriSet[TriIndex]; } OldStrips[IndexCopy][OldStrips[IndexCopy].Num()-1].Add(OldIndices[TriIndex*3+0]); OldStrips[IndexCopy][OldStrips[IndexCopy].Num()-1].Add(OldIndices[TriIndex*3+1]); OldStrips[IndexCopy][OldStrips[IndexCopy].Num()-1].Add(OldIndices[TriIndex*3+2]); } } bool bFoundMatchingSection = false; // Try all remaining sections to find a match for( int32 UnmatchedSectionsIdx=0; !bFoundMatchingSection && UnmatchedSectionsIdx<UnmatchedSections.Num(); UnmatchedSectionsIdx++ ) { // Section of the new mesh to try int32 SectionIndex = UnmatchedSections[UnmatchedSectionsIdx]; FSkelMeshSection& Section = LODModel.Sections[SectionIndex]; TArray<uint32> Indices; LODModel.MultiSizeIndexContainer.GetIndexBuffer( Indices ); const uint32* NewSectionIndices = Indices.GetData() + Section.BaseIndex; // Build the list of triangle sets in the new mesh's section TArray<uint32> TriSet; GetConnectedTriangleSets( Section.NumTriangles, NewSectionIndices, TriSet ); // Mapping from triangle set number to the array of indices that make up the contiguous strip. TMap<uint32, TArray<uint32> > NewStripsMap; // Go through each triangle and assign it to the appropriate contiguous strip. // This is necessary if the strips in the index buffer are not contiguous. int32 Index=0; for( int32 s=0;s<TriSet.Num();s++ ) { // Store the indices for this triangle in the appropriate contiguous set. TArray<uint32>* ThisStrip = NewStripsMap.Find(TriSet[s]); if( !ThisStrip ) { ThisStrip = &NewStripsMap.Add(TriSet[s],TArray<uint32>()); } // Add the three indices for this triangle. ThisStrip->Add(NewSectionIndices[Index++]); ThisStrip->Add(NewSectionIndices[Index++]); ThisStrip->Add(NewSectionIndices[Index++]); } // Get the new vertices TArray<FSoftSkinVertex> NewVertices; LODModel.GetVertices(NewVertices); // Do the processing once for each copy if the index data for( int32 IndexCopy=0; IndexCopy < (SavedSortOption==TRISORT_CustomLeftRight ? 2 : 1); IndexCopy++ ) { // Copy strips in the new mesh's section into an array. We'll remove items from // here as we match to the old strips, so we need to keep a new copy of it each time. TArray<TArray<uint32> > NewStrips; for( TMap<uint32, TArray<uint32> >::TIterator It(NewStripsMap); It; ++It ) { NewStrips.Add(It.Value()); } // Match up old strips to new int32 NumMismatchedStrips = 0; TArray<TArray<uint32> > NewSortedStrips; // output for( int32 OsIdx=0;OsIdx<OldStrips[IndexCopy].Num();OsIdx++ ) { TArray<uint32>& OldStripIndices = OldStrips[IndexCopy][OsIdx]; int32 MatchingNewStrip = INDEX_NONE; for( int32 NsIdx=0;NsIdx<NewStrips.Num() && MatchingNewStrip==INDEX_NONE;NsIdx++ ) { // Check if we have the same number of triangles in the old and new strips. if( NewStrips[NsIdx].Num() != OldStripIndices.Num() ) { continue; } // Make a copy of the indices, as we'll remove them as we try to match triangles. TArray<uint32> NewStripIndices = NewStrips[NsIdx]; // Check if all the triangles in the new strip closely match those in the old. for( int32 OldTriIdx=0;OldTriIdx<OldStripIndices.Num();OldTriIdx+=3 ) { // Try to find a match for this triangle in the new strip. bool FoundMatch = false; for( int32 NewTriIdx=0;NewTriIdx<NewStripIndices.Num();NewTriIdx+=3 ) { if( (SavedVertices[OldStripIndices[OldTriIdx+0]] - NewVertices[NewStripIndices[NewTriIdx+0]].Position).SizeSquared() < KINDA_SMALL_NUMBER && (SavedVertices[OldStripIndices[OldTriIdx+1]] - NewVertices[NewStripIndices[NewTriIdx+1]].Position).SizeSquared() < KINDA_SMALL_NUMBER && (SavedVertices[OldStripIndices[OldTriIdx+2]] - NewVertices[NewStripIndices[NewTriIdx+2]].Position).SizeSquared() < KINDA_SMALL_NUMBER ) { // Found a triangle match. Remove the triangle from the new list and try to match the next old triangle. NewStripIndices.RemoveAt(NewTriIdx,3); FoundMatch = true; break; } } // If we didn't find a match for this old triangle, the whole strip doesn't match. if( !FoundMatch ) { break; } } if( NewStripIndices.Num() == 0 ) { // strip completely matched MatchingNewStrip = NsIdx; } } if( MatchingNewStrip != INDEX_NONE ) { NewSortedStrips.Add( NewStrips[MatchingNewStrip] ); NewStrips.RemoveAt(MatchingNewStrip); } else { NumMismatchedStrips++; } } if( IndexCopy == 0 ) { if( 100 * NumMismatchedStrips / OldStrips[0].Num() > 50 ) { // If less than 50% of this section's strips match, we assume this is not the correct section. break; } // This section matches! bFoundMatchingSection = true; // Warn the user if we couldn't match things up. if( NumMismatchedStrips ) { UnFbx::FFbxImporter* FFbxImporter = UnFbx::FFbxImporter::GetInstance(); FFbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Warning, FText::Format(LOCTEXT("RestoreSortingMismatchedStripsForSection", "While restoring \"{0}\" sort order for section {1}, {2} of {3} strips could not be matched to the new data."), FText::FromString(TriangleSortOptionToString((ETriangleSortOption)SavedSortOption)), FText::AsNumber(SavedSectionIdx), FText::AsNumber(NumMismatchedStrips), FText::AsNumber(OldStrips[0].Num())))); } // Restore the settings saved in the LODInfo (used for the UI) FTriangleSortSettings& TriangleSortSettings = LODInfo.TriangleSortSettings[SectionIndex]; TriangleSortSettings.TriangleSorting = SavedSortOption; TriangleSortSettings.CustomLeftRightAxis = SavedCustomLeftRightAxis; TriangleSortSettings.CustomLeftRightBoneName = SavedCustomLeftRightBoneName; // Restore the sorting mode. For TRISORT_CustomLeftRight, this will also make the second copy of the index data. FVector SortCenter; bool bHaveSortCenter = NewSkelMesh->GetSortCenterPoint(SortCenter); LODModel.SortTriangles(SortCenter, bHaveSortCenter, SectionIndex, (ETriangleSortOption)SavedSortOption); } // Append any strips we couldn't match to the end NewSortedStrips += NewStrips; // Export the strips out to the index buffer in order TArray<uint32> Indexes; LODModel.MultiSizeIndexContainer.GetIndexBuffer( Indexes ); uint32* NewIndices = Indexes.GetData() + (Section.BaseIndex + Section.NumTriangles*3*IndexCopy); for( int32 StripIdx=0;StripIdx<NewSortedStrips.Num();StripIdx++ ) { FMemory::Memcpy( NewIndices, &NewSortedStrips[StripIdx][0], NewSortedStrips[StripIdx].Num() * sizeof(uint32) ); // Cache-optimize the triangle order inside the final strip CacheOptimizeSortStrip( NewIndices, NewSortedStrips[StripIdx].Num() ); NewIndices += NewSortedStrips[StripIdx].Num(); } LODModel.MultiSizeIndexContainer.CopyIndexBuffer( Indexes ); } } if( !bFoundMatchingSection ) { UnFbx::FFbxImporter* FFbxImporter = UnFbx::FFbxImporter::GetInstance(); FFbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Warning, FText::Format(LOCTEXT("FailedRestoreSortingNoSectionMatch", "Unable to restore triangle sort setting \"{0}\" for section number {1} in the old mesh, as a matching section could not be found in the new mesh. The custom sorting information has been lost."), FText::FromString(TriangleSortOptionToString((ETriangleSortOption)SavedSortOption)), FText::AsNumber(SavedSectionIdx)))); } }
void SortTriangles_None( int32 NumTriangles, const FSoftSkinVertex* Vertices, uint32* Indices ) { CacheOptimizeSortStrip( Indices, NumTriangles*3 ); }