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

658 lines
28 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Dataflow/GeometryCollectionAssetNodes.h"
#include "Dataflow/DataflowCore.h"
#include "Engine/StaticMesh.h"
#include "GeometryCollection/GeometryCollection.h"
#include "GeometryCollection/GeometryCollectionEngineConversion.h"
#include "GeometryCollection/GeometryCollectionObject.h"
#include "GeometryCollection/GeometryCollectionClusteringUtility.h"
#include "GeometryCollection/Facades/CollectionHierarchyFacade.h"
#include "GeometryCollection/Facades/CollectionInstancedMeshFacade.h"
#include "Components/StaticMeshComponent.h"
#include "Materials/Material.h"
#include "Materials/MaterialInterface.h"
#include "PreviewScene.h"
namespace UE::Dataflow
{
void GeometryCollectionEngineAssetNodes()
{
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FGeometryCollectionTerminalDataflowNode_v2);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FGetGeometryCollectionAssetDataflowNode);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FGetGeometryCollectionSourcesDataflowNode);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCreateGeometryCollectionFromSourcesDataflowNode_v2);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FGeometryCollectionToCollectionDataflowNode_v2);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FBlueprintToCollectionDataflowNode_v2);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FMakeRootProxyMeshDataflowNode);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FMakeRootProxyMeshArrayDataflowNode);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FAddRootProxyMeshToArrayDataflowNode);
// deprecated nodes (need to stay registered)
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FGeometryCollectionTerminalDataflowNode);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FCreateGeometryCollectionFromSourcesDataflowNode);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FGeometryCollectionToCollectionDataflowNode);
DATAFLOW_NODE_REGISTER_CREATION_FACTORY(FBlueprintToCollectionDataflowNode);
}
}
// ===========================================================================================================================
FMakeRootProxyMeshDataflowNode::FMakeRootProxyMeshDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid)
: FDataflowNode(InParam, InGuid)
{
RegisterInputConnection(&Mesh);
RegisterInputConnection(&Transform);
RegisterOutputConnection(&RootProxyMesh);
}
void FMakeRootProxyMeshDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
if (Out->IsA(&RootProxyMesh))
{
FDataflowRootProxyMesh OutRootProxyMesh;
OutRootProxyMesh.Mesh = GetValue(Context, &Mesh);
OutRootProxyMesh.Transform = GetValue(Context, &Transform);
SetValue(Context, MoveTemp(OutRootProxyMesh), &RootProxyMesh);
}
}
// ===========================================================================================================================
FMakeRootProxyMeshArrayDataflowNode::FMakeRootProxyMeshArrayDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid)
: FDataflowNode(InParam, InGuid)
{
RegisterOutputConnection(&RootProxyMeshes);
}
void FMakeRootProxyMeshArrayDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
if (Out->IsA(&RootProxyMeshes))
{
SetValue(Context, RootProxyMeshes, &RootProxyMeshes);
}
}
// ===========================================================================================================================
FAddRootProxyMeshToArrayDataflowNode::FAddRootProxyMeshToArrayDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid)
: FDataflowNode(InParam, InGuid)
{
RegisterInputConnection(&RootProxyMeshes);
RegisterInputConnection(&RootProxyMesh);
RegisterOutputConnection(&RootProxyMeshes, &RootProxyMeshes);
}
void FAddRootProxyMeshToArrayDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
if (Out->IsA(&RootProxyMeshes))
{
const FDataflowRootProxyMesh& InRootProxyMesh = GetValue(Context, &RootProxyMesh);
TArray<FDataflowRootProxyMesh> OutArray;
OutArray.Add(InRootProxyMesh);
SetValue(Context, MoveTemp(OutArray), &RootProxyMeshes);
}
}
// ===========================================================================================================================
FGeometryCollectionTerminalDataflowNode_v2::FGeometryCollectionTerminalDataflowNode_v2(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid)
: FDataflowTerminalNode(InParam, InGuid)
{
RegisterInputConnection(&Collection);
RegisterInputConnection(&Materials);
RegisterInputConnection(&InstancedMeshes);
RegisterInputConnection(&RootProxyMeshes);
RegisterOutputConnection(&Collection, &Collection);
RegisterOutputConnection(&Materials, &Materials);
RegisterOutputConnection(&InstancedMeshes, &InstancedMeshes);
}
void FGeometryCollectionTerminalDataflowNode_v2::SetAssetValue(TObjectPtr<UObject> Asset, UE::Dataflow::FContext& Context) const
{
using FGeometryCollectionPtr = TSharedPtr<FGeometryCollection, ESPMode::ThreadSafe>;
using FMaterialArray = TArray<TObjectPtr<UMaterialInterface>>;
using FInstancedMeshesArray = TArray<FGeometryCollectionAutoInstanceMesh>;
using FRootProxyMeshesArray = TArray<FDataflowRootProxyMesh>;
if (UGeometryCollection* CollectionAsset = Cast<UGeometryCollection>(Asset.Get()))
{
if (FGeometryCollectionPtr GeometryCollection = CollectionAsset->GetGeometryCollection())
{
const FManagedArrayCollection& InCollection = GetValue(Context, &Collection);
const FMaterialArray& InMaterials = GetValue(Context, &Materials);
const FInstancedMeshesArray& InInstancedMeshes = GetValue(Context, &InstancedMeshes);
const FRootProxyMeshesArray& InRootProxyMeshes = GetValue(Context, &RootProxyMeshes);
constexpr bool bHasInternalMaterial = false; // with dataflow there's no assumption of internal materials
CollectionAsset->ResetFrom(InCollection, InMaterials, bHasInternalMaterial);
CollectionAsset->SetAutoInstanceMeshes(InInstancedMeshes);
if (IsConnected(&RootProxyMeshes))
{
FGeometryCollectionProxyMeshData& Data = CollectionAsset->RootProxyData;
Data.ProxyMeshes.Reset();
Data.ProxyMeshes.Reserve(InRootProxyMeshes.Num());
Data.MeshTransforms.Reset();
Data.MeshTransforms.Reserve(InRootProxyMeshes.Num());
for (const FDataflowRootProxyMesh& ProxyMesh : InRootProxyMeshes)
{
Data.ProxyMeshes.Add(ProxyMesh.Mesh);
Data.MeshTransforms.Add(FTransform3f(ProxyMesh.Transform));
}
}
#if WITH_EDITOR
// make sure we rebuild the render data when we are done setting everything
CollectionAsset->RebuildRenderData();
// also make sure all components using it are getting a notification about it
CollectionAsset->PropagateTransformUpdateToComponents();
#endif
}
}
}
void FGeometryCollectionTerminalDataflowNode_v2::Evaluate(UE::Dataflow::FContext& Context) const
{
// simply forward all inputs to corresponding outputs
SafeForwardInput(Context, &Collection, &Collection);
SafeForwardInput(Context, &Materials, &Materials);
SafeForwardInput(Context, &InstancedMeshes, &InstancedMeshes);
}
// ===========================================================================================================================
// DEPRECATED 5.6 : see FGeometryCollectionTerminalDataflowNode_v2
FGeometryCollectionTerminalDataflowNode::FGeometryCollectionTerminalDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid)
: FDataflowTerminalNode(InParam, InGuid)
{
RegisterInputConnection(&Collection);
RegisterOutputConnection(&Collection, &Collection);
RegisterInputConnection(&Materials);
RegisterInputConnection(&MaterialInstances);
RegisterOutputConnection(&Materials, &Materials);
RegisterOutputConnection(&MaterialInstances, &MaterialInstances);
RegisterInputConnection(&InstancedMeshes);
RegisterOutputConnection(&InstancedMeshes, &InstancedMeshes);
}
void FGeometryCollectionTerminalDataflowNode::SetAssetValue(TObjectPtr<UObject> Asset, UE::Dataflow::FContext& Context) const
{
using FGeometryCollectionPtr = TSharedPtr<FGeometryCollection, ESPMode::ThreadSafe>;
using FMaterialArray = TArray<TObjectPtr<UMaterial>>;
using FMaterialInstanceArray = TArray<TObjectPtr<UMaterialInterface>>;
using FInstancedMeshesArray = TArray<FGeometryCollectionAutoInstanceMesh>;
if (UGeometryCollection* CollectionAsset = Cast<UGeometryCollection>(Asset.Get()))
{
if (FGeometryCollectionPtr GeometryCollection = CollectionAsset->GetGeometryCollection())
{
const FManagedArrayCollection& InCollection = GetValue(Context, &Collection);
const FMaterialArray& InMaterials = GetValue(Context, &Materials);
const FMaterialInstanceArray& InMaterialInstances = GetValue(Context, &MaterialInstances);
const FInstancedMeshesArray& InInstancedMeshes = GetValue(Context, &InstancedMeshes);
const bool bHasInternalMaterial = false; // with data flow there's no assumption of internal materials
if (InMaterialInstances.Num() > 0)
{
CollectionAsset->ResetFrom(InCollection, InMaterialInstances, false);
}
else
{
CollectionAsset->ResetFrom(InCollection, InMaterials, false);
}
CollectionAsset->SetAutoInstanceMeshes(InInstancedMeshes);
#if WITH_EDITOR
// make sure we rebuild the render data when we are done setting everything
CollectionAsset->RebuildRenderData();
// also make sure all components using it are getting a notification about it
CollectionAsset->PropagateTransformUpdateToComponents();
#endif
}
}
}
void FGeometryCollectionTerminalDataflowNode::Evaluate(UE::Dataflow::FContext& Context) const
{
// simply forward all inputs to corresponding outputs
SafeForwardInput(Context, &Collection, &Collection);
SafeForwardInput(Context, &Materials, &Materials);
SafeForwardInput(Context, &MaterialInstances, &MaterialInstances);
SafeForwardInput(Context, &InstancedMeshes, &InstancedMeshes);
}
// ===========================================================================================================================
FGetGeometryCollectionAssetDataflowNode::FGetGeometryCollectionAssetDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid)
: FDataflowNode(InParam, InGuid)
{
RegisterOutputConnection(&Asset);
}
void FGetGeometryCollectionAssetDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
ensure(Out->IsA(&Asset));
TObjectPtr<UGeometryCollection> CollectionAsset(nullptr);
if (const UE::Dataflow::FEngineContext* EngineContext = Context.AsType<UE::Dataflow::FEngineContext>())
{
CollectionAsset = Cast<UGeometryCollection>(EngineContext->Owner);
}
SetValue(Context, CollectionAsset, &Asset);
}
// ===========================================================================================================================
FGetGeometryCollectionSourcesDataflowNode::FGetGeometryCollectionSourcesDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid)
: FDataflowNode(InParam, InGuid)
{
RegisterInputConnection(&Asset);
RegisterOutputConnection(&Sources);
}
void FGetGeometryCollectionSourcesDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
ensure(Out->IsA(&Sources));
TArray<FGeometryCollectionSource> OutSources;
if (const TObjectPtr<const UGeometryCollection> InAsset = GetValue(Context, &Asset))
{
#if WITH_EDITORONLY_DATA
OutSources = InAsset->GeometrySource;
#else
ensureMsgf(false, TEXT("FGetGeometryCollectionSourcesDataflowNode - GeometrySource is only available in editor, returning an empty array"));
#endif
}
SetValue(Context, OutSources, &Sources);
}
// ===========================================================================================================================
// DEPRECATED 5.6 : see FCreateGeometryCollectionFromSourcesDataflowNode_v2
FCreateGeometryCollectionFromSourcesDataflowNode::FCreateGeometryCollectionFromSourcesDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid)
: FDataflowNode(InParam, InGuid)
{
RegisterInputConnection(&Sources);
RegisterOutputConnection(&Collection);
RegisterOutputConnection(&Materials);
RegisterOutputConnection(&MaterialInstances);
RegisterOutputConnection(&InstancedMeshes);
}
void FCreateGeometryCollectionFromSourcesDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
ensure(Out->IsA(&Collection) || Out->IsA(&Materials) || Out->IsA(&MaterialInstances) || Out->IsA(&InstancedMeshes));
const TArray<FGeometryCollectionSource>& InSources = GetValue(Context, &Sources);
FGeometryCollection OutCollection;
TArray<TObjectPtr<UMaterialInterface>> OutMaterialInstances;
TArray<FGeometryCollectionAutoInstanceMesh> OutInstancedMeshes;
// make sure we have an attribute for instanced meshes
GeometryCollection::Facades::FCollectionInstancedMeshFacade InstancedMeshFacade(OutCollection);
InstancedMeshFacade.DefineSchema();
constexpr bool bReindexMaterialsInLoop = false;
for (int32 SourceIndex = 0; SourceIndex < InSources.Num(); SourceIndex++)
{
const FGeometryCollectionSource& Source = InSources[SourceIndex];
const int32 NumTransformsBeforeAppending = OutCollection.NumElements(FGeometryCollection::TransformGroup);
// todo: change AppendGeometryCollectionSource to take a FManagedArrayCollection so we could move the collection when assigning it to the output
FGeometryCollectionEngineConversion::AppendGeometryCollectionSource(Source, OutCollection, MutableView(OutMaterialInstances), bReindexMaterialsInLoop);
// todo(chaos) if the source is a geometry collection this will not work properly
FGeometryCollectionAutoInstanceMesh InstancedMesh;
InstancedMesh.Mesh = Cast<UStaticMesh>(Source.SourceGeometryObject.TryLoad());
InstancedMesh.Materials = Source.SourceMaterial;
InstancedMesh.NumInstances = 0;
InstancedMesh.CustomData.Reset();
int32 InstancedMeshIndex = OutInstancedMeshes.Find(InstancedMesh);
if (InstancedMeshIndex == INDEX_NONE)
{
InstancedMeshIndex = OutInstancedMeshes.Add(InstancedMesh);
}
OutInstancedMeshes[InstancedMeshIndex].NumInstances++;
OutInstancedMeshes[InstancedMeshIndex].CustomData.Append(Source.InstanceCustomData);
// add the instanced mesh for all the newly added transforms
const int32 NumTransformsAfterAppending = OutCollection.NumElements(FGeometryCollection::TransformGroup);
//ensure((NumTransformsAfterAppending - NumTransformsBeforeAppending) == 1);
for (int32 TransformIndex = NumTransformsBeforeAppending; TransformIndex < NumTransformsAfterAppending; TransformIndex++)
{
InstancedMeshFacade.SetIndex(TransformIndex, InstancedMeshIndex);
}
}
if (bReindexMaterialsInLoop == false)
{
OutCollection.ReindexMaterials();
}
// add the instanced mesh indices
const int32 NumTransforms = InstancedMeshFacade.GetNumIndices();
for (int32 TransformIndex = 0; TransformIndex < NumTransforms; TransformIndex++)
{
}
// make sure we have only one root
if (FGeometryCollectionClusteringUtility::ContainsMultipleRootBones(&OutCollection))
{
FGeometryCollectionClusteringUtility::ClusterAllBonesUnderNewRoot(&OutCollection);
}
// make sure we have a level attribute
Chaos::Facades::FCollectionHierarchyFacade HierarchyFacade(OutCollection);
HierarchyFacade.GenerateLevelAttribute();
TArray<TObjectPtr<UMaterial>> OutMaterials;
FGeometryCollectionEngineConversion::GetMaterialsFromInstances(OutMaterialInstances, OutMaterials);
// we have to make a copy since we have generated a FGeometryCollection which is inherited from FManagedArrayCollection
SetValue(Context, static_cast<const FManagedArrayCollection&>(OutCollection), &Collection);
SetValue(Context, MoveTemp(OutMaterials), &Materials);
SetValue(Context, MoveTemp(OutMaterialInstances), &MaterialInstances);
SetValue(Context, MoveTemp(OutInstancedMeshes), &InstancedMeshes);
}
// ===========================================================================================================================
FCreateGeometryCollectionFromSourcesDataflowNode_v2::FCreateGeometryCollectionFromSourcesDataflowNode_v2(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid)
: FDataflowNode(InParam, InGuid)
{
RegisterInputConnection(&Sources);
RegisterOutputConnection(&Collection);
RegisterOutputConnection(&Materials);
RegisterOutputConnection(&InstancedMeshes);
RegisterOutputConnection(&RootProxyMeshes);
}
void FCreateGeometryCollectionFromSourcesDataflowNode_v2::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
ensure(Out->IsA(&Collection) || Out->IsA(&Materials) || Out->IsA(&InstancedMeshes) || Out->IsA(&RootProxyMeshes));
const TArray<FGeometryCollectionSource>& InSources = GetValue(Context, &Sources);
FGeometryCollection OutCollection;
TArray<TObjectPtr<UMaterialInterface>> OutMaterials;
TArray<FGeometryCollectionAutoInstanceMesh> OutInstancedMeshes;
TArray<FDataflowRootProxyMesh> OutRootProxyMeshes;
// make sure we have an attribute for instanced meshes
GeometryCollection::Facades::FCollectionInstancedMeshFacade InstancedMeshFacade(OutCollection);
InstancedMeshFacade.DefineSchema();
constexpr bool bReindexMaterialsInLoop = false;
for (int32 SourceIndex = 0; SourceIndex < InSources.Num(); SourceIndex++)
{
const FGeometryCollectionSource& Source = InSources[SourceIndex];
const int32 NumTransformsBeforeAppending = OutCollection.NumElements(FGeometryCollection::TransformGroup);
// todo: change AppendGeometryCollectionSource to take a FManagedArrayCollection so we could move the collection when assigning it to the output
FGeometryCollectionEngineConversion::AppendGeometryCollectionSource(Source, OutCollection, MutableView(OutMaterials), bReindexMaterialsInLoop);
UStaticMesh* StaticMeshObject = Cast<UStaticMesh>(Source.SourceGeometryObject.TryLoad());
// todo(chaos) if the source is a geometry collection this will not work properly
FGeometryCollectionAutoInstanceMesh InstancedMesh;
InstancedMesh.Mesh = StaticMeshObject;
InstancedMesh.Materials = Source.SourceMaterial;
InstancedMesh.NumInstances = 0;
InstancedMesh.CustomData.Reset();
int32 InstancedMeshIndex = OutInstancedMeshes.Find(InstancedMesh);
if (InstancedMeshIndex == INDEX_NONE)
{
InstancedMeshIndex = OutInstancedMeshes.Add(InstancedMesh);
}
OutInstancedMeshes[InstancedMeshIndex].NumInstances++;
OutInstancedMeshes[InstancedMeshIndex].CustomData.Append(Source.InstanceCustomData);
// add the instanced mesh for all the newly added transforms
const int32 NumTransformsAfterAppending = OutCollection.NumElements(FGeometryCollection::TransformGroup);
//ensure((NumTransformsAfterAppending - NumTransformsBeforeAppending) == 1);
for (int32 TransformIndex = NumTransformsBeforeAppending; TransformIndex < NumTransformsAfterAppending; TransformIndex++)
{
InstancedMeshFacade.SetIndex(TransformIndex, InstancedMeshIndex);
}
// Root proxy meshes
FDataflowRootProxyMesh RootProxyMesh;
RootProxyMesh.Mesh = StaticMeshObject;
RootProxyMesh.Transform = Source.LocalTransform;
OutRootProxyMeshes.Add(RootProxyMesh);
}
if (bReindexMaterialsInLoop == false)
{
OutCollection.ReindexMaterials();
}
// add the instanced mesh indices
const int32 NumTransforms = InstancedMeshFacade.GetNumIndices();
for (int32 TransformIndex = 0; TransformIndex < NumTransforms; TransformIndex++)
{
}
// make sure we have only one root
if (FGeometryCollectionClusteringUtility::ContainsMultipleRootBones(&OutCollection))
{
FGeometryCollectionClusteringUtility::ClusterAllBonesUnderNewRoot(&OutCollection);
}
// make sure we have a level attribute
Chaos::Facades::FCollectionHierarchyFacade HierarchyFacade(OutCollection);
HierarchyFacade.GenerateLevelAttribute();
// we have to make a copy since we have generated a FGeometryCollection which is inherited from FManagedArrayCollection
SetValue(Context, static_cast<const FManagedArrayCollection&>(OutCollection), &Collection);
SetValue(Context, MoveTemp(OutMaterials), &Materials);
SetValue(Context, MoveTemp(OutInstancedMeshes), &InstancedMeshes);
SetValue(Context, MoveTemp(OutRootProxyMeshes), &RootProxyMeshes);
}
// ===========================================================================================================================
// DEPRECATED 5.6 : see FGeometryCollectionToCollectionDataflowNode_v2
FGeometryCollectionToCollectionDataflowNode::FGeometryCollectionToCollectionDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid)
: FDataflowNode(InParam, InGuid)
{
RegisterOutputConnection(&Collection);
RegisterOutputConnection(&Materials);
RegisterOutputConnection(&MaterialInstances);
RegisterOutputConnection(&InstancedMeshes);
}
void FGeometryCollectionToCollectionDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
ensure(Out->IsA(&Collection) || Out->IsA(&Materials) || Out->IsA(&MaterialInstances) || Out->IsA(&InstancedMeshes));
FManagedArrayCollection OutCollection;
TArray<TObjectPtr<UMaterialInterface>> OutMaterialInstances;
TArray<FGeometryCollectionAutoInstanceMesh> OutInstancedMeshes;
if (GeometryCollection)
{
FGeometryCollectionEngineConversion::ConvertGeometryCollectionToGeometryCollection(GeometryCollection, OutCollection, OutMaterialInstances, OutInstancedMeshes);
}
TArray<TObjectPtr<UMaterial>> OutMaterials;
FGeometryCollectionEngineConversion::GetMaterialsFromInstances(OutMaterialInstances, OutMaterials);
// Set Outputs
SetValue(Context, MoveTemp(OutCollection), &Collection);
SetValue(Context, MoveTemp(OutMaterials), &Materials);
SetValue(Context, MoveTemp(OutMaterialInstances), &MaterialInstances);
SetValue(Context, MoveTemp(OutInstancedMeshes), &InstancedMeshes);
}
// ===========================================================================================================================
FGeometryCollectionToCollectionDataflowNode_v2::FGeometryCollectionToCollectionDataflowNode_v2(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid)
: FDataflowNode(InParam, InGuid)
{
RegisterOutputConnection(&Collection);
RegisterOutputConnection(&Materials);
RegisterOutputConnection(&InstancedMeshes);
RegisterOutputConnection(&RootProxyMeshes);
}
void FGeometryCollectionToCollectionDataflowNode_v2::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
ensure(Out->IsA(&Collection) || Out->IsA(&Materials) || Out->IsA(&InstancedMeshes) || Out->IsA(&RootProxyMeshes));
FManagedArrayCollection OutCollection;
TArray<TObjectPtr<UMaterialInterface>> OutMaterials;
TArray<FGeometryCollectionAutoInstanceMesh> OutInstancedMeshes;
TArray<FDataflowRootProxyMesh> OutRootProxyMeshes;
if (GeometryCollection)
{
FGeometryCollectionEngineConversion::ConvertGeometryCollectionToGeometryCollection(GeometryCollection, OutCollection, OutMaterials, OutInstancedMeshes);
for (int32 MeshIndex = 0; MeshIndex < GeometryCollection->RootProxyData.ProxyMeshes.Num(); MeshIndex++)
{
FDataflowRootProxyMesh RootProxyMesh;
RootProxyMesh.Mesh = GeometryCollection->RootProxyData.ProxyMeshes[MeshIndex];
RootProxyMesh.Transform = FTransform(GeometryCollection->RootProxyData.GetMeshTransform(MeshIndex));
OutRootProxyMeshes.Emplace(RootProxyMesh);
}
}
// Set Outputs
SetValue(Context, MoveTemp(OutCollection), &Collection);
SetValue(Context, MoveTemp(OutMaterials), &Materials);
SetValue(Context, MoveTemp(OutInstancedMeshes), &InstancedMeshes);
SetValue(Context, MoveTemp(OutRootProxyMeshes), &RootProxyMeshes);
}
// ===========================================================================================================================
// DEPRECATED 5.6 : see FBlueprintToCollectionDataflowNode
FBlueprintToCollectionDataflowNode::FBlueprintToCollectionDataflowNode(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid)
: FDataflowNode(InParam, InGuid)
{
RegisterOutputConnection(&Collection);
RegisterOutputConnection(&Materials);
RegisterOutputConnection(&MaterialInstances);
RegisterOutputConnection(&InstancedMeshes);
}
void FBlueprintToCollectionDataflowNode::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
ensure(Out->IsA(&Collection) || Out->IsA(&Materials) || Out->IsA(&MaterialInstances) || Out->IsA(&InstancedMeshes));
FManagedArrayCollection OutCollection;
TArray<TObjectPtr<UMaterialInterface>> OutMaterialInstances;
TArray<FGeometryCollectionAutoInstanceMesh> OutInstancedMeshes;
if (Blueprint)
{
if (TUniquePtr<FPreviewScene> PreviewScene = MakeUnique<FPreviewScene>(FPreviewScene::ConstructionValues()))
{
if (UWorld* PreviewWorld = PreviewScene->GetWorld())
{
FActorSpawnParameters SpawnInfo;
SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
SpawnInfo.bNoFail = true;
SpawnInfo.ObjectFlags = RF_Transient;
if (AActor* PreviewActor = PreviewWorld->SpawnActor(Blueprint->GeneratedClass, nullptr, SpawnInfo))
{
FGeometryCollectionEngineConversion::FSkeletalMeshToCollectionConversionParameters ConversionParameters;
FGeometryCollectionEngineConversion::ConvertActorToGeometryCollection(PreviewActor, OutCollection, OutMaterialInstances, OutInstancedMeshes, ConversionParameters, bSplitComponents);
}
}
}
}
TArray<TObjectPtr<UMaterial>> OutMaterials;
FGeometryCollectionEngineConversion::GetMaterialsFromInstances(OutMaterialInstances, OutMaterials);
// Set Outputs
SetValue(Context, MoveTemp(OutCollection), &Collection);
SetValue(Context, MoveTemp(OutMaterials), &Materials);
SetValue(Context, MoveTemp(OutMaterialInstances), &MaterialInstances);
SetValue(Context, MoveTemp(OutInstancedMeshes), &InstancedMeshes);
}
// ===========================================================================================================================
FBlueprintToCollectionDataflowNode_v2::FBlueprintToCollectionDataflowNode_v2(const UE::Dataflow::FNodeParameters& InParam, FGuid InGuid)
: FDataflowNode(InParam, InGuid)
{
RegisterOutputConnection(&Collection);
RegisterOutputConnection(&Materials);
RegisterOutputConnection(&InstancedMeshes);
RegisterOutputConnection(&RootProxyMeshes);
}
void FBlueprintToCollectionDataflowNode_v2::Evaluate(UE::Dataflow::FContext& Context, const FDataflowOutput* Out) const
{
ensure(Out->IsA(&Collection) || Out->IsA(&Materials) || Out->IsA(&InstancedMeshes) || Out->IsA(&RootProxyMeshes));
FManagedArrayCollection OutCollection;
TArray<TObjectPtr<UMaterialInterface>> OutMaterials;
TArray<FGeometryCollectionAutoInstanceMesh> OutInstancedMeshes;
TArray<FDataflowRootProxyMesh> OutRootProxyMeshes;
if (Blueprint)
{
if (TUniquePtr<FPreviewScene> PreviewScene = MakeUnique<FPreviewScene>(FPreviewScene::ConstructionValues()))
{
if (UWorld* PreviewWorld = PreviewScene->GetWorld())
{
FActorSpawnParameters SpawnInfo;
SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
SpawnInfo.bNoFail = true;
SpawnInfo.ObjectFlags = RF_Transient;
if (AActor* PreviewActor = PreviewWorld->SpawnActor(Blueprint->GeneratedClass, nullptr, SpawnInfo))
{
FGeometryCollectionEngineConversion::FSkeletalMeshToCollectionConversionParameters ConversionParameters;
FGeometryCollectionEngineConversion::ConvertActorToGeometryCollection(PreviewActor, OutCollection, OutMaterials, OutInstancedMeshes, ConversionParameters, bSplitComponents);
// root proxies
const FTransform ActorTransform(PreviewActor->GetTransform());
TInlineComponentArray<UStaticMeshComponent*> StaticMeshComponents(PreviewActor);
for (UStaticMeshComponent* StaticMeshComponent : StaticMeshComponents)
{
if (StaticMeshComponent)
{
if (UStaticMesh* StaticMeshObject = StaticMeshComponent->GetStaticMesh())
{
FTransform MeshTransform(StaticMeshComponent->GetComponentTransform());
MeshTransform.SetTranslation((MeshTransform.GetTranslation() - ActorTransform.GetTranslation()));
FDataflowRootProxyMesh RootProxyMesh;
RootProxyMesh.Mesh = StaticMeshObject;
RootProxyMesh.Transform = MeshTransform;
OutRootProxyMeshes.Add(RootProxyMesh);
}
}
}
}
}
}
}
// Set Outputs
SetValue(Context, MoveTemp(OutCollection), &Collection);
SetValue(Context, MoveTemp(OutMaterials), &Materials);
SetValue(Context, MoveTemp(OutInstancedMeshes), &InstancedMeshes);
SetValue(Context, MoveTemp(OutRootProxyMeshes), &RootProxyMeshes);
}