// Copyright Epic Games, Inc. All Rights Reserved. #include "Sculpting/MeshSculptUtil.h" #include "Async/ParallelFor.h" #include "Async/Async.h" using namespace UE::Geometry; void UE::SculptUtil::RecalculateNormals_Overlay( FDynamicMesh3* Mesh, const TSet& ModifiedTris, TSet& VertexSetBuffer, TArray& NormalsBuffer) { FDynamicMeshNormalOverlay* Normals = Mesh->HasAttributes() ? Mesh->Attributes()->PrimaryNormals() : nullptr; check(Normals != nullptr); NormalsBuffer.Reset(); VertexSetBuffer.Reset(); for (int32 TriangleID : ModifiedTris) { FIndex3i TriElems = Normals->GetTriangle(TriangleID); if (TriElems.A != INDEX_NONE) { for (int32 j = 0; j < 3; ++j) { int32 elemid = TriElems[j]; if (VertexSetBuffer.Contains(elemid) == false) { VertexSetBuffer.Add(elemid); NormalsBuffer.Add(elemid); } } } } ParallelFor(NormalsBuffer.Num(), [&](int32 k) { int32 elemid = NormalsBuffer[k]; FVector3d NewNormal = FMeshNormals::ComputeOverlayNormal(*Mesh, Normals, elemid); Normals->SetElement(elemid, (FVector3f)NewNormal); }); } void UE::SculptUtil::RecalculateNormals_PerVertex( FDynamicMesh3* Mesh, const TSet& ModifiedTris, TSet& VertexSetBuffer, TArray& NormalsBuffer) { NormalsBuffer.Reset(); VertexSetBuffer.Reset(); for (int32 TriangleID : ModifiedTris) { FIndex3i TriV = Mesh->GetTriangle(TriangleID); for (int32 j = 0; j < 3; ++j) { int32 vid = TriV[j]; if (VertexSetBuffer.Contains(vid) == false) { VertexSetBuffer.Add(vid); NormalsBuffer.Add(vid); } } } ParallelFor(NormalsBuffer.Num(), [&](int32 k) { int32 vid = NormalsBuffer[k]; FVector3d NewNormal = FMeshNormals::ComputeVertexNormal(*Mesh, vid); Mesh->SetVertexNormal(vid, (FVector3f)NewNormal); }); } void UE::SculptUtil::RecalculateROINormals( FDynamicMesh3* Mesh, const TSet& TriangleROI, TSet& VertexSetBuffer, TArray& NormalsBuffer, bool bForceVertex) { if (Mesh->HasAttributes() && Mesh->Attributes()->PrimaryNormals() && bForceVertex == false) { RecalculateNormals_Overlay(Mesh, TriangleROI, VertexSetBuffer, NormalsBuffer); } else { RecalculateNormals_PerVertex(Mesh, TriangleROI, VertexSetBuffer, NormalsBuffer); } } void UE::SculptUtil::RecalculateNormals_Overlay( FDynamicMesh3* Mesh, const TSet& ModifiedTris, FUniqueIndexSet& VertexSetTemp) { FDynamicMeshNormalOverlay* Normals = Mesh->HasAttributes() ? Mesh->Attributes()->PrimaryNormals() : nullptr; check(Normals != nullptr); VertexSetTemp.Initialize(Normals->MaxElementID()); for (int32 TriangleID : ModifiedTris) { FIndex3i TriElems = Normals->GetTriangle(TriangleID); if (TriElems.A != INDEX_NONE) { VertexSetTemp.Add(TriElems.A); VertexSetTemp.Add(TriElems.B); VertexSetTemp.Add(TriElems.C); } } const TArray& ElemIndices = VertexSetTemp.Indices(); ParallelFor(ElemIndices.Num(), [&](int32 k) { int32 elemid = ElemIndices[k]; FVector3d NewNormal = FMeshNormals::ComputeOverlayNormal(*Mesh, Normals, elemid); Normals->SetElement(elemid, (FVector3f)NewNormal); }); } void UE::SculptUtil::RecalculateNormals_PerVertex( FDynamicMesh3* Mesh, const TSet& ModifiedTris, FUniqueIndexSet& VertexSetTemp) { VertexSetTemp.Initialize(Mesh->MaxVertexID()); for (int32 TriangleID : ModifiedTris) { const FIndex3i& Triangle = Mesh->GetTriangleRef(TriangleID); VertexSetTemp.Add(Triangle.A); VertexSetTemp.Add(Triangle.B); VertexSetTemp.Add(Triangle.C); } const TArray& ElemIndices = VertexSetTemp.Indices(); ParallelFor(ElemIndices.Num(), [&](int32 k) { int32 vid = ElemIndices[k]; FVector3d NewNormal = FMeshNormals::ComputeVertexNormal(*Mesh, vid); Mesh->SetVertexNormal(vid, (FVector3f)NewNormal); }); } void UE::SculptUtil::RecalculateROINormals( FDynamicMesh3* Mesh, const TSet& TriangleROI, FUniqueIndexSet& VertexSetTemp, bool bForceVertex) { if (Mesh->HasAttributes() && Mesh->Attributes()->PrimaryNormals() && bForceVertex == false) { RecalculateNormals_Overlay(Mesh, TriangleROI, VertexSetTemp); } else { RecalculateNormals_PerVertex(Mesh, TriangleROI, VertexSetTemp); } } static FAxisAlignedBox3d ParallelComputeROIBounds(const FDynamicMesh3& Mesh, const TArray& Triangles) { FAxisAlignedBox3d FinalBounds = FAxisAlignedBox3d::Empty(); FCriticalSection FinalBoundsLock; int32 N = Triangles.Num(); constexpr int32 BlockSize = 4096; int32 Blocks = (N / BlockSize) + 1; ParallelFor(Blocks, [&](int bi) { FAxisAlignedBox3d BlockBounds = FAxisAlignedBox3d::Empty(); for (int32 k = 0; k < BlockSize; ++k) { int32 i = bi * BlockSize + k; if (i < N) { int32 tid = Triangles[i]; const FIndex3i& TriV = Mesh.GetTriangleRef(tid); BlockBounds.Contain(Mesh.GetVertexRef(TriV.A)); BlockBounds.Contain(Mesh.GetVertexRef(TriV.B)); BlockBounds.Contain(Mesh.GetVertexRef(TriV.C)); } } FinalBoundsLock.Lock(); FinalBounds.Contain(BlockBounds); FinalBoundsLock.Unlock(); }); return FinalBounds; } namespace { // probably should be something defined for the whole tool framework... #if WITH_EDITOR static EAsyncExecution MeshSculptUtilAsyncExecTarget = EAsyncExecution::LargeThreadPool; #else static EAsyncExecution MeshSculptUtilAsyncExecTarget = EAsyncExecution::ThreadPool; #endif } void UE::SculptUtil::PrecalculateNormalsROI( const FDynamicMesh3* Mesh, const TArray& TriangleROI, FUniqueIndexSet& IndexSetTemp, bool& bIsOverlayElements, bool bForceVertex) { const FDynamicMeshNormalOverlay* Normals = Mesh->HasAttributes() ? Mesh->Attributes()->PrimaryNormals() : nullptr; if (Normals != nullptr && bForceVertex == false) { bIsOverlayElements = true; static TArray List; static TArray> Flags; if (Flags.Num() != Normals->MaxElementID()) { Flags.SetNum(Normals->MaxElementID()); } { TRACE_CPUPROFILER_EVENT_SCOPE(PrecalculateNormalsROI_InitClear); ParallelFor(Normals->MaxElementID(), [&](int32 i) { Flags[i] = false; }); } { TRACE_CPUPROFILER_EVENT_SCOPE(PrecalculateNormalsROI_FIND); List.Reset(); FCriticalSection ListLock; int32 N = TriangleROI.Num(); ParallelFor(TriangleROI.Num(), [&](int i) { FIndex3i TriElems = Normals->GetTriangle(TriangleROI[i]); Flags[TriElems.A] = true; Flags[TriElems.B] = true; Flags[TriElems.C] = true; //bool PrevA = false, PrevB = false, PrevC = false; //bool SetA = Flags[TriElems.A].compare_exchange_strong(PrevA, true); //bool SetB = Flags[TriElems.B].compare_exchange_strong(PrevB, true); //bool SetC = Flags[TriElems.C].compare_exchange_strong(PrevC, true); //if ( SetA || SetB || SetC ) //{ // ListLock.Lock(); // if (SetA) List.Add(TriElems.A); // if (SetB) List.Add(TriElems.B); // if (SetC) List.Add(TriElems.C); // ListLock.Unlock(); //} }); } { TRACE_CPUPROFILER_EVENT_SCOPE(PrecalculateNormalsROI_NORMALS); ParallelFor(Normals->MaxElementID(), [&](int32 elemid) { if (Flags[elemid] == true) { FVector3d NewNormal = FMeshNormals::ComputeOverlayNormal(*Mesh, Normals, elemid); //Normals->SetElement(elemid, (FVector3f)NewNormal); } }); } //{ // TRACE_CPUPROFILER_EVENT_SCOPE(PrecalculateNormalsROI_Clear); // for (int32 k : List) // { // Flags[k].store(false); // } //} //{ // TRACE_CPUPROFILER_EVENT_SCOPE(PrecalculateNormalsROI_InsertTest); // static TArray ListX; // ListX.Reset(); // for (int32 k : List) // { // ListX.Add(k); // } //} { TRACE_CPUPROFILER_EVENT_SCOPE(PrecalculateNormalsROI_BitArraySet); IndexSetTemp.Initialize(Normals->MaxElementID()); for (int32 TriangleID : TriangleROI) { FIndex3i TriElems = Normals->GetTriangle(TriangleID); IndexSetTemp.Add(TriElems.A); IndexSetTemp.Add(TriElems.B); IndexSetTemp.Add(TriElems.C); } } UE_LOG(LogTemp, Warning, TEXT("LIST %d INDEXSET %d"), List.Num(), IndexSetTemp.Indices().Num()); } else { bIsOverlayElements = false; IndexSetTemp.Initialize(Mesh->MaxVertexID()); for (int32 TriangleID : TriangleROI) { const FIndex3i& Triangle = Mesh->GetTriangleRef(TriangleID); IndexSetTemp.Add(Triangle.A); IndexSetTemp.Add(Triangle.B); IndexSetTemp.Add(Triangle.C); } } } void UE::SculptUtil::RecalculateROINormals(FDynamicMesh3* Mesh, const TArray& Indices, bool bIsOverlayElements) { if (bIsOverlayElements) { FDynamicMeshNormalOverlay* Normals = Mesh->HasAttributes() ? Mesh->Attributes()->PrimaryNormals() : nullptr; ParallelFor(Indices.Num(), [&](int32 k) { int32 elemid = Indices[k]; FVector3d NewNormal = FMeshNormals::ComputeOverlayNormal(*Mesh, Normals, elemid); Normals->SetElement(elemid, (FVector3f)NewNormal); }); } else { ParallelFor(Indices.Num(), [&](int32 k) { int32 vid = Indices[k]; FVector3d NewNormal = FMeshNormals::ComputeVertexNormal(*Mesh, vid); Mesh->SetVertexNormal(vid, (FVector3f)NewNormal); }); } } void UE::SculptUtil::PrecalculateNormalsROI(const FDynamicMesh3* Mesh, const TArray& TriangleROI, TArray>& ROIFlags, bool& bIsOverlayElements, bool bForceVertex) { const FDynamicMeshNormalOverlay* Normals = Mesh->HasAttributes() ? Mesh->Attributes()->PrimaryNormals() : nullptr; if (Normals != nullptr && bForceVertex == false) { bIsOverlayElements = true; if (ROIFlags.Num() != Normals->MaxElementID()) { ROIFlags.SetNum(Normals->MaxElementID()); ParallelFor(Normals->MaxElementID(), [&](int i) { ROIFlags[i] = false; }); } { TRACE_CPUPROFILER_EVENT_SCOPE(PrecalculateNormalsROI_FIND); ParallelFor(TriangleROI.Num(), [&](int i) { if (Normals->IsSetTriangle(TriangleROI[i])) { FIndex3i TriElems = Normals->GetTriangle(TriangleROI[i]); ROIFlags[TriElems.A] = true; ROIFlags[TriElems.B] = true; ROIFlags[TriElems.C] = true; } }); } } else { bIsOverlayElements = false; check(false); } } void UE::SculptUtil::RecalculateROINormals(FDynamicMesh3* Mesh, TArray>& ROIFlags, bool bIsOverlayElements) { if (bIsOverlayElements) { FDynamicMeshNormalOverlay* Normals = Mesh->HasAttributes() ? Mesh->Attributes()->PrimaryNormals() : nullptr; ParallelFor(ROIFlags.Num(), [&](int32 elemid) { if (ROIFlags[elemid]) { FVector3d NewNormal = FMeshNormals::ComputeOverlayNormal(*Mesh, Normals, elemid); Normals->SetElement(elemid, (FVector3f)NewNormal); ROIFlags[elemid] = false; } //bool bFalse = false; //bool bWasTrue = ROIFlags[elemid].compare_exchange_strong(bFalse, true); //if (bWasTrue) //{ // FVector3d NewNormal = FMeshNormals::ComputeOverlayNormal(*Mesh, Normals, elemid); // Normals->SetElement(elemid, (FVector3f)NewNormal); //} }); } else { ParallelFor(ROIFlags.Num(), [&](int32 vid) { if (ROIFlags[vid]) { FVector3d NewNormal = FMeshNormals::ComputeVertexNormal(*Mesh, vid); Mesh->SetVertexNormal(vid, (FVector3f)NewNormal); ROIFlags[vid] = false; } //bool bFalse = false; //bool bWasTrue = ROIFlags[vid].compare_exchange_strong(bFalse, true); //if (bWasTrue) //{ // FVector3d NewNormal = FMeshNormals::ComputeVertexNormal(*Mesh, vid); // Mesh->SetVertexNormal(vid, (FVector3f)NewNormal); //} }); } } void UE::SculptUtil::RecalculateROINormalForTriangles(FDynamicMesh3* Mesh, TArray Triangles, bool bIsOverlayElements) { if (bIsOverlayElements) { FMeshNormals::RecomputeOverlayTriNormals(*Mesh, Triangles); } else { FMeshNormals::QuickComputeVertexNormalsForTriangles(*Mesh, Triangles); } }