// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "DynamicMesh/DynamicMeshAttributeSet.h" #include "GeometryFlowMovableData.h" #include "GeometryFlowNodeUtil.h" #include "DataTypes/DynamicMeshData.h" #include "DataTypes/IndexSetsData.h" #include "DataTypes/WeightMapData.h" #include "Selections/MeshConnectedComponents.h" namespace UE { namespace GeometryFlow { class FMakeTriangleSetsFromMeshNode : public FNode { static constexpr int Version = 1; GEOMETRYFLOW_NODE_INTERNAL(FMakeTriangleSetsFromMeshNode, Version, FNode) public: static const FString InParam() { return TEXT("Mesh"); } static const FString OutParamIndexSets() { return TEXT("IndexSets"); } public: FMakeTriangleSetsFromMeshNode() { AddInput(InParam(), MakeUnique()); AddOutput(OutParamIndexSets(), MakeBasicOutput()); } virtual void Evaluate( const FNamedDataMap& DatasIn, FNamedDataMap& DatasOut, TUniquePtr& EvaluationInfo) override { if (ensure(DatasOut.Contains(OutParamIndexSets()))) { bool bAllInputsValid = true; bool bRecomputeRequired = (IsOutputAvailable(OutParamIndexSets()) == false); TSafeSharedPtr MeshArg = FindAndUpdateInputForEvaluate(InParam(), DatasIn, bRecomputeRequired, bAllInputsValid); CheckAdditionalInputs(DatasIn, bRecomputeRequired, bAllInputsValid); if (bAllInputsValid) { if (bRecomputeRequired) { const FDynamicMesh3& Mesh = MeshArg->GetDataConstRef((int)EMeshProcessingDataTypes::DynamicMesh); FIndexSets NewSets; ComputeIndexSets(DatasIn, Mesh, NewSets); SetOutput(OutParamIndexSets(), MakeMovableData(MoveTemp(NewSets))); EvaluationInfo->CountCompute(this); } DatasOut.SetData(OutParamIndexSets(), GetOutput(OutParamIndexSets())); } } } virtual void CheckAdditionalInputs(const FNamedDataMap& DatasIn, bool& bRecomputeRequired, bool& bAllInputsValid) { // none } virtual void ComputeIndexSets(const FNamedDataMap& DatasIn, const FDynamicMesh3& Mesh, FIndexSets& SetsOut) { SetsOut.IndexSets.SetNum(1); SetsOut.IndexSets[0].Reserve(Mesh.TriangleCount()); for (int32 tid : Mesh.TriangleIndicesItr()) { SetsOut.IndexSets[0].Add(tid); } } }; /// /// Gather triangles into sets based on their GroupIDs. Optionally exclude any triangles belonging to specified groups. /// class FMakeTriangleSetsFromGroupsNode : public FMakeTriangleSetsFromMeshNode { static constexpr int Version = 1; GEOMETRYFLOW_NODE_INTERNAL(FMakeTriangleSetsFromGroupsNode, Version, FMakeTriangleSetsFromMeshNode) public: static const FString InParamGroupLayer() { return TEXT("GroupLayerName"); } static const FString InParamIgnoreGroups() { return TEXT("IgnoreGroups"); } public: FMakeTriangleSetsFromGroupsNode() : FMakeTriangleSetsFromMeshNode() { AddInput(InParamGroupLayer(), MakeUnique>(), MakeSafeShared>()); AddInput(InParamIgnoreGroups(), MakeBasicInput()); } virtual void CheckAdditionalInputs(const FNamedDataMap& DatasIn, bool& bRecomputeRequired, bool& bAllInputsValid) override { FindAndUpdateInputForEvaluate(InParamGroupLayer(), DatasIn, bRecomputeRequired, bAllInputsValid); FindAndUpdateInputForEvaluate(InParamIgnoreGroups(), DatasIn, bRecomputeRequired, bAllInputsValid); } protected: void ComputeIndexSetsForGroups(const FDynamicMesh3& Mesh, const TSet& IgnoreGroups, TFunction TriangleGroupFn, FIndexSets& SetsOut) { TMap GroupsMap; TArray GroupCounts; int32 NumGroups = 0; for (int32 tid : Mesh.TriangleIndicesItr()) { int32 GroupID = TriangleGroupFn(tid); if (IgnoreGroups.Contains(GroupID)) { continue; } int32* FoundIndex = GroupsMap.Find(GroupID); if (FoundIndex == nullptr) { int32 Index = NumGroups++; GroupsMap.Add(GroupID, Index); GroupCounts.Add(1); } else { GroupCounts[*FoundIndex]++; } } SetsOut.IndexSets.SetNum(NumGroups); for (int32 k = 0; k < NumGroups; ++k) { SetsOut.IndexSets[k].Reserve(GroupCounts[k]); } for (int32 tid : Mesh.TriangleIndicesItr()) { int32 GroupID = TriangleGroupFn(tid); if (IgnoreGroups.Contains(GroupID)) { continue; } int32* FoundIndex = GroupsMap.Find(GroupID); SetsOut.IndexSets[*FoundIndex].Add(tid); } } public: virtual void ComputeIndexSets(const FNamedDataMap& DatasIn, const FDynamicMesh3& Mesh, FIndexSets& SetsOut) override { TSafeSharedPtr IgnoreGroupsArg = DatasIn.FindData(InParamIgnoreGroups()); const FIndexSets& IgnoreGroupsSets = IgnoreGroupsArg->GetDataConstRef(FIndexSets::DataTypeIdentifier); TSet IgnoreGroups; IgnoreGroupsSets.GetAllValues(IgnoreGroups); TSafeSharedPtr GroupLayerArg = DatasIn.FindData(InParamGroupLayer()); FName GroupName = GroupLayerArg->GetDataConstRef((int)EDataTypes::Name); if (GroupName.IsNone()) { return; } if (Mesh.HasTriangleGroups() && (GroupName == "Default")) { ComputeIndexSetsForGroups(Mesh, IgnoreGroups, [&Mesh](int TriangleID) { return Mesh.GetTriangleGroup(TriangleID); }, SetsOut); } else if (Mesh.HasAttributes()) { for (int32 PolygroupLayerIndex = 0; PolygroupLayerIndex < Mesh.Attributes()->NumPolygroupLayers(); ++PolygroupLayerIndex) { const FDynamicMeshPolygroupAttribute* PolygroupLayer = Mesh.Attributes()->GetPolygroupLayer(PolygroupLayerIndex); if (PolygroupLayer->GetName() == GroupName) { ComputeIndexSetsForGroups(Mesh, IgnoreGroups, [&PolygroupLayer](int TriangleID) { return PolygroupLayer->GetValue(TriangleID); }, SetsOut); } } } } }; class FMakeTriangleSetsFromConnectedComponentsNode : public FMakeTriangleSetsFromMeshNode { static constexpr int Version = 1; GEOMETRYFLOW_NODE_INTERNAL(FMakeTriangleSetsFromConnectedComponentsNode, Version, FMakeTriangleSetsFromMeshNode) public: virtual void ComputeIndexSets(const FNamedDataMap& DatasIn, const FDynamicMesh3& Mesh, FIndexSets& SetsOut) override { FMeshConnectedComponents MeshRegions(&Mesh); MeshRegions.FindConnectedTriangles(); for (const FMeshConnectedComponents::FComponent& Component : MeshRegions.Components) { SetsOut.AppendSet(Component.Indices); } } }; /// If one triangle vertex has a weight greater than the given threshold, include it in the output triangle set. /// TODO: Optionally make it so the *average* triangle vertex weight has to exceed the threshold. Or the minumum triangle /// vertex weight. class FMakeTriangleSetsFromWeightMapNode : public FMakeTriangleSetsFromMeshNode { static constexpr int Version = 1; GEOMETRYFLOW_NODE_INTERNAL(FMakeTriangleSetsFromWeightMapNode, Version, FMakeTriangleSetsFromMeshNode) public: static const FString InParamWeightMap() { return TEXT("WeightMap"); } static const FString InParamThreshold() { return TEXT("Threshold"); } FMakeTriangleSetsFromWeightMapNode() : FMakeTriangleSetsFromMeshNode() { AddInput(InParamWeightMap(), MakeBasicInput()); AddInput(InParamThreshold(), MakeUnique>()); } virtual void CheckAdditionalInputs(const FNamedDataMap& DatasIn, bool& bRecomputeRequired, bool& bAllInputsValid) override { FindAndUpdateInputForEvaluate(InParamWeightMap(), DatasIn, bRecomputeRequired, bAllInputsValid); FindAndUpdateInputForEvaluate(InParamThreshold(), DatasIn, bRecomputeRequired, bAllInputsValid); } virtual void ComputeIndexSets(const FNamedDataMap& DatasIn, const FDynamicMesh3& Mesh, FIndexSets& SetsOut) override { TSafeSharedPtr WeightMapArg = DatasIn.FindData(InParamWeightMap()); const FWeightMap& WeightMap = WeightMapArg->GetDataConstRef(FWeightMap::DataTypeIdentifier); const TArray& Weights = WeightMap.Weights; check(Weights.Num() >= Mesh.MaxVertexID()); TSafeSharedPtr ThresholdArg = DatasIn.FindData(InParamThreshold()); const float Threshold = ThresholdArg->GetDataConstRef((int)EDataTypes::Float); TArray Set; for (int TriangleID : Mesh.TriangleIndicesItr()) { FIndex3i Triangle = Mesh.GetTriangle(TriangleID); for (int VertexID : {Triangle[0], Triangle[1], Triangle[2]} ) { if (Weights[VertexID] > Threshold) { Set.Add(TriangleID); break; } } } SetsOut.AppendSet(Set); } }; } // end namespace GeometryFlow } // end namespace UE