Files
2025-05-18 13:04:45 +08:00

205 lines
6.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Dataflow/GeometryCollectionDebugNodes.h"
#include "Dataflow/DataflowCore.h"
#include "Containers/UnrealString.h"
#include "Misc/FileHelper.h"
#include "DynamicMesh/DynamicMesh3.h"
#include "DynamicMeshEditor.h"
#include "Generators/BoxSphereGenerator.h"
#include "FractureEngineConvex.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GeometryCollectionDebugNodes)
namespace UE::Dataflow
{
void GeometryCollectionDebugNodes()
{
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FConvexHullToMeshDataflowNode);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FSphereCoveringToMeshDataflowNode);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FMeshToOBJStringDebugDataflowNode);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FSphereCoveringCountSpheresNode);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FWriteStringToFile);
}
}
FConvexHullToMeshDataflowNode::FConvexHullToMeshDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid)
: FDataflowNode(InParam, InGuid)
{
RegisterInputConnection(&Collection);
RegisterInputConnection(&OptionalSelectionFilter);
RegisterOutputConnection(&Mesh);
RegisterOutputConnection(&Meshes);
}
void FConvexHullToMeshDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
using namespace UE::Geometry;
if (Out->IsA(&Mesh))
{
const FManagedArrayCollection& InCollection = GetValue<FManagedArrayCollection>(Context, &Collection);
TArray<int32> TransformSelection;
bool bHasSelection = IsConnected(&OptionalSelectionFilter);
if (bHasSelection)
{
const FDataflowTransformSelection& InOptionalSelectionFilter = GetValue(Context, &OptionalSelectionFilter);
TransformSelection = InOptionalSelectionFilter.AsArray();
}
FDynamicMesh3 HullsMesh;
UE::FractureEngine::Convex::GetConvexHullsAsDynamicMesh(InCollection, HullsMesh, bHasSelection, TransformSelection, bUseRobustHulls);
TObjectPtr<UDynamicMesh> NewMesh = NewObject<UDynamicMesh>();
NewMesh->SetMesh(MoveTemp(HullsMesh));
SetValue(Context, NewMesh, &Mesh);
}
else if (Out->IsA(&Meshes))
{
const FManagedArrayCollection& InCollection = GetValue<FManagedArrayCollection>(Context, &Collection);
TArray<int32> TransformSelection;
bool bHasSelection = IsConnected(&OptionalSelectionFilter);
if (bHasSelection)
{
const FDataflowTransformSelection& InOptionalSelectionFilter = GetValue(Context, &OptionalSelectionFilter);
TransformSelection = InOptionalSelectionFilter.AsArray();
}
TArray<FDynamicMesh3> HullsMeshes;
UE::FractureEngine::Convex::GetConvexHullsAsDynamicMeshes(InCollection, HullsMeshes, bHasSelection, TransformSelection, bUseRobustHulls);
TArray<TObjectPtr<UDynamicMesh>> NewMeshes;
NewMeshes.SetNum(HullsMeshes.Num());
for (FDynamicMesh3& HullMesh : HullsMeshes)
{
TObjectPtr<UDynamicMesh> NewMesh = NewObject<UDynamicMesh>();
NewMesh->SetMesh(MoveTemp(HullMesh));
NewMeshes.Add(NewMesh);
}
SetValue(Context, MoveTemp(NewMeshes), &Meshes);
}
}
void FSphereCoveringToMeshDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
using namespace UE::Geometry;
if (Out->IsA(&Mesh))
{
TObjectPtr<UDynamicMesh> NewMesh = NewObject<UDynamicMesh>();
NewMesh->Reset();
FDynamicMesh3 Accum;
FDynamicMeshEditor Editor(&Accum);
FMeshIndexMappings IndexMaps_Unused;
FDataflowSphereCovering InSphereCovering = GetValue(Context, &SphereCovering);
for (int32 SphereIdx = 0; SphereIdx < InSphereCovering.Spheres.Num(); ++SphereIdx)
{
FBoxSphereGenerator SphereGen;
SphereGen.EdgeVertices.A = SphereGen.EdgeVertices.B = SphereGen.EdgeVertices.C = FMath::Max(2, VerticesAlongEachSide);
SphereGen.Radius = InSphereCovering.Spheres.GetRadius(SphereIdx);
FVector Center = InSphereCovering.Spheres.GetCenter(SphereIdx);
FDynamicMesh3 Sphere(&SphereGen.Generate());
Editor.AppendMesh(&Sphere, IndexMaps_Unused,
[Center](int VID, const FVector3d& Pos) { return Pos + Center; });
IndexMaps_Unused.Reset();
}
NewMesh->SetMesh(MoveTemp(Accum));
SetValue(Context, NewMesh, &Mesh);
}
}
void FMeshToOBJStringDebugDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
if (Out->IsA(&StringOBJ))
{
const UDynamicMesh* InMesh = GetValue(Context, &Mesh);
const UE::Geometry::FDynamicMesh3& MeshRef = InMesh->GetMeshRef();
FString Build;
Build.Reset(MeshRef.MaxVertexID() * 40 + MeshRef.TriangleCount() * 24);
for (int32 VID = 0; VID < MeshRef.MaxVertexID(); ++VID)
{
FVector V = FVector::ZeroVector;
if (MeshRef.IsVertex(VID))
{
V = MeshRef.GetVertex(VID);
}
Build += FString::Printf(TEXT("v %f %f %f\n"), V.X, V.Y, V.Z);
}
// Uvs
const UE::Geometry::FDynamicMeshUVOverlay* UvOverlay = nullptr;
if (MeshRef.HasVertexUVs())
{
for (int32 VID = 0; VID < MeshRef.MaxVertexID(); ++VID)
{
const FVector2f Uv = MeshRef.GetVertexUV(VID);
Build += FString::Printf(TEXT("vt %f %f\n"), Uv.X, Uv.Y);
}
}
else if (MeshRef.Attributes()->NumUVLayers() > 0)
{
UvOverlay = MeshRef.Attributes()->GetUVLayer(0);
if (UvOverlay)
{
for (int32 UvIndex = 0; UvIndex < UvOverlay->ElementCount(); ++UvIndex)
{
const FVector2f Uv = UvOverlay->GetElement(UvIndex);
Build += FString::Printf(TEXT("vt %f %f\n"), Uv.X, Uv.Y);
}
}
}
// faces
bool bInInvertFaces = GetValue(Context, &bInvertFaces);
for (int32 TID = 0; TID < MeshRef.MaxTriangleID(); ++TID)
{
if (MeshRef.IsTriangle(TID))
{
UE::Geometry::FIndex3i T = MeshRef.GetTriangle(TID);
// Note: OBJ viewers generally expect the opposite triangle winding from UE meshes, so inverted == the UE order
if (!bInInvertFaces)
{
Swap(T.B, T.C);
}
if (UvOverlay)
{
UE::Geometry::FIndex3i UvIndices = UvOverlay->GetTriangle(TID);
if (!bInInvertFaces)
{
Swap(UvIndices.B, UvIndices.C);
}
Build += FString::Printf(TEXT("f %d/%d %d/%d %d/%d\n"), T.A + 1, UvIndices.A + 1, T.B + 1, UvIndices.B + 1, T.C + 1, UvIndices.C + 1);
}
else
{
Build += FString::Printf(TEXT("f %d %d %d\n"), T.A + 1, T.B + 1, T.C + 1);
}
}
}
SetValue(Context, MoveTemp(Build), &StringOBJ);
}
}
void FWriteStringToFile::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
FString InContents = GetValue(Context, &FileContents);
FString InPath = GetValue(Context, &FilePath);
bool bSuccess = FFileHelper::SaveStringToFile(InContents, *InPath);
if (!bSuccess)
{
UE_LOG(LogChaos, Warning, TEXT("Failed to write to file %s:\n\n%s"), *InPath, *InContents);
}
}