// Copyright Epic Games, Inc. All Rights Reserved. #include "OverlappingCorners.h" #include "MeshUtilitiesCommon.h" // Create a mesh view from arrays of vertices & indices struct FOverlappingCornersArrayMeshView : FLayoutUV::IMeshView { const TArray& Vertices; const TArray& Indices; FOverlappingCornersArrayMeshView(const TArray& InVertices, const TArray& InIndices) : Vertices(InVertices) , Indices(InIndices) { } // IMeshView Interface -- virtual uint32 GetNumIndices() const override { return Indices.Num(); } virtual FVector3f GetPosition(uint32 Index) const override { return Vertices[Indices[Index]]; } virtual FVector3f GetNormal(uint32 Index) const override { check(0); return FVector3f(); } virtual FVector2f GetInputTexcoord(uint32 Index) const override { check(0); return FVector2f(); } virtual void InitOutputTexcoords(uint32 Num) override { check(0); } virtual void SetOutputTexcoord(uint32 Index, const FVector2f& Value) override { check(0); } // IMeshView Interface -- }; FOverlappingCorners::FOverlappingCorners(const TArray& InVertices, const TArray& InIndices, float ComparisonThreshold) : FOverlappingCorners(FOverlappingCornersArrayMeshView(InVertices, InIndices), ComparisonThreshold) { } FOverlappingCorners::FOverlappingCorners(const FLayoutUV::IMeshView& MeshView, float ComparisonThreshold) { const int32 NumWedges = MeshView.GetNumIndices(); // Create a list of vertex Z/index pairs TArray VertIndexAndZ; VertIndexAndZ.Reserve(NumWedges); for (int32 WedgeIndex = 0; WedgeIndex < NumWedges; WedgeIndex++) { VertIndexAndZ.Emplace(WedgeIndex, MeshView.GetPosition(WedgeIndex)); } // Sort the vertices by z value VertIndexAndZ.Sort(FCompareIndexAndZ()); Init(NumWedges); // Search for duplicates, quickly! for (int32 i = 0; i < VertIndexAndZ.Num(); i++) { // only need to search forward, since we add pairs both ways for (int32 j = i + 1; j < VertIndexAndZ.Num(); j++) { if (FMath::Abs(VertIndexAndZ[j].Z - VertIndexAndZ[i].Z) > ComparisonThreshold) break; // can't be any more dups const FVector3f& PositionA = MeshView.GetPosition(VertIndexAndZ[i].Index); const FVector3f& PositionB = MeshView.GetPosition(VertIndexAndZ[j].Index); if (PointsEqual(PositionA, PositionB, ComparisonThreshold)) { Add(VertIndexAndZ[i].Index, VertIndexAndZ[j].Index); } } } FinishAdding(); } void FOverlappingCorners::Init(int32 NumIndices) { Arrays.Reset(); Sets.Reset(); bFinishedAdding = false; IndexBelongsTo.Reset(NumIndices); IndexBelongsTo.AddUninitialized(NumIndices); FMemory::Memset(IndexBelongsTo.GetData(), 0xFF, NumIndices * sizeof(int32)); } void FOverlappingCorners::Add(int32 Key, int32 Value) { check(Key != Value); check(bFinishedAdding == false); int32 ContainerIndex = IndexBelongsTo[Key]; if (ContainerIndex == INDEX_NONE) { ContainerIndex = Arrays.Num(); TArray& Container = Arrays.AddDefaulted_GetRef(); Container.Reserve(6); Container.Add(Key); Container.Add(Value); IndexBelongsTo[Key] = ContainerIndex; IndexBelongsTo[Value] = ContainerIndex; } else { IndexBelongsTo[Value] = ContainerIndex; TArray& ArrayContainer = Arrays[ContainerIndex]; if (ArrayContainer.Num() == 1) { // Container is a set Sets[ArrayContainer.Last()].Add(Value); } else { // Container is an array ArrayContainer.AddUnique(Value); // Change container into set when one vertex is shared by large number of triangles if (ArrayContainer.Num() > 12) { int32 SetIndex = Sets.Num(); TSet& Set = Sets.AddDefaulted_GetRef(); Set.Append(ArrayContainer); // Having one element means we are using a set // An array will never have just 1 element normally because we add them as pairs ArrayContainer.Reset(1); ArrayContainer.Add(SetIndex); } } } } void FOverlappingCorners::FinishAdding() { check(bFinishedAdding == false); for (TArray& Array : Arrays) { // Turn sets back into arrays for easier iteration code // Also reduces peak memory later in the import process if (Array.Num() == 1) { TSet& Set = Sets[Array.Last()]; Array.Reset(Set.Num()); for (int32 i : Set) { Array.Add(i); } } // Sort arrays now to avoid sort multiple times Array.Sort(); } Sets.Empty(); bFinishedAdding = true; } uint32 FOverlappingCorners::GetAllocatedSize(void) const { uint32 BaseMemoryAllocated = IndexBelongsTo.GetAllocatedSize() + Arrays.GetAllocatedSize() + Sets.GetAllocatedSize(); uint32 ArraysMemory = 0; for (const TArray& ArrayIt : Arrays) { ArraysMemory += ArrayIt.GetAllocatedSize(); } uint32 SetsMemory = 0; for (const TSet& SetsIt : Sets) { SetsMemory += SetsIt.GetAllocatedSize(); } return BaseMemoryAllocated + ArraysMemory + SetsMemory; }