// 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 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 Asset, UE::Dataflow::FContext& Context) const { using FGeometryCollectionPtr = TSharedPtr; using FMaterialArray = TArray>; using FInstancedMeshesArray = TArray; using FRootProxyMeshesArray = TArray; if (UGeometryCollection* CollectionAsset = Cast(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 Asset, UE::Dataflow::FContext& Context) const { using FGeometryCollectionPtr = TSharedPtr; using FMaterialArray = TArray>; using FMaterialInstanceArray = TArray>; using FInstancedMeshesArray = TArray; if (UGeometryCollection* CollectionAsset = Cast(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 CollectionAsset(nullptr); if (const UE::Dataflow::FEngineContext* EngineContext = Context.AsType()) { CollectionAsset = Cast(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 OutSources; if (const TObjectPtr 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& InSources = GetValue(Context, &Sources); FGeometryCollection OutCollection; TArray> OutMaterialInstances; TArray 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(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> 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(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& InSources = GetValue(Context, &Sources); FGeometryCollection OutCollection; TArray> OutMaterials; TArray OutInstancedMeshes; TArray 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(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(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> OutMaterialInstances; TArray OutInstancedMeshes; if (GeometryCollection) { FGeometryCollectionEngineConversion::ConvertGeometryCollectionToGeometryCollection(GeometryCollection, OutCollection, OutMaterialInstances, OutInstancedMeshes); } TArray> 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> OutMaterials; TArray OutInstancedMeshes; TArray 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> OutMaterialInstances; TArray OutInstancedMeshes; if (Blueprint) { if (TUniquePtr PreviewScene = MakeUnique(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> 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> OutMaterials; TArray OutInstancedMeshes; TArray OutRootProxyMeshes; if (Blueprint) { if (TUniquePtr PreviewScene = MakeUnique(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 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); }