// Copyright Epic Games, Inc. All Rights Reserved. #include "Operations/AverageOverlayToVertices.h" #include "DynamicMesh/DynamicMesh3.h" #include "DynamicMesh/DynamicMeshOverlay.h" #include "Async/ParallelFor.h" #include "MeshQueries.h" #include "Math/UnrealMathUtility.h" using namespace UE::Geometry; namespace FAverageOverlayToVerticesLocals { /** * Compute the weighted sum of elements values for all the mesh vertices. * * @param Overlay overlay containing the elements to be averaged. * @param bWeightByArea if true, include weighting by the area of the triangle. * @param bWeightByAngle if true, include weighting by the interior angles of the triangle. * @param SummedElementValues Mesh->MaxVertexID()*ElementSize sized array of the weighted sum of the element values * at a vertex. * @param HasElements Mesh->MaxVertexID() sized array where HasElements[VertexID] is true if VertexID has at * least one element (i.e, at least one triangle sharing the vertex is set in the overlay). * @param VertexTotalWeights sum of all element weights at a vertex. */ template void AverageFullMesh(const FDynamicMesh3* Mesh, const TDynamicMeshOverlay* Overlay, bool bWeightByArea, bool bWeightByAngle, TArray& SummedElementValues, TArray& HasElements, TArray& VertexTotalWeights) { SummedElementValues.Init((RealType)0.0, Mesh->MaxVertexID()*ElementSize); HasElements.Init(false, Mesh->MaxVertexID()); VertexTotalWeights.Init(0.0, Mesh->MaxVertexID()); // reuse buffer RealType ElementData[ElementSize]; // Iterate over every triangle and accumulate the overlay element values for each triangle vertex for (const int32 TID : Mesh->TriangleIndicesItr()) { if (Overlay->IsSetTriangle(TID)) { const FIndex3i TriVert = Mesh->GetTriangle(TID); const FIndex3i TriElem = Overlay->GetTriangle(TID); // per-corner weights const FVector3d Weights = TMeshQueries::GetVertexWeightsOnTriangle(*Mesh, TID, Mesh->GetTriArea(TID), bWeightByArea, bWeightByAngle); // iterate over every corner of the triangle for (int32 CornerIdx = 0; CornerIdx < 3; ++CornerIdx) { const int32 CornerVID = TriVert[CornerIdx]; Overlay->GetElement(TriElem[CornerIdx], ElementData); const int32 StartIdx = CornerVID * ElementSize; // starting offset into the output averaged values array VertexTotalWeights[CornerVID] += Weights[CornerIdx]; // accumulate weights for the vertex to be used later for the normalization // add the weighted contribution of the element for the vertex for (int32 EIdx = 0; EIdx < ElementSize; ++EIdx) { SummedElementValues[StartIdx + EIdx] += RealType(Weights[CornerIdx] * (double)ElementData[EIdx]); } HasElements[CornerVID] = true; } } } } /** * Compute the weighed sum of element values for the selection of the mesh vertices. * * @param Overlay overlay containing the elements to be averaged. * @param SelectedVertices the array of vertex IDs representing the selection. * @param bWeightByArea if true, include weighting by the area of the triangle. * @param bWeightByAngle if true, include weighting by the interior angles of the triangle. * @param SummedElementValues Mesh->MaxVertexID()*ElementSize sized array of the weighted sum of the element values * at a vertex. * @param HasElements Mesh->MaxVertexID() sized array where HasElements[VertexID] is true if VertexID has at * least one element (i.e, at least one triangle sharing the vertex is set in the overlay). * @param VertexTotalWeights sum of all element weights at a vertex. */ template void AverageSelection(const FDynamicMesh3* Mesh, const TDynamicMeshOverlay* Overlay, const TArray& SelectedVertices, bool bWeightByArea, bool bWeightByAngle, TArray& SummedElementValues, TArray& HasElements, TArray& VertexTotalWeights) { // Map mesh vertex ID to selection idx TMap VIDToSelected; int32 Count = 0; VIDToSelected.Reserve(SelectedVertices.Num()); for (const int32 VID : SelectedVertices) { VIDToSelected.Add(VID, Count++); } // Get selected triangles TArray SelectedTriangles; SelectedTriangles = TMeshQueries::GetVertexSelectedTriangles(*Mesh, SelectedVertices); SummedElementValues.Init((RealType)0.0, SelectedVertices.Num()*ElementSize); HasElements.Init(false, SelectedVertices.Num()); VertexTotalWeights.Init(0.0, SelectedVertices.Num()); // reuse buffer RealType ElementData[ElementSize]; // Iterate over every triangle and accumulate the overlay element values for each triangle vertex for (const int32 TID : SelectedTriangles) { if (Mesh->IsTriangle(TID) && Overlay->IsSetTriangle(TID)) { const FIndex3i TriVert = Mesh->GetTriangle(TID); const FIndex3i TriElem = Overlay->GetTriangle(TID); // per-corner weights const FVector3d Weights = TMeshQueries::GetVertexWeightsOnTriangle(*Mesh, TID, Mesh->GetTriArea(TID), bWeightByArea, bWeightByAngle); // iterate over every corner of the triangle for (int32 CornerIdx = 0; CornerIdx < 3; ++CornerIdx) { const int32 CornerVID = TriVert[CornerIdx]; if (VIDToSelected.Contains(CornerVID)) // check if corner is one of the selected vertices { Overlay->GetElement(TriElem[CornerIdx], ElementData); const int32 SelectedIdx = VIDToSelected[CornerVID]; // remap vertex ID to the index into output averaged values array const int32 StartIdx = SelectedIdx * ElementSize; // starting offset into the output averaged values array VertexTotalWeights[SelectedIdx] += Weights[CornerIdx]; // accumulate weights for the vertex to be used later for the normalization // add the weighted contribution of the element for the vertex for (int32 EIdx = 0; EIdx < ElementSize; ++EIdx) { SummedElementValues[StartIdx + EIdx] += RealType(Weights[CornerIdx] * (double)ElementData[EIdx]); } HasElements[SelectedIdx] = true; } } } } } } FAverageOverlayToVertices::FAverageOverlayToVertices(const FDynamicMesh3& InMesh) : Mesh(&InMesh) { } template bool FAverageOverlayToVertices::AverageOverlay(const TDynamicMeshOverlay* Overlay, TArray& VertexValues, TArray& HasElements) { using namespace FAverageOverlayToVerticesLocals; if (Overlay == nullptr) { return false; } const bool bWeightByArea = TriangleWeight == ETriangleWeight::Area || TriangleWeight == ETriangleWeight::AngleArea; const bool bWeightByAngle = TriangleWeight == ETriangleWeight::Angle || TriangleWeight == ETriangleWeight::AngleArea; TArray VertexTotalWeights; if (Selection.Num()) { AverageSelection(Mesh, Overlay, Selection, bWeightByArea, bWeightByAngle, VertexValues, HasElements, VertexTotalWeights); } else { AverageFullMesh(Mesh, Overlay, bWeightByArea, bWeightByAngle, VertexValues, HasElements, VertexTotalWeights); } // // Normalize the accumulated values // ParallelFor(HasElements.Num(), [&VertexValues, &HasElements, &VertexTotalWeights](int32 SelectedIdx) { if (HasElements[SelectedIdx]) { double InvTotalWeight = 1.0; if (!FMath::IsNearlyZero(VertexTotalWeights[SelectedIdx])) { InvTotalWeight = 1.0/VertexTotalWeights[SelectedIdx]; } const int32 StartIdx = SelectedIdx*ElementSize; for (int32 EIdx = 0; EIdx < ElementSize; ++EIdx) { VertexValues[StartIdx + EIdx] = RealType(InvTotalWeight * (double)VertexValues[StartIdx + EIdx]); } } }, bUseParallel ? EParallelForFlags::None : EParallelForFlags::ForceSingleThread); return true; } // template instantiation template DYNAMICMESH_API bool UE::Geometry::FAverageOverlayToVertices::AverageOverlay(class UE::Geometry::TDynamicMeshOverlay const *,class TArray > &,class TArray > &); template DYNAMICMESH_API bool UE::Geometry::FAverageOverlayToVertices::AverageOverlay(class UE::Geometry::TDynamicMeshOverlay const *,class TArray > &,class TArray > &); template DYNAMICMESH_API bool UE::Geometry::FAverageOverlayToVertices::AverageOverlay(class UE::Geometry::TDynamicMeshOverlay const *,class TArray > &,class TArray > &); template DYNAMICMESH_API bool UE::Geometry::FAverageOverlayToVertices::AverageOverlay(class UE::Geometry::TDynamicMeshOverlay const *,class TArray > &,class TArray > &); template DYNAMICMESH_API bool UE::Geometry::FAverageOverlayToVertices::AverageOverlay(class UE::Geometry::TDynamicMeshOverlay const *,class TArray > &,class TArray > &);