// Copyright Epic Games, Inc. All Rights Reserved. #include "Dataflow/GeometryCollectionFracturingNodes.h" #include "Dataflow/DataflowCore.h" #include "Engine/StaticMesh.h" #include "GeometryCollection/GeometryCollectionObject.h" #include "GeometryCollection/ManagedArrayCollection.h" #include "GeometryCollection/GeometryCollection.h" #include "GeometryCollection/GeometryCollectionEngineUtility.h" #include "GeometryCollection/GeometryCollectionEngineConversion.h" #include "Logging/LogMacros.h" #include "Templates/SharedPointer.h" #include "UObject/UnrealTypePrivate.h" #include "DynamicMeshToMeshDescription.h" #include "MeshDescriptionToDynamicMesh.h" #include "StaticMeshAttributes.h" #include "DynamicMeshEditor.h" #include "Operations/MeshBoolean.h" #include "EngineGlobals.h" #include "GeometryCollection/GeometryCollectionAlgo.h" #include "GeometryCollection/GeometryCollectionClusteringUtility.h" #include "GeometryCollection/GeometryCollectionConvexUtility.h" #include "Voronoi/Voronoi.h" #include "PlanarCut.h" #include "GeometryCollection/GeometryCollectionProximityUtility.h" #include "FractureEngineClustering.h" #include "FractureEngineSelection.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(GeometryCollectionFracturingNodes) namespace Dataflow { void GeometryCollectionFracturingNodes() { static const FLinearColor CDefaultNodeBodyTintColor = FLinearColor(0.f, 0.f, 0.f, 0.5f); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FUniformScatterPointsDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FRadialScatterPointsDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FVoronoiFractureDataflowNode); DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FPlaneCutterDataflowNode); // GeometryCollection|Fracture DATAFLOW_NODE_REGISTER_CREATION_FACTORY_NODE_COLORS_BY_CATEGORY("GeometryCollection|Fracture", FLinearColor(1.f, 1.f, 0.8f), CDefaultNodeBodyTintColor); } } void FUniformScatterPointsDataflowNode::Evaluate(Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA>(&Points)) { const FBox& BBox = GetValue(Context, &BoundingBox); if (BBox.GetVolume() > 0.f) { FRandomStream RandStream(GetValue(Context, &RandomSeed)); const FVector Extent(BBox.Max - BBox.Min); const int32 NumPoints = RandStream.RandRange(GetValue(Context, &MinNumberOfPoints), GetValue(Context, &MaxNumberOfPoints)); TArray PointsArr; PointsArr.Reserve(NumPoints); for (int32 Idx = 0; Idx < NumPoints; ++Idx) { PointsArr.Emplace(BBox.Min + FVector(RandStream.FRand(), RandStream.FRand(), RandStream.FRand()) * Extent); } SetValue>(Context, PointsArr, &Points); } else { // ERROR: Invalid BoundingBox input SetValue>(Context, TArray(), &Points); } } } void FRadialScatterPointsDataflowNode::Evaluate(Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA>(&Points)) { const FVector::FReal RadialStep = GetValue(Context, &Radius) / GetValue(Context, &RadialSteps); const FVector::FReal AngularStep = 2 * PI / GetValue(Context, &AngularSteps); FRandomStream RandStream(GetValue(Context, &RandomSeed)); FVector UpVector(GetValue(Context, &Normal)); UpVector.Normalize(); FVector BasisX, BasisY; UpVector.FindBestAxisVectors(BasisX, BasisY); TArray PointsArr; FVector::FReal Len = RadialStep * .5; for (int32 ii = 0; ii < GetValue(Context, &RadialSteps); ++ii, Len += RadialStep) { FVector::FReal Angle = FMath::DegreesToRadians(GetValue(Context, &AngleOffset)); for (int32 kk = 0; kk < AngularSteps; ++kk, Angle += AngularStep) { FVector RotatingOffset = Len * (FMath::Cos(Angle) * BasisX + FMath::Sin(Angle) * BasisY); PointsArr.Emplace(GetValue(Context, &Center) + RotatingOffset + (RandStream.VRand() * RandStream.FRand() * Variability)); } } SetValue>(Context, PointsArr, &Points); } } static float GetMaxVertexMovement(float Grout, float Amplitude, int OctaveNumber, float Persistence) { float MaxDisp = Grout; float AmplitudeScaled = Amplitude; for (int32 OctaveIdx = 0; OctaveIdx < OctaveNumber; OctaveIdx++, AmplitudeScaled *= Persistence) { MaxDisp += FMath::Abs(AmplitudeScaled); } return MaxDisp; } void FVoronoiFractureDataflowNode::Evaluate(Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); if (TUniquePtr GeomCollection = TUniquePtr(InCollection.NewCopy())) { const TArray& Sites = GetValue>(Context, &Points); if (Sites.Num() > 0) { // // Compute BoundingBox for ManagedArrayIn // FBox BoundingBox(ForceInit); if (InCollection.HasAttribute("Transform", FGeometryCollection::TransformGroup) && InCollection.HasAttribute("Parent", FGeometryCollection::TransformGroup) && InCollection.HasAttribute("TransformIndex", FGeometryCollection::GeometryGroup) && InCollection.HasAttribute("BoundingBox", FGeometryCollection::GeometryGroup)) { const TManagedArray& Transforms = InCollection.GetAttribute("Transform", FGeometryCollection::TransformGroup); const TManagedArray& ParentIndices = InCollection.GetAttribute("Parent", FGeometryCollection::TransformGroup); const TManagedArray& TransformIndices = InCollection.GetAttribute("TransformIndex", FGeometryCollection::GeometryGroup); const TManagedArray& BoundingBoxes = InCollection.GetAttribute("BoundingBox", FGeometryCollection::GeometryGroup); TArray TmpGlobalMatrices; GeometryCollectionAlgo::GlobalMatrices(Transforms, ParentIndices, TmpGlobalMatrices); if (TmpGlobalMatrices.Num() > 0) { for (int32 BoxIdx = 0; BoxIdx < BoundingBoxes.Num(); ++BoxIdx) { const int32 TransformIndex = TransformIndices[BoxIdx]; BoundingBox += BoundingBoxes[BoxIdx].TransformBy(TmpGlobalMatrices[TransformIndex]); } } // // Compute Voronoi Bounds // FBox VoronoiBounds = BoundingBox; VoronoiBounds += FBox(Sites); float GroutVal = GetValue(Context, &Grout); float AmplitudeVal = GetValue(Context, &Amplitude); int32 OctaveNumberVal = GetValue(Context, &OctaveNumber); float PersistenceVal = GetValue(Context, &Persistence); VoronoiBounds = VoronoiBounds.ExpandBy(GetMaxVertexMovement(GroutVal, AmplitudeVal, OctaveNumberVal, PersistenceVal) + KINDA_SMALL_NUMBER); // // Voronoi Fracture // FNoiseSettings NoiseSettings; NoiseSettings.Amplitude = AmplitudeVal; NoiseSettings.Frequency = GetValue(Context, &Frequency); NoiseSettings.Octaves = OctaveNumberVal; NoiseSettings.PointSpacing = GetValue(Context, &PointSpacing); NoiseSettings.Lacunarity = GetValue(Context, &Lacunarity); NoiseSettings.Persistence = GetValue(Context, &Persistence);; FVoronoiDiagram Voronoi(Sites, VoronoiBounds, .1f); FPlanarCells VoronoiPlanarCells = FPlanarCells(Sites, Voronoi); VoronoiPlanarCells.InternalSurfaceMaterials.NoiseSettings = NoiseSettings; const TArrayView& TransformIndicesArray(TransformIndices.GetConstArray()); float CollisionSampleSpacingVal = GetValue(Context, &CollisionSampleSpacing); float RandomSeedVal = GetValue(Context, &RandomSeed); int ResultGeometryIndex = CutMultipleWithPlanarCells(VoronoiPlanarCells, *GeomCollection, TransformIndicesArray, GroutVal, CollisionSampleSpacingVal, RandomSeedVal, FTransform().Identity); SetValue(Context, (const FManagedArrayCollection&)(*GeomCollection), &Collection); } } } } } void FPlaneCutterDataflowNode::Evaluate(Dataflow::FContext& Context, const FDataflowOutput* Out) const { if (Out->IsA(&Collection)) { const FManagedArrayCollection& InCollection = GetValue(Context, &Collection); if (TUniquePtr GeomCollection = TUniquePtr(InCollection.NewCopy())) { TArray CuttingPlanes; TArray CuttingPlaneTransforms; float RandomSeedVal = GetValue(Context, &RandomSeed); FRandomStream RandStream(RandomSeedVal); FBox Bounds = GetValue(Context, &BoundingBox); const FVector Extent(Bounds.Max - Bounds.Min); CuttingPlaneTransforms.Reserve(CuttingPlaneTransforms.Num() + NumPlanes); for (int32 ii = 0; ii < NumPlanes; ++ii) { FVector Position(Bounds.Min + FVector(RandStream.FRand(), RandStream.FRand(), RandStream.FRand()) * Extent); CuttingPlaneTransforms.Emplace(FTransform(FRotator(RandStream.FRand() * 360.0f, RandStream.FRand() * 360.0f, 0.0f), Position)); } for (const FTransform& Transform : CuttingPlaneTransforms) { CuttingPlanes.Add(FPlane(Transform.GetLocation(), Transform.GetUnitAxis(EAxis::Z))); } FInternalSurfaceMaterials InternalSurfaceMaterials; FNoiseSettings NoiseSettings; float AmplitudeVal = GetValue(Context, &Amplitude); if (AmplitudeVal > 0.0f) { NoiseSettings.Amplitude = AmplitudeVal; NoiseSettings.Frequency = GetValue(Context, &Frequency); NoiseSettings.Lacunarity = GetValue(Context, &Lacunarity); NoiseSettings.Persistence = GetValue(Context, &Persistence); NoiseSettings.Octaves = GetValue(Context, &OctaveNumber); NoiseSettings.PointSpacing = GetValue(Context, &PointSpacing); InternalSurfaceMaterials.NoiseSettings = NoiseSettings; } if (GeomCollection->HasAttribute("TransformIndex", "Geometry")) { const TManagedArray& TransformIndices = GeomCollection->GetAttribute("TransformIndex", "Geometry"); const TArrayView& TransformIndicesArray(TransformIndices.GetConstArray()); float CollisionSampleSpacingVal = GetValue(Context, &CollisionSampleSpacing); float GroutVal = GetValue(Context, &Grout); int ResultGeometryIndex = CutMultipleWithMultiplePlanes(CuttingPlanes, InternalSurfaceMaterials, *GeomCollection, TransformIndicesArray, GroutVal, CollisionSampleSpacingVal, RandomSeedVal, FTransform().Identity); SetValue(Context, (const FManagedArrayCollection&)(*GeomCollection), &Collection); } } } }