Files
UnrealEngine/Engine/Plugins/Interchange/Runtime/Source/Pipelines/Private/InterchangeGenericScenesPipeline.cpp
2025-05-18 13:04:45 +08:00

1533 lines
55 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "InterchangeGenericScenesPipeline.h"
#include "InterchangeActorFactoryNode.h"
#include "InterchangeAssetImportData.h"
#include "InterchangeAssetUserData.h"
#include "InterchangeCameraFactoryNode.h"
#include "InterchangeCameraNode.h"
#include "InterchangeCommonPipelineDataFactoryNode.h"
#include "InterchangeDecalActorFactoryNode.h"
#include "InterchangeDecalNode.h"
#include "InterchangeEditorUtilitiesBase.h"
#include "InterchangeHeterogeneousVolumeActorFactoryNode.h"
#include "InterchangeImportReset.h"
#include "InterchangeLevelFactoryNode.h"
#include "InterchangeLevelInstanceActorFactoryNode.h"
#include "InterchangeLightFactoryNode.h"
#include "InterchangeLightNode.h"
#include "InterchangeManager.h"
#include "InterchangeMeshActorFactoryNode.h"
#include "InterchangeMeshNode.h"
#include "InterchangePipelineHelper.h"
#include "InterchangePipelineLog.h"
#include "InterchangePipelineMeshesUtilities.h"
#include "InterchangeSceneImportAsset.h"
#include "InterchangeSceneImportAssetFactoryNode.h"
#include "InterchangeSceneNode.h"
#include "InterchangeSceneVariantSetsFactoryNode.h"
#include "InterchangeSkeletalMeshFactoryNode.h"
#include "InterchangeSkeletalMeshLodDataNode.h"
#include "InterchangeSkeletonFactoryNode.h"
#include "InterchangeStaticMeshFactoryNode.h"
#include "InterchangeStaticMeshLodDataNode.h"
#include "InterchangeVariantSetNode.h"
#include "InterchangeVolumeNode.h"
#include "Nodes/InterchangeSourceNode.h"
#include "Nodes/InterchangeUserDefinedAttribute.h"
#include "Scene/InterchangeActorHelper.h"
#include "Volume/InterchangeVolumeDefinitions.h"
#include "Animation/SkeletalMeshActor.h"
#include "CineCameraActor.h"
#include "Engine/Blueprint.h"
#include "Engine/DirectionalLight.h"
#include "Engine/Level.h"
#include "Engine/PointLight.h"
#include "Engine/RectLight.h"
#include "Engine/SpotLight.h"
#include "Engine/StaticMeshActor.h"
#include "Engine/World.h"
#include "GameFramework/WorldSettings.h"
#include "LevelInstance/LevelInstanceActor.h"
#include "Misc/PackageName.h"
#include "PackedLevelActor/PackedLevelActor.h"
#include "PackedLevelActor/PackedLevelActorBuilder.h"
#include "PreviewScene.h"
#include "UObject/UObjectIterator.h"
#include "WorldPartition/WorldPartition.h"
#if WITH_EDITOR
#include "ObjectTools.h"
#endif
#include UE_INLINE_GENERATED_CPP_BY_NAME(InterchangeGenericScenesPipeline)
#define COPY_FROM_TRANSLATED_TO_FACTORY(TranslatedNode, FactoryNode, AttributeName, AttributeType) \
if(AttributeType AttributeName; TranslatedNode->GetCustom##AttributeName(AttributeName)) \
{ \
FactoryNode->SetCustom##AttributeName(AttributeName); \
}
namespace UE::Interchange::Private
{
//Either a (TransformSpecialized || !JointSpecialized || RootJoint) can be a parent (only those get FactoryNodes) :
FString FindFactoryParentSceneNodeUid(UInterchangeBaseNodeContainer* BaseNodeContainer, TArray<FString>& ActiveSkeletonUids, const UInterchangeSceneNode* SceneNode)
{
FString ParentUid = SceneNode->GetParentUid();
if (const UInterchangeSceneNode* ParentSceneNode = Cast<UInterchangeSceneNode>(BaseNodeContainer->GetNode(ParentUid)))
{
if (!ActiveSkeletonUids.Contains(ParentUid) || ParentSceneNode->IsSpecializedTypeContains(UE::Interchange::FSceneNodeStaticData::GetTransformSpecializeTypeString()))
{
return ParentUid;
}
else
{
bool bParentIsJoint = ParentSceneNode->IsSpecializedTypeContains(UE::Interchange::FSceneNodeStaticData::GetJointSpecializeTypeString());
if (bParentIsJoint)
{
//Check if its a rootjoint:
// aka check parent's parent if its !joint:
FString ParentsParentUid = ParentSceneNode->GetParentUid();
if (const UInterchangeSceneNode* ParentsParentSceneNode = Cast<UInterchangeSceneNode>(BaseNodeContainer->GetNode(ParentsParentUid)))
{
bool bParentsParentIsJoint = ParentSceneNode->IsSpecializedTypeContains(UE::Interchange::FSceneNodeStaticData::GetJointSpecializeTypeString());
if (bParentsParentIsJoint)
{
return FindFactoryParentSceneNodeUid(BaseNodeContainer, ActiveSkeletonUids, ParentSceneNode);
}
else
{
return ParentUid;
}
}
}
else
{
return ParentUid;
}
}
}
return UInterchangeBaseNode::InvalidNodeUid();
}
void UpdateReimportStrategyFlags(UInterchangeBaseNodeContainer& NodeContainer, UInterchangeFactoryBaseNode& FactoryNode, EReimportStrategyFlags ReimportPropertyStrategy)
{
FactoryNode.SetReimportStrategyFlags(ReimportPropertyStrategy);
TArray<FString> ActorDependencies;
FactoryNode.GetFactoryDependencies(ActorDependencies);
for (const FString& FactoryNodeID : ActorDependencies)
{
if (UInterchangeFactoryBaseNode* DependencyFactoryNode = NodeContainer.GetFactoryNode(FactoryNodeID))
{
UpdateReimportStrategyFlags(NodeContainer, *DependencyFactoryNode, ReimportPropertyStrategy);
}
}
}
#if WITH_EDITOR
void DeleteAssets(const TArray<UObject*>& AssetsToDelete)
{
if (AssetsToDelete.IsEmpty())
{
return;
}
TArray<UObject*> ObjectsToForceDelete;
ObjectsToForceDelete.Reserve(AssetsToDelete.Num());
for (UObject* Asset : AssetsToDelete)
{
if (Asset)
{
ObjectsToForceDelete.Add(Asset);
}
}
if (ObjectsToForceDelete.IsEmpty())
{
return;
}
constexpr bool bShowConfirmation = true;
constexpr ObjectTools::EAllowCancelDuringDelete AllowCancelDuringDelete = ObjectTools::EAllowCancelDuringDelete::CancelNotAllowed;
ObjectTools::DeleteObjects(ObjectsToForceDelete, bShowConfirmation, AllowCancelDuringDelete);
}
#else
void DeleteAssets(const TArray<UObject*>& AssetsToDelete)
{
if (AssetsToDelete.IsEmpty())
{
return;
}
bool bForceGarbageCollection = false;
for (UObject* Asset : AssetsToDelete)
{
if (Asset)
{
Asset->Rename(nullptr, GetTransientPackage(), REN_NonTransactional | REN_DontCreateRedirectors);
if (Asset->IsRooted())
{
Asset->RemoveFromRoot();
}
Asset->ClearFlags(RF_Public | RF_Standalone);
Asset->MarkAsGarbage();
bForceGarbageCollection = true;
}
}
if (bForceGarbageCollection)
{
CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS);
}
}
#endif // WITH_EDITOR
void DeleteActors(const TArray<AActor*>& ActorsToDelete)
{
if (ActorsToDelete.IsEmpty())
{
return;
}
for (AActor* Actor : ActorsToDelete)
{
if (!Actor)
{
continue;
}
if (UWorld* OwningWorld = Actor->GetWorld())
{
OwningWorld->EditorDestroyActor(Actor, true);
// Since deletion can be delayed, rename to avoid future name collision
// Call UObject::Rename directly on actor to avoid AActor::Rename which unnecessarily sunregister and re-register components
Actor->UObject::Rename(nullptr, GetTransientPackage(), REN_DontCreateRedirectors);
}
}
}
}
UInterchangeGenericLevelPipeline::~UInterchangeGenericLevelPipeline()
{
if (PreviewScene)
{
delete PreviewScene;
PreviewScene = nullptr;
}
}
void UInterchangeGenericLevelPipeline::AdjustSettingsForContext(const FInterchangePipelineContextParams& ContextParams)
{
Super::AdjustSettingsForContext(ContextParams);
bIsReimportContext |= ContextParams.ReimportAsset != nullptr;
}
void UInterchangeGenericLevelPipeline::ExecutePipeline(UInterchangeBaseNodeContainer* InBaseNodeContainer, const TArray<UInterchangeSourceData*>& InSourceDatas, const FString& ContentBasePath)
{
if (!InBaseNodeContainer)
{
UE_LOG(LogInterchangePipeline, Warning, TEXT("UInterchangeGenericLevelPipeline: Cannot execute pre-import pipeline because InBaseNodeContrainer is null"));
return;
}
//Clear any editor selection to avoid crash if selected actor change
if (UInterchangeEditorUtilitiesBase* EditorUtilities = UInterchangeManager::GetInterchangeManager().GetEditorUtilities())
{
EditorUtilities->ClearEditorSelection();
}
BaseNodeContainer = InBaseNodeContainer;
// Make sure all factory nodes created for assets have the chosen policy strategy
InBaseNodeContainer->IterateNodesOfType<UInterchangeFactoryBaseNode>([this](const FString& NodeUid, UInterchangeFactoryBaseNode* FactoryNode)
{
if (this->bForceReimportDeletedAssets)
{
FactoryNode->SetForceNodeReimport();
}
});
FTransform GlobalOffsetTransform = FTransform::Identity;
if (UInterchangeCommonPipelineDataFactoryNode* CommonPipelineDataFactoryNode = UInterchangeCommonPipelineDataFactoryNode::GetUniqueInstance(BaseNodeContainer))
{
CommonPipelineDataFactoryNode->GetCustomGlobalOffsetTransform(GlobalOffsetTransform);
}
TArray<UInterchangeSceneNode*> SceneNodes;
//Find all translated node we need for this pipeline
BaseNodeContainer->IterateNodes([&SceneNodes](const FString& NodeUid, UInterchangeBaseNode* Node)
{
switch(Node->GetNodeContainerType())
{
case EInterchangeNodeContainerType::TranslatedScene:
{
if (UInterchangeSceneNode* SceneNode = Cast<UInterchangeSceneNode>(Node))
{
SceneNodes.Add(SceneNode);
}
}
break;
}
});
#if WITH_EDITORONLY_DATA
const FString FilePath = FPaths::ConvertRelativePathToFull(InSourceDatas[0]->GetFilename());
UInterchangeSourceNode* SourceNode = UInterchangeSourceNode::FindOrCreateUniqueInstance(InBaseNodeContainer);
if (SceneHierarchyType != EInterchangeSceneHierarchyType::CreateLevelActors)
{
ensure(!LevelFactoryNode);
const bool bCreatePackedActor = SceneHierarchyType == EInterchangeSceneHierarchyType::CreatePackedActor;
const FString DisplayLabel = ("Level_") + FPaths::GetBaseFilename(FilePath);
const FString NodeUid = TEXT("Level_") + FilePath;
ensure(!BaseNodeContainer->IsNodeUidValid(NodeUid));
LevelFactoryNode = NewObject<UInterchangeLevelFactoryNode>(BaseNodeContainer, NAME_None);
BaseNodeContainer->SetupNode(LevelFactoryNode, NodeUid, DisplayLabel, EInterchangeNodeContainerType::FactoryData);
LevelFactoryNode->SetCustomCreateWorldPartitionLevel(false);
LevelFactoryNode->SetCustomShouldCreateLevel(!bIsReimportContext);
UE::Interchange::PipelineHelper::FillSubPathFromSourceNode(LevelFactoryNode, SourceNode);
if (bIsReimportContext)
{
//When we re-import we want the FinalizeObject_GameThread to be call on the level factory for this level node
LevelFactoryNode->SetForceNodeReimport();
}
//Create a level instance or packed actor
{
LevelInstanceActorFactoryNode = NewObject<UInterchangeLevelInstanceActorFactoryNode>(BaseNodeContainer, NAME_None);
if (ensure(LevelInstanceActorFactoryNode))
{
const UClass* LevelInstanceClass = bCreatePackedActor ? APackedLevelActor::StaticClass() : ALevelInstance::StaticClass();
LevelInstanceActorFactoryNode->SetCustomActorClassName(LevelInstanceClass->GetPathName());
const FString ActorNodeUid = TEXT("LevelInstance_") + FilePath;
BaseNodeContainer->SetupNode(LevelInstanceActorFactoryNode, ActorNodeUid, DisplayLabel, EInterchangeNodeContainerType::FactoryData);
//Set the level this actor is referring
LevelInstanceActorFactoryNode->SetCustomLevelReference(LevelFactoryNode->GetUniqueID());
//We ensure the actor will be created after the parent and reference world are create or ready
LevelInstanceActorFactoryNode->AddFactoryDependencyUid(LevelFactoryNode->GetUniqueID());
}
}
}
// Add the SceneImportData factory node
{
ensure(!SceneImportFactoryNode);
const FString DisplayLabel = TEXT("SceneImport_") + FPaths::GetBaseFilename(FilePath);
const FString NodeUid = TEXT("SceneImport_") + FilePath;
const FString FactoryNodeUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(NodeUid);
ensure(!BaseNodeContainer->IsNodeUidValid(FactoryNodeUid));
TArray<FString> FactoryNodeUIDs;
BaseNodeContainer->GetNodeUIDsOfType<UInterchangeFactoryBaseNode>(FactoryNodeUIDs);
SceneImportFactoryNode = NewObject<UInterchangeSceneImportAssetFactoryNode>(BaseNodeContainer, NAME_None);
BaseNodeContainer->SetupNode(SceneImportFactoryNode, FactoryNodeUid, DisplayLabel, EInterchangeNodeContainerType::FactoryData);
UE::Interchange::PipelineHelper::FillSubPathFromSourceNode(SceneImportFactoryNode, SourceNode);
// Add dependency to all the factory nodes created so far
for (const FString& FactoryNodeUID : FactoryNodeUIDs)
{
this->SceneImportFactoryNode->AddFactoryDependencyUid(FactoryNodeUID);
}
}
#endif
/* Find all scene node that are active joint. Non active joint should be convert to actor if they are in a static mesh hierarchy */
CacheActiveJointUids();
//Cache any lod group data, this way we can add actor only for the lod group
TMap< const UInterchangeSceneNode*, TArray<const UInterchangeSceneNode*>> SceneNodesPerLodGroupNode;
for (const UInterchangeSceneNode* SceneNode : SceneNodes)
{
if (!SceneNode)
{
continue;
}
TArray<FString> SpecializeTypes;
SceneNode->GetSpecializedTypes(SpecializeTypes);
if (SpecializeTypes.Num() > 0)
{
if (SpecializeTypes.Contains(UE::Interchange::FSceneNodeStaticData::GetLodGroupSpecializeTypeString()))
{
TArray<const UInterchangeSceneNode*>& LodGroupChildren = SceneNodesPerLodGroupNode.FindOrAdd(SceneNode);
BaseNodeContainer->IterateNodeChildren(SceneNode->GetUniqueID(), [&LodGroupChildren, &SceneNode](const UInterchangeBaseNode* ChildNode)
{
if (const UInterchangeSceneNode* ChildSceneNode = Cast<UInterchangeSceneNode>(ChildNode))
{
//Avoid adding self (first iterative call is self)
if (SceneNode != ChildSceneNode)
{
LodGroupChildren.Add(ChildSceneNode);
}
}
});
}
}
}
auto GetParentLodGroup = [&SceneNodesPerLodGroupNode](const UInterchangeSceneNode* Node)->const UInterchangeSceneNode*
{
for (TPair< const UInterchangeSceneNode*, TArray<const UInterchangeSceneNode*>> LodSceneNodes : SceneNodesPerLodGroupNode)
{
if (LodSceneNodes.Value.Contains(Node))
{
return LodSceneNodes.Key;
}
}
return nullptr;
};
for (const UInterchangeSceneNode* SceneNode : SceneNodes)
{
if (SceneNode)
{
TArray<FString> SpecializeTypes;
SceneNode->GetSpecializedTypes(SpecializeTypes);
if (SpecializeTypes.Num() > 0)
{
if (!SpecializeTypes.Contains(UE::Interchange::FSceneNodeStaticData::GetTransformSpecializeTypeString()))
{
bool bSkipNode = true;
if (SpecializeTypes.Contains(UE::Interchange::FSceneNodeStaticData::GetJointSpecializeTypeString()))
{
if(!Cached_ActiveJointUids.Contains(SceneNode->GetUniqueID()))
{
bSkipNode = false;
}
else
{
//check if its the root joint (we want to create an actor for the root joint)
FString CurrentNodesParentUid = SceneNode->GetParentUid();
const UInterchangeBaseNode* ParentNode = BaseNodeContainer->GetNode(CurrentNodesParentUid);
if (const UInterchangeSceneNode* ParentSceneNode = Cast<UInterchangeSceneNode>(ParentNode))
{
if (!ParentSceneNode->IsSpecializedTypeContains(UE::Interchange::FSceneNodeStaticData::GetJointSpecializeTypeString()))
{
bSkipNode = false;
}
}
}
}
else if (SpecializeTypes.Contains(UE::Interchange::FSceneNodeStaticData::GetLodGroupSpecializeTypeString()))
{
//Do not skip lod group, we always treat it has a mesh (import LODs option control if we import all lod or only the first)
bSkipNode = false;
}
if (bSkipNode)
{
//Skip any scene node that have specialized types but not the "Transform" type.
continue;
}
}
}
if (GetParentLodGroup(SceneNode))
{
//Ignore all lod hierarchy after the lod group
continue;
}
ExecuteSceneNodePreImport(GlobalOffsetTransform, SceneNode);
}
}
//Find all translated scene variant sets
TArray<UInterchangeSceneVariantSetsNode*> SceneVariantSetNodes;
InBaseNodeContainer->IterateNodesOfType<UInterchangeSceneVariantSetsNode>([&SceneVariantSetNodes](const FString& NodeUid, UInterchangeSceneVariantSetsNode* Node)
{
SceneVariantSetNodes.Add(Node);
});
for (const UInterchangeSceneVariantSetsNode* SceneVariantSetNode : SceneVariantSetNodes)
{
if (SceneVariantSetNode)
{
ExecuteSceneVariantSetNodePreImport(*SceneVariantSetNode);
}
}
}
void UInterchangeGenericLevelPipeline::ExecuteSceneNodePreImport(const FTransform& GlobalOffsetTransform, const UInterchangeSceneNode* SceneNode)
{
using namespace UE::Interchange;
if (!BaseNodeContainer || !SceneNode)
{
return;
}
const UInterchangeBaseNode* TranslatedAssetNode = nullptr;
bool bRootJointNode = false;
FString SkeletalMeshFactoryNodeUid;
FString SkeletonFactoryNodeUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(SceneNode->GetUniqueID());
const UInterchangeSkeletonFactoryNode* SkeletonFactoryNode = Cast<UInterchangeSkeletonFactoryNode>(BaseNodeContainer->GetFactoryNode(SkeletonFactoryNodeUid));
if (SkeletonFactoryNode)
{
if (SkeletonFactoryNode->GetCustomSkeletalMeshFactoryNodeUid(SkeletalMeshFactoryNodeUid))
{
if (const UInterchangeFactoryBaseNode* SkeletalMeshFactoryNode = BaseNodeContainer->GetFactoryNode(SkeletalMeshFactoryNodeUid))
{
TArray<FString> NodeUids;
SkeletalMeshFactoryNode->GetTargetNodeUids(NodeUids);
if (NodeUids.Num() > 0)
{
TranslatedAssetNode = BaseNodeContainer->GetNode(NodeUids[0]);
bRootJointNode = true;
}
}
}
}
if (!bRootJointNode)
{
FString AssetInstanceUid;
if (SceneNode->GetCustomAssetInstanceUid(AssetInstanceUid))
{
TranslatedAssetNode = BaseNodeContainer->GetNode(AssetInstanceUid);
}
if (const UInterchangeMeshNode* MeshNode = Cast<UInterchangeMeshNode>(TranslatedAssetNode))
{
bool bIsSkinnedMesh = MeshNode->IsSkinnedMesh();
if(!bIsSkinnedMesh)
{
//In case we have a rigid mesh (static mesh having morph target...), we should find a skeletalmesh factory node holding this mesh node
BaseNodeContainer->BreakableIterateNodesOfType<UInterchangeSkeletalMeshFactoryNode>([this, &SceneNode, &bIsSkinnedMesh](const FString& NodeUid, UInterchangeSkeletalMeshFactoryNode* SkeletalMeshNode)
{
int32 LodCount = SkeletalMeshNode->GetLodDataCount();
TArray<FString> LodDataUniqueIds;
SkeletalMeshNode->GetLodDataUniqueIds(LodDataUniqueIds);
for (int32 LodIndex = 0; LodIndex < LodCount; ++LodIndex)
{
FString LodUniqueId = LodDataUniqueIds[LodIndex];
const UInterchangeSkeletalMeshLodDataNode* LodDataNode = Cast<UInterchangeSkeletalMeshLodDataNode>(BaseNodeContainer->GetNode(LodUniqueId));
if (LodDataNode)
{
TArray<FString> MeshUids;
LodDataNode->GetMeshUids(MeshUids);
if (MeshUids.Contains(SceneNode->GetUniqueID()))
{
bIsSkinnedMesh = true;
return bIsSkinnedMesh;
}
}
}
return false;
});
}
//Skinned mesh are added when the bRootJointNode is true.
//In this case we dont want to add an empty staticmesh actor.
if (bIsSkinnedMesh)
{
return;
}
}
}
// Don't generate actor factory nodes for static mesh scene nodes that haven't produced any static mesh factory nodes,
// as we're not going to have any static mesh to assign to the component anyway!
//
// We currently use this to prevent the generation of static mesh components for collision mesh nodes, when importing into level.
// In that case we won't produce a factory node for the collider mesh node, which we can detect here and early out
if (const UInterchangeMeshNode* MeshNode = Cast<UInterchangeMeshNode>(TranslatedAssetNode))
{
if (!MeshNode->IsSkinnedMesh())
{
const FString MeshFactoryNodeUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(MeshNode->GetUniqueID());
const UInterchangeStaticMeshFactoryNode* MeshFactoryNode = Cast<UInterchangeStaticMeshFactoryNode>(BaseNodeContainer->GetFactoryNode(MeshFactoryNodeUid));
if (!MeshFactoryNode)
{
return;
}
}
}
const bool bIsLodGroup = [&SceneNode]()
{
TArray<FString> SpecializeTypes;
SceneNode->GetSpecializedTypes(SpecializeTypes);
return SpecializeTypes.Contains(UE::Interchange::FSceneNodeStaticData::GetLodGroupSpecializeTypeString());
}();
UInterchangeStaticMeshFactoryNode* LodGroupStaticMeshFactoryNode = nullptr;
if (bIsLodGroup)
{
//Find the staticmesh factory node create for this lod group
BaseNodeContainer->BreakableIterateNodesOfType<UInterchangeStaticMeshFactoryNode>([this, &LodGroupStaticMeshFactoryNode, &SceneNode, &TranslatedAssetNode](const FString& NodeUid, UInterchangeStaticMeshFactoryNode* StaticMeshFactoryNode)
{
TArray<FString> LodDataUids;
StaticMeshFactoryNode->GetLodDataUniqueIds(LodDataUids);
if (!LodDataUids.IsEmpty())
{
if (UInterchangeStaticMeshLodDataNode* LodDataNode = Cast<UInterchangeStaticMeshLodDataNode>(BaseNodeContainer->GetFactoryNode(LodDataUids[0])))
{
TArray<FString> LodDataMeshUids;
LodDataNode->GetMeshUids(LodDataMeshUids);
if (!LodDataMeshUids.IsEmpty())
{
if (BaseNodeContainer->GetIsAncestor(LodDataMeshUids[0], SceneNode->GetUniqueID()))
{
LodGroupStaticMeshFactoryNode = StaticMeshFactoryNode;
//Set the first lod meshuid has the TranslatedAssetNode so it create the mesh actor factory node,
//and setup it properly
if (const UInterchangeSceneNode* LodDataMeshNode = Cast<UInterchangeSceneNode>(BaseNodeContainer->GetNode(LodDataMeshUids[0])))
{
FString AssetUid;
if (LodDataMeshNode->GetCustomAssetInstanceUid(AssetUid))
{
TranslatedAssetNode = BaseNodeContainer->GetNode(AssetUid);
}
}
else
{
TranslatedAssetNode = BaseNodeContainer->GetNode(LodDataMeshUids[0]);
}
return true;
}
}
}
}
return false;
});
if (!LodGroupStaticMeshFactoryNode || !TranslatedAssetNode)
{
//Skip this lod group if there is no associated mesh
return;
}
}
UInterchangeActorFactoryNode* ActorFactoryNode = CreateActorFactoryNode(SceneNode, TranslatedAssetNode);
if (!ensure(ActorFactoryNode))
{
return;
}
UInterchangeUserDefinedAttributesAPI::DuplicateAllUserDefinedAttribute(SceneNode, ActorFactoryNode, false);
TArray<FString> LayerNames;
SceneNode->GetLayerNames(LayerNames);
ActorFactoryNode->AddLayerNames(LayerNames);
TArray<FString> Tags;
SceneNode->GetTags(Tags);
ActorFactoryNode->AddTags(Tags);
FString NodeUid = SceneNode->GetUniqueID() + (bRootJointNode ? TEXT("_SkeletonNode") : TEXT(""));
const FString ActorFactoryNodeUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(NodeUid);
FString ParentFactoryNodeUid;
if (!SceneNode->GetParentUid().IsEmpty())
{
/* Find all scene node that are active joint. Non active joint should be convert to actor if they are in a static mesh hierarchy */
FString ParentNodeUid = UE::Interchange::Private::FindFactoryParentSceneNodeUid(BaseNodeContainer, Cached_ActiveJointUids, SceneNode);
if (ParentNodeUid != UInterchangeBaseNode::InvalidNodeUid())
{
ParentFactoryNodeUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(ParentNodeUid);
}
}
BaseNodeContainer->SetupNode(ActorFactoryNode, ActorFactoryNodeUid, SceneNode->GetDisplayLabel(), EInterchangeNodeContainerType::FactoryData, ParentFactoryNodeUid);
#if WITH_EDITORONLY_DATA
//The level must be create before any actor asset since all actors will be create in the specified level
if (LevelFactoryNode)
{
ActorFactoryNode->AddFactoryDependencyUid(LevelFactoryNode->GetUniqueID());
ActorFactoryNode->SetCustomLevelUid(LevelFactoryNode->GetUniqueID());
LevelFactoryNode->AddCustomActorFactoryNodeUid(ActorFactoryNodeUid);
//The level instance actor must be create after the actor on the reference level are created
if (LevelInstanceActorFactoryNode)
{
LevelInstanceActorFactoryNode->AddFactoryDependencyUid(ActorFactoryNode->GetUniqueID());
}
}
#endif
// The translator is responsible to provide a unique name
ActorFactoryNode->SetAssetName(SceneNode->GetAssetName());
if (ParentFactoryNodeUid.Len())
{
ActorFactoryNode->AddFactoryDependencyUid(ParentFactoryNodeUid);
}
if (bRootJointNode)
{
ActorFactoryNode->AddTargetNodeUid(SkeletalMeshFactoryNodeUid);
}
else if (LodGroupStaticMeshFactoryNode)
{
ActorFactoryNode->AddTargetNodeUid(LodGroupStaticMeshFactoryNode->GetUniqueID());
}
else
{
ActorFactoryNode->AddTargetNodeUid(SceneNode->GetUniqueID());
SceneNode->AddTargetNodeUid(ActorFactoryNode->GetUniqueID());
}
//TODO move this code to the factory, a stack over pipeline can change the global offset transform which will affect this value.
//We prioritize Local (Relative) Transforms due to issues introduced by 0 scales with Global Transforms.
//In case the LocalTransform is not available we fallback onto GlobalTransforms
FTransform LocalTransform;
if (SceneNode->GetCustomLocalTransform(LocalTransform))
{
if (bRootJointNode)
{
//LocalTransform of RootjointNode is already baked into the Skeletal and animation.
LocalTransform = FTransform::Identity;
}
if (SceneNode->GetParentUid().IsEmpty())
{
LocalTransform = LocalTransform * GlobalOffsetTransform;
}
ActorFactoryNode->SetCustomLocalTransform(LocalTransform);
}
else
{
FTransform GlobalTransform;
if (SceneNode->GetCustomGlobalTransform(BaseNodeContainer, GlobalOffsetTransform, GlobalTransform))
{
if (bRootJointNode)
{
GlobalTransform = FTransform::Identity;
//LocalTransform of RootjointNode is already baked into the Skeletal and animation.
//due to that we acquire the Parent SceneNode and get its GlobalTransform:
if (!SceneNode->GetParentUid().IsEmpty())
{
if (const UInterchangeSceneNode* ParentSceneNode = Cast<UInterchangeSceneNode>(BaseNodeContainer->GetNode(SceneNode->GetParentUid())))
{
ParentSceneNode->GetCustomGlobalTransform(BaseNodeContainer, GlobalOffsetTransform, GlobalTransform);
}
}
}
ActorFactoryNode->SetCustomGlobalTransform(GlobalTransform);
}
}
bool bCustomVisibility = true;
SceneNode->GetCustomComponentVisibility(bCustomVisibility);
ActorFactoryNode->SetCustomComponentVisibility(bCustomVisibility);
bCustomVisibility = true;
SceneNode->GetCustomActorVisibility(bCustomVisibility);
ActorFactoryNode->SetCustomActorVisibility(bCustomVisibility);
ActorFactoryNode->SetCustomMobility(EComponentMobility::Static);
if (TranslatedAssetNode)
{
SetUpFactoryNode(ActorFactoryNode, SceneNode, TranslatedAssetNode);
}
//Make sure all actor factory nodes and dependencies have the specified strategy
Private::UpdateReimportStrategyFlags(*BaseNodeContainer, *ActorFactoryNode, ReimportPropertyStrategy);
if (bForceReimportDeletedActors)
{
ActorFactoryNode->SetForceNodeReimport();
}
#if WITH_EDITORONLY_DATA
// Add dependency to newly created factory node
SceneImportFactoryNode->AddFactoryDependencyUid(ActorFactoryNode->GetUniqueID());
#endif
}
UInterchangeActorFactoryNode* UInterchangeGenericLevelPipeline::CreateActorFactoryNode(const UInterchangeSceneNode* SceneNode, const UInterchangeBaseNode* TranslatedAssetNode) const
{
if (!ensure(BaseNodeContainer))
{
return nullptr;
}
if(TranslatedAssetNode)
{
if(TranslatedAssetNode->IsA<UInterchangePhysicalCameraNode>())
{
return NewObject<UInterchangePhysicalCameraFactoryNode>(BaseNodeContainer, NAME_None);
}
if (TranslatedAssetNode->IsA<UInterchangeStandardCameraNode>())
{
if (bUsePhysicalInsteadOfStandardPerspectiveCamera)
{
//in case it has perspective projection we want to use PhysicalCamera (CineCamera) instead:
if (const UInterchangeStandardCameraNode* CameraNode = Cast<UInterchangeStandardCameraNode>(TranslatedAssetNode))
{
EInterchangeCameraProjectionType ProjectionType = EInterchangeCameraProjectionType::Perspective;
if (CameraNode->GetCustomProjectionMode(ProjectionType) && ProjectionType == EInterchangeCameraProjectionType::Perspective)
{
return NewObject<UInterchangePhysicalCameraFactoryNode>(BaseNodeContainer, NAME_None);
}
}
}
return NewObject<UInterchangeStandardCameraFactoryNode>(BaseNodeContainer, NAME_None);
}
else if(TranslatedAssetNode->IsA<UInterchangeMeshNode>())
{
return NewObject<UInterchangeMeshActorFactoryNode>(BaseNodeContainer, NAME_None);
}
else if(TranslatedAssetNode->IsA<UInterchangeSpotLightNode>())
{
return NewObject<UInterchangeSpotLightFactoryNode>(BaseNodeContainer, NAME_None);
}
else if(TranslatedAssetNode->IsA<UInterchangePointLightNode>())
{
return NewObject<UInterchangePointLightFactoryNode>(BaseNodeContainer, NAME_None);
}
else if(TranslatedAssetNode->IsA<UInterchangeRectLightNode>())
{
return NewObject<UInterchangeRectLightFactoryNode>(BaseNodeContainer, NAME_None);
}
else if(TranslatedAssetNode->IsA<UInterchangeDirectionalLightNode>())
{
return NewObject<UInterchangeDirectionalLightFactoryNode>(BaseNodeContainer, NAME_None);
}
else if (TranslatedAssetNode && TranslatedAssetNode->IsA<UInterchangeDecalNode>())
{
return NewObject<UInterchangeDecalActorFactoryNode>(BaseNodeContainer, NAME_None);
}
else if (TranslatedAssetNode->IsA<UInterchangeVolumeNode>())
{
return NewObject<UInterchangeHeterogeneousVolumeActorFactoryNode>(BaseNodeContainer, NAME_None);
}
}
return NewObject<UInterchangeActorFactoryNode>(BaseNodeContainer, NAME_None);
}
void UInterchangeGenericLevelPipeline::SetUpFactoryNode(UInterchangeActorFactoryNode* ActorFactoryNode, const UInterchangeSceneNode* SceneNode, const UInterchangeBaseNode* TranslatedAssetNode) const
{
if (!ensure(BaseNodeContainer && ActorFactoryNode && SceneNode && TranslatedAssetNode))
{
return;
}
if (const UInterchangeMeshNode* MeshNode = Cast<UInterchangeMeshNode>(TranslatedAssetNode))
{
TArray<FString> TargetNodeUids;
ActorFactoryNode->GetTargetNodeUids(TargetNodeUids);
bool bSkeletal = false;
if (TargetNodeUids.Num() > 0)
{
if (const UInterchangeSkeletalMeshFactoryNode* SkeletalMeshFactoryNode = Cast<UInterchangeSkeletalMeshFactoryNode>(BaseNodeContainer->GetFactoryNode(TargetNodeUids[0])))
{
bSkeletal = true;
}
}
if (bSkeletal)
{
ActorFactoryNode->SetCustomActorClassName(ASkeletalMeshActor::StaticClass()->GetPathName());
ActorFactoryNode->SetCustomMobility(EComponentMobility::Movable);
}
else
{
ActorFactoryNode->SetCustomActorClassName(AStaticMeshActor::StaticClass()->GetPathName());
}
if (UInterchangeMeshActorFactoryNode* MeshActorFactoryNode = Cast<UInterchangeMeshActorFactoryNode>(ActorFactoryNode))
{
TMap<FString, FString> SlotMaterialDependencies;
SceneNode->GetSlotMaterialDependencies(SlotMaterialDependencies);
UE::Interchange::MeshesUtilities::ApplySlotMaterialDependencies(*MeshActorFactoryNode, SlotMaterialDependencies, *BaseNodeContainer, nullptr);
FString AnimationAssetUidToPlay;
if (SceneNode->GetCustomAnimationAssetUidToPlay(AnimationAssetUidToPlay))
{
MeshActorFactoryNode->SetCustomAnimationAssetUidToPlay(AnimationAssetUidToPlay);
}
MeshActorFactoryNode->AddFactoryDependencyUid(UInterchangeFactoryBaseNode::BuildFactoryNodeUid(MeshNode->GetUniqueID()));
if (!bSkeletal)
{
MeshActorFactoryNode->SetCustomInstancedAssetFactoryNodeUid(UInterchangeFactoryBaseNode::BuildFactoryNodeUid(MeshNode->GetUniqueID()));
}
else
{
// Directly points to the SkeletalMeshActorFactory that holds reference to SkeletalMesh Asset in the CustomReferencedObject.
MeshActorFactoryNode->SetCustomInstancedAssetFactoryNodeUid(TargetNodeUids[0]);
}
FTransform GeometricTransform;
if (SceneNode->GetCustomGeometricTransform(GeometricTransform))
{
MeshActorFactoryNode->SetCustomGeometricTransform(GeometricTransform);
}
}
}
else if (const UInterchangeBaseLightNode* BaseLightNode = Cast<UInterchangeBaseLightNode>(TranslatedAssetNode))
{
if (UInterchangeBaseLightFactoryNode* BaseLightFactoryNode = Cast<UInterchangeBaseLightFactoryNode>(ActorFactoryNode))
{
if (FLinearColor LightColor; BaseLightNode->GetCustomLightColor(LightColor))
{
BaseLightFactoryNode->SetCustomLightColor(LightColor.ToFColor(true));
}
if (float Intensity; BaseLightNode->GetCustomIntensity(Intensity))
{
BaseLightFactoryNode->SetCustomIntensity(Intensity);
}
if(bool bUseTemperature; BaseLightNode->GetCustomUseTemperature(bUseTemperature))
{
BaseLightFactoryNode->SetCustomUseTemperature(bUseTemperature);
if(float Temperature; BaseLightNode->GetCustomTemperature(Temperature))
{
BaseLightFactoryNode->SetCustomTemperature(Temperature);
}
}
using FLightUnits = std::underlying_type_t<ELightUnits>;
using FInterchangeLightUnits = std::underlying_type_t<EInterchangeLightUnits>;
using FCommonLightUnits = std::common_type_t<FLightUnits, FInterchangeLightUnits>;
static_assert(FCommonLightUnits(EInterchangeLightUnits::Unitless) == FCommonLightUnits(ELightUnits::Unitless), "EInterchangeLightUnits::Unitless differs from ELightUnits::Unitless");
static_assert(FCommonLightUnits(EInterchangeLightUnits::Lumens) == FCommonLightUnits(ELightUnits::Lumens), "EInterchangeLightUnits::Lumens differs from ELightUnits::Lumens");
static_assert(FCommonLightUnits(EInterchangeLightUnits::Candelas) == FCommonLightUnits(ELightUnits::Candelas), "EInterchangeLightUnits::Candelas differs from ELightUnits::Candelas");
static_assert(FCommonLightUnits(EInterchangeLightUnits::EV) == FCommonLightUnits(ELightUnits::EV), "EInterchangeLightUnits::EV differs from ELightUnits::EV");
if (const UInterchangeLightNode* LightNode = Cast<UInterchangeLightNode>(BaseLightNode))
{
if (UInterchangeLightFactoryNode* LightFactoryNode = Cast<UInterchangeLightFactoryNode>(BaseLightFactoryNode))
{
if (FString IESTextureUid; LightNode->GetCustomIESTexture(IESTextureUid))
{
if (BaseNodeContainer->GetNode(IESTextureUid))
{
LightFactoryNode->SetCustomIESTexture(IESTextureUid);
LightFactoryNode->AddFactoryDependencyUid(UInterchangeFactoryBaseNode::BuildFactoryNodeUid(IESTextureUid));
COPY_FROM_TRANSLATED_TO_FACTORY(LightNode, LightFactoryNode, UseIESBrightness, bool)
COPY_FROM_TRANSLATED_TO_FACTORY(LightNode, LightFactoryNode, IESBrightnessScale, float)
COPY_FROM_TRANSLATED_TO_FACTORY(LightNode, LightFactoryNode, Rotation, FRotator)
}
}
if (EInterchangeLightUnits IntensityUnits; LightNode->GetCustomIntensityUnits(IntensityUnits))
{
LightFactoryNode->SetCustomIntensityUnits(ELightUnits(IntensityUnits));
}
COPY_FROM_TRANSLATED_TO_FACTORY(LightNode, LightFactoryNode, AttenuationRadius, float)
// RectLight
if(const UInterchangeRectLightNode* RectLightNode = Cast<UInterchangeRectLightNode>(LightNode))
{
if(UInterchangeRectLightFactoryNode* RectLightFactoryNode = Cast<UInterchangeRectLightFactoryNode>(LightFactoryNode))
{
COPY_FROM_TRANSLATED_TO_FACTORY(RectLightNode, RectLightFactoryNode, SourceWidth, float)
COPY_FROM_TRANSLATED_TO_FACTORY(RectLightNode, RectLightFactoryNode, SourceHeight, float)
}
}
// Point Light
if (const UInterchangePointLightNode* PointLightNode = Cast<UInterchangePointLightNode>(LightNode))
{
if (UInterchangePointLightFactoryNode* PointLightFactoryNode = Cast<UInterchangePointLightFactoryNode>(LightFactoryNode))
{
if (bool bUseInverseSquaredFalloff; PointLightNode->GetCustomUseInverseSquaredFalloff(bUseInverseSquaredFalloff))
{
PointLightFactoryNode->SetCustomUseInverseSquaredFalloff(bUseInverseSquaredFalloff);
COPY_FROM_TRANSLATED_TO_FACTORY(PointLightNode, PointLightFactoryNode, LightFalloffExponent, float)
}
// Spot Light
if (const UInterchangeSpotLightNode* SpotLightNode = Cast<UInterchangeSpotLightNode>(PointLightNode))
{
if (UInterchangeSpotLightFactoryNode* SpotLightFactoryNode = Cast<UInterchangeSpotLightFactoryNode>(PointLightFactoryNode))
{
COPY_FROM_TRANSLATED_TO_FACTORY(SpotLightNode, SpotLightFactoryNode, InnerConeAngle, float)
COPY_FROM_TRANSLATED_TO_FACTORY(SpotLightNode, SpotLightFactoryNode, OuterConeAngle, float)
}
}
}
}
}
}
}
//Test for spot before point since a spot light is a point light
if (BaseLightNode->IsA<UInterchangeSpotLightNode>())
{
ActorFactoryNode->SetCustomActorClassName(ASpotLight::StaticClass()->GetPathName());
}
else if (BaseLightNode->IsA<UInterchangePointLightNode>())
{
ActorFactoryNode->SetCustomActorClassName(APointLight::StaticClass()->GetPathName());
}
else if (BaseLightNode->IsA<UInterchangeRectLightNode>())
{
ActorFactoryNode->SetCustomActorClassName(ARectLight::StaticClass()->GetPathName());
}
else if (BaseLightNode->IsA<UInterchangeDirectionalLightNode>())
{
ActorFactoryNode->SetCustomActorClassName(ADirectionalLight::StaticClass()->GetPathName());
}
else
{
ActorFactoryNode->SetCustomActorClassName(APointLight::StaticClass()->GetPathName());
}
}
else if (const UInterchangePhysicalCameraNode* PhysicalCameraNode = Cast<UInterchangePhysicalCameraNode>(TranslatedAssetNode))
{
ActorFactoryNode->SetCustomActorClassName(ACineCameraActor::StaticClass()->GetPathName());
ActorFactoryNode->SetCustomMobility(EComponentMobility::Movable);
if (UInterchangePhysicalCameraFactoryNode* PhysicalCameraFactoryNode = Cast<UInterchangePhysicalCameraFactoryNode>(ActorFactoryNode))
{
float FocalLength;
if (PhysicalCameraNode->GetCustomFocalLength(FocalLength))
{
PhysicalCameraFactoryNode->SetCustomFocalLength(FocalLength);
}
float SensorHeight;
if (PhysicalCameraNode->GetCustomSensorHeight(SensorHeight))
{
PhysicalCameraFactoryNode->SetCustomSensorHeight(SensorHeight);
}
float SensorWidth;
if (PhysicalCameraNode->GetCustomSensorWidth(SensorWidth))
{
PhysicalCameraFactoryNode->SetCustomSensorWidth(SensorWidth);
}
bool bEnableDepthOfField;
if (PhysicalCameraNode->GetCustomEnableDepthOfField(bEnableDepthOfField))
{
PhysicalCameraFactoryNode->SetCustomFocusMethod(bEnableDepthOfField ? ECameraFocusMethod::Manual : ECameraFocusMethod::DoNotOverride);
}
}
}
else if (const UInterchangeStandardCameraNode* CameraNode = Cast<UInterchangeStandardCameraNode>(TranslatedAssetNode))
{
EInterchangeCameraProjectionType ProjectionType = EInterchangeCameraProjectionType::Perspective;
if (CameraNode->GetCustomProjectionMode(ProjectionType) && bUsePhysicalInsteadOfStandardPerspectiveCamera && ProjectionType == EInterchangeCameraProjectionType::Perspective)
{
float AspectRatio = 1.0f;
CameraNode->GetCustomAspectRatio(AspectRatio);
const float SensorWidth = 36.f; // mm
float SensorHeight = SensorWidth / AspectRatio;
float FieldOfView = 90;
CameraNode->GetCustomFieldOfView(FieldOfView); //Degrees
float FocalLength = (SensorHeight) / (2.0 * tan(FMath::DegreesToRadians(FieldOfView) / 2.0));
ActorFactoryNode->SetCustomActorClassName(ACineCameraActor::StaticClass()->GetPathName());
ActorFactoryNode->SetCustomMobility(EComponentMobility::Movable);
if (UInterchangePhysicalCameraFactoryNode* PhysicalCameraFactoryNode = Cast<UInterchangePhysicalCameraFactoryNode>(ActorFactoryNode))
{
PhysicalCameraFactoryNode->SetCustomFocalLength(FocalLength);
PhysicalCameraFactoryNode->SetCustomSensorHeight(SensorHeight);
PhysicalCameraFactoryNode->SetCustomSensorWidth(SensorWidth);
PhysicalCameraFactoryNode->SetCustomFocusMethod(ECameraFocusMethod::DoNotOverride);
}
}
else
{
ActorFactoryNode->SetCustomActorClassName(ACameraActor::StaticClass()->GetPathName());
ActorFactoryNode->SetCustomMobility(EComponentMobility::Movable);
if (UInterchangeStandardCameraFactoryNode* CameraFactoryNode = Cast<UInterchangeStandardCameraFactoryNode>(ActorFactoryNode))
{
if (CameraNode->GetCustomProjectionMode(ProjectionType))
{
CameraFactoryNode->SetCustomProjectionMode((ECameraProjectionMode::Type)ProjectionType);
}
float OrthoWidth;
if (CameraNode->GetCustomWidth(OrthoWidth))
{
CameraFactoryNode->SetCustomWidth(OrthoWidth);
}
float OrthoNearClipPlane;
if (CameraNode->GetCustomNearClipPlane(OrthoNearClipPlane))
{
CameraFactoryNode->SetCustomNearClipPlane(OrthoNearClipPlane);
}
float OrthoFarClipPlane;
if (CameraNode->GetCustomFarClipPlane(OrthoFarClipPlane))
{
CameraFactoryNode->SetCustomFarClipPlane(OrthoFarClipPlane);
}
float AspectRatio;
if (CameraNode->GetCustomAspectRatio(AspectRatio))
{
CameraFactoryNode->SetCustomAspectRatio(AspectRatio);
}
float FieldOfView;
if (CameraNode->GetCustomFieldOfView(FieldOfView))
{
CameraFactoryNode->SetCustomFieldOfView(FieldOfView);
}
}
}
}
else if (const UInterchangeDecalNode* DecalNode = Cast<UInterchangeDecalNode>(TranslatedAssetNode))
{
UInterchangeDecalActorFactoryNode* DecalActorFactory = Cast<UInterchangeDecalActorFactoryNode>(ActorFactoryNode);
ensure(DecalActorFactory);
if (FVector DecalSize; DecalNode->GetCustomDecalSize(DecalSize))
{
DecalActorFactory->SetCustomDecalSize(DecalSize);
}
if (int32 SortOrder; DecalNode->GetCustomSortOrder(SortOrder))
{
DecalActorFactory->SetCustomSortOrder(SortOrder);
}
bool bHasMaterialPathName = false;
FString DecalMaterialPathName;
if (DecalNode->GetCustomDecalMaterialPathName(DecalMaterialPathName))
{
DecalActorFactory->SetCustomDecalMaterialPathName(DecalMaterialPathName);
bHasMaterialPathName = true;
}
// If the path is not a valid object path then it is an Interchange Node UID (Decal Material Node to be specific).
if (bHasMaterialPathName && !FPackageName::IsValidObjectPath(DecalMaterialPathName))
{
const FString MaterialFactoryUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(DecalMaterialPathName);
DecalActorFactory->SetCustomDecalMaterialPathName(MaterialFactoryUid);
DecalActorFactory->AddFactoryDependencyUid(MaterialFactoryUid);
}
}
else if (const UInterchangeVolumeNode* VolumeNode = Cast<UInterchangeVolumeNode>(TranslatedAssetNode))
{
UInterchangeHeterogeneousVolumeActorFactoryNode* FactoryNode = Cast<UInterchangeHeterogeneousVolumeActorFactoryNode>(ActorFactoryNode);
ensure(FactoryNode);
// Setup a material binding if we have one (we always should, but let's not ensure here as the user
// may have custom pipelines, etc.)
{
TMap<FString, FString> SlotNameToMaterialPath;
SceneNode->GetSlotMaterialDependencies(SlotNameToMaterialPath);
FString MaterialFactoryNodeUid;
if (FString* FoundUid = SlotNameToMaterialPath.Find(UE::Interchange::Volume::VolumetricMaterial))
{
MaterialFactoryNodeUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(*FoundUid);
if (!MaterialFactoryNodeUid.IsEmpty())
{
FactoryNode->SetCustomVolumetricMaterialUid(MaterialFactoryNodeUid);
}
}
}
}
}
void UInterchangeGenericLevelPipeline::ExecuteSceneVariantSetNodePreImport(const UInterchangeSceneVariantSetsNode& SceneVariantSetNode)
{
if (!ensure(BaseNodeContainer))
{
return;
}
// We may eventually want to optionally import variants
static bool bEnableSceneVariantSet = true;
const FString FactoryNodeUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(SceneVariantSetNode.GetUniqueID());
UInterchangeSceneVariantSetsFactoryNode* FactoryNode = NewObject<UInterchangeSceneVariantSetsFactoryNode>(BaseNodeContainer, NAME_None);
BaseNodeContainer->SetupNode(FactoryNode, FactoryNodeUid, SceneVariantSetNode.GetDisplayLabel(), EInterchangeNodeContainerType::FactoryData);
FactoryNode->SetEnabled(bEnableSceneVariantSet);
UInterchangeSourceNode* SourceNode = UInterchangeSourceNode::FindOrCreateUniqueInstance(BaseNodeContainer);
UE::Interchange::PipelineHelper::FillSubPathFromSourceNode(FactoryNode, SourceNode);
// The reimport strategy should be the one from the InterchangeAssetsPipeline not the Level
if (EReimportStrategyFlags ReimportStrategy; SourceNode->GetCustomReimportStrategyFlags(reinterpret_cast<uint8&>(ReimportStrategy)))
{
FactoryNode->SetReimportStrategyFlags(ReimportStrategy);
}
TArray<FString> VariantSetUids;
SceneVariantSetNode.GetCustomVariantSetUids(VariantSetUids);
for (const FString& VariantSetUid : VariantSetUids)
{
FactoryNode->AddCustomVariantSetUid(VariantSetUid);
// Update factory's dependencies
if (const UInterchangeVariantSetNode* TrackNode = Cast<UInterchangeVariantSetNode>(BaseNodeContainer->GetNode(VariantSetUid)))
{
TArray<FString> DependencyNodeUids;
TrackNode->GetCustomDependencyUids(DependencyNodeUids);
for (const FString& DependencyNodeUid : DependencyNodeUids)
{
const FString DependencyFactoryNodeUid = UInterchangeFactoryBaseNode::BuildFactoryNodeUid(DependencyNodeUid);
FactoryNode->AddFactoryDependencyUid(DependencyFactoryNodeUid);
if (UInterchangeFactoryBaseNode* DependencyFactoryNode = BaseNodeContainer->GetFactoryNode(DependencyFactoryNodeUid))
{
if (bEnableSceneVariantSet && !DependencyFactoryNode->IsEnabled())
{
DependencyFactoryNode->SetEnabled(true);
}
}
}
}
}
UInterchangeUserDefinedAttributesAPI::DuplicateAllUserDefinedAttribute(&SceneVariantSetNode, FactoryNode, false);
FactoryNode->AddTargetNodeUid(SceneVariantSetNode.GetUniqueID());
SceneVariantSetNode.AddTargetNodeUid(FactoryNode->GetUniqueID());
}
void UInterchangeGenericLevelPipeline::ExecutePostImportPipeline(const UInterchangeBaseNodeContainer* InBaseNodeContainer, const FString& NodeKey, UObject* CreatedAsset, bool bIsAReimport)
{
Super::ExecutePostImportPipeline(InBaseNodeContainer, NodeKey, CreatedAsset, bIsAReimport);
#if WITH_EDITORONLY_DATA
using namespace UE::Interchange;
//We do not use the provided base container since ExecutePreImportPipeline cache it
//We just make sure the same one is pass in parameter
if (!InBaseNodeContainer || !ensure(BaseNodeContainer == InBaseNodeContainer) || !CreatedAsset || !ensure(IsInGameThread()))
{
return;
}
if (UInterchangeSceneImportAsset* SceneImportAsset = Cast<UInterchangeSceneImportAsset>(CreatedAsset))
{
const FSoftObjectPath SceneImportAssetPath(SceneImportAsset);
auto AddAssetUserDataToObjects = [&]()
{
if (SceneHierarchyType == EInterchangeSceneHierarchyType::CreateLevelActors)
{
InBaseNodeContainer->IterateNodesOfType<UInterchangeActorFactoryNode>(
[&SceneImportAssetPath, &SceneImportAsset](const FString& NodeUid, UInterchangeActorFactoryNode* FactoryNode)
{
FSoftObjectPath ObjectPath;
if (FactoryNode->GetCustomReferenceObject(ObjectPath))
{
if (AActor* ReferencedObject = Cast<AActor>(ObjectPath.TryLoad()))
{
if (UWorld* World = ReferencedObject->GetWorld())
{
UE::Interchange::ActorHelper::AddInterchangeAssetUserDataToActor(ReferencedObject, SceneImportAsset, FactoryNode);
}
}
}
}
);
}
else
{
InBaseNodeContainer->IterateNodesOfType<UInterchangeLevelFactoryNode>(
[&SceneImportAssetPath, &SceneImportAsset](const FString& NodeUid, UInterchangeLevelFactoryNode* FactoryNode)
{
bool bWasLevelCreatedForImport = false;
if (FactoryNode->GetCustomShouldCreateLevel(bWasLevelCreatedForImport) && bWasLevelCreatedForImport)
{
FSoftObjectPath ObjectPath;
if (FactoryNode->GetCustomReferenceObject(ObjectPath))
{
if (UWorld* World = Cast<UWorld>(ObjectPath.TryLoad()))
{
UE::Interchange::ActorHelper::AddInterchangeLevelAssetUserDataToWorld(World, SceneImportAsset);
}
}
}
}
);
}
};
if (!bIsReimportContext || !(bDeleteMissingActors || bDeleteMissingAssets))
{
SceneImportAsset->UpdateSceneObjects();
AddAssetUserDataToObjects();
return;
}
const UInterchangeSceneImportAssetFactoryNode* FactoryNode = Cast<UInterchangeSceneImportAssetFactoryNode>(BaseNodeContainer->GetFactoryNode(NodeKey));
if (!ensure(FactoryNode))
{
SceneImportAsset->UpdateSceneObjects();
AddAssetUserDataToObjects();
return;
}
// Cache list of objects previously imported in case of a re-import
TArray<FSoftObjectPath> PrevSoftObjectPaths;
SceneImportAsset->GetSceneSoftObjectPaths(PrevSoftObjectPaths);
SceneImportAsset->UpdateSceneObjects();
AddAssetUserDataToObjects();
const bool bIsCreateLevelActor = (SceneHierarchyType == EInterchangeSceneHierarchyType::CreateLevelActors);
// Nothing to take care of
if (PrevSoftObjectPaths.IsEmpty())
{
return;
}
TArray<FSoftObjectPath> NewSoftObjectPaths;
SceneImportAsset->GetSceneSoftObjectPaths(NewSoftObjectPaths);
TSet<FSoftObjectPath> SoftObjectPathSet(NewSoftObjectPaths);
TArray<AActor*> ActorsToDelete;
TArray<UObject*> AssetsToForceDelete;
ActorsToDelete.Reserve(PrevSoftObjectPaths.Num());
AssetsToForceDelete.Reserve(PrevSoftObjectPaths.Num());
for (const FSoftObjectPath& ObjectPath : PrevSoftObjectPaths)
{
if (!SoftObjectPathSet.Contains(ObjectPath))
{
if (UObject* Object = ObjectPath.TryLoad())
{
if (IsValid(Object))
{
if (Object->GetClass()->IsChildOf<AActor>())
{
//Do not delete ALevelInstance or APackedLevelActor
if (bIsCreateLevelActor || !(Object->GetClass()->IsChildOf<ALevelInstance>() || Object->GetClass()->IsChildOf<APackedLevelActor>()))
{
ActorsToDelete.Add(Cast<AActor>(Object));
}
}
else if(!Object->IsA<UWorld>()) //Avoid deleting UWorld asset
{
AssetsToForceDelete.Add(Object);
}
}
}
}
}
if (bDeleteMissingActors)
{
Private::DeleteActors(ActorsToDelete);
}
if (bDeleteMissingAssets && !AssetsToForceDelete.IsEmpty())
{
Private::DeleteAssets(AssetsToForceDelete);
}
// Update newly imported objects with a soft reference to the UInterchangeSceneImportAsset
for (const FSoftObjectPath& ObjectPath : NewSoftObjectPaths)
{
UObject* Object = ObjectPath.TryLoad();
if (IsValid(Object) && Object != CreatedAsset)
{
if (UInterchangeAssetImportData* AssetImportData = UInterchangeAssetImportData::GetFromObject(Object))
{
AssetImportData->SceneImportAsset = CreatedAsset;
}
}
}
}
if (ALevelInstance* LevelInstanceActor = Cast<ALevelInstance>(CreatedAsset))
{
const UInterchangeLevelInstanceActorFactoryNode* LevelInstanceFactoryNode = Cast<UInterchangeLevelInstanceActorFactoryNode>(InBaseNodeContainer->GetFactoryNode(NodeKey));
FString LevelFactoryNodeUid;
if (LevelInstanceFactoryNode->GetCustomLevelReference(LevelFactoryNodeUid))
{
if (const UInterchangeFactoryBaseNode* ReferenceLevelFactoryNode = InBaseNodeContainer->GetFactoryNode(LevelFactoryNodeUid))
{
FSoftObjectPath ReferenceLevelPath;
if (ReferenceLevelFactoryNode->GetCustomReferenceObject(ReferenceLevelPath))
{
if (UWorld* ReferenceWorld = Cast<UWorld>(ReferenceLevelPath.TryLoad()))
{
PostPipelineImportData.AddLevelInstanceActor(LevelInstanceActor, ReferenceWorld);
}
}
}
}
}
#endif
}
void UInterchangeGenericLevelPipeline::ExecutePostBroadcastPipeline(const UInterchangeBaseNodeContainer* InBaseNodeContainer, const FString& NodeKey, UObject* CreatedAsset, bool bIsAReimport)
{
Super::ExecutePostBroadcastPipeline(InBaseNodeContainer, NodeKey, CreatedAsset, bIsAReimport);
if (!CreatedAsset || !ensure(IsInGameThread()))
{
return;
}
#if WITH_EDITORONLY_DATA
if (ALevelInstance* LevelInstanceActor = Cast<ALevelInstance>(CreatedAsset))
{
if (UWorld* WorldAsset = LevelInstanceActor->GetWorldAsset().Get())
{
//We cannot call EnterEdit on a dirty world.
if (WorldAsset->GetPackage()->IsDirty())
{
if (UInterchangeEditorUtilitiesBase* EditorUtilities = UInterchangeManager::GetInterchangeManager().GetEditorUtilities())
{
if (!EditorUtilities->SaveAsset(WorldAsset))
{
UE_LOG(LogInterchangePipeline, Warning, TEXT("UInterchangeGenericLevelPipeline: Cannot save the level instance actor (%s) referenced world (%s)"), *LevelInstanceActor->GetName(), *WorldAsset->GetName());
}
}
}
//Update all levelinstance actor that are referencing this reference world
for (FThreadSafeObjectIterator It; It; ++It)
{
if (ALevelInstance* CurrentLevelInstanceActor = Cast<ALevelInstance>(*It))
{
if (CurrentLevelInstanceActor->GetWorldAsset().Get() == WorldAsset)
{
const UWorld* CurrentLevelInstanceActorWorld = CurrentLevelInstanceActor->GetWorld();
if (!CurrentLevelInstanceActorWorld || CurrentLevelInstanceActorWorld->IsCleanedUp())
{
continue;
}
CurrentLevelInstanceActor->UpdateLevelInstanceFromWorldAsset();
if (!CurrentLevelInstanceActor->IsA<APackedLevelActor>())
{
CurrentLevelInstanceActor->LoadLevelInstance();
}
}
}
}
//Reference world must be cleanup since they are not the main world. It was loaded by the Update of the level instance
//This remove all the world managers and prevent GC issue when unloading the main world referencing this world.
WorldAsset->ClearFlags(RF_Standalone);
WorldAsset->ClearInternalFlags(EInternalObjectFlags::Async);
if (WorldAsset->bIsWorldInitialized)
{
WorldAsset->CleanupWorld();
}
}
}
#endif
}
#if WITH_EDITORONLY_DATA
void UInterchangeGenericLevelPipeline::FPostPipelineImportData::AddLevelInstanceActor(ALevelInstance* LevelInstanceActor, UWorld* ReferencedWorld)
{
bool bReferencedWorldAlreadyProcess = Worlds.Contains(ReferencedWorld);
UWorld* ParentWorld = LevelInstanceActor->GetWorld();
if (!bReferencedWorldAlreadyProcess)
{
if (UInterchangeEditorUtilitiesBase* EditorUtilities = UInterchangeManager::GetInterchangeManager().GetEditorUtilities())
{
if (!EditorUtilities->SaveAsset(ReferencedWorld))
{
UE_LOG(LogInterchangePipeline, Warning, TEXT("UInterchangeGenericScenesPipeline: Cannot save the level instance actor (%s) referenced world (%s)"), *LevelInstanceActor->GetName(), *ReferencedWorld->GetName());
}
}
// Make sure newly created level asset gets scanned
ULevel::ScanLevelAssets(ReferencedWorld->GetPackage()->GetName());
}
ParentWorld->PreEditChange(nullptr);
LevelInstanceActor->SetWorldAsset(ReferencedWorld);
if (APackedLevelActor* PackedLevelActor = Cast<APackedLevelActor>(LevelInstanceActor))
{
constexpr bool bCheckoutAndSaveFalse = false;
constexpr bool bPromptForSaveFalse = false;
//Get the blueprint
UBlueprint* Blueprint = PackedLevelActor->GetRootBlueprint();
ULevel::ScanLevelAssets(Blueprint->GetPackage()->GetName());
//Update the blueprint with the reference level content
FPackedLevelActorBuilder::CreateDefaultBuilder()->CreateOrUpdateBlueprint(ReferencedWorld, Blueprint, bCheckoutAndSaveFalse, bPromptForSaveFalse);
}
else
{
LevelInstanceActor->UpdateLevelInstanceFromWorldAsset();
LevelInstanceActor->LoadLevelInstance();
}
if (!bReferencedWorldAlreadyProcess)
{
//Reference world must be cleanup since they are not the main world.
//This remove all the world managers and prevent GC issue when unloading the main world referencing this world.
if (ReferencedWorld->bIsWorldInitialized)
{
ReferencedWorld->CleanupWorld();
}
Worlds.Add(ReferencedWorld);
}
ParentWorld->PostEditChange();
}
#endif //WITH_EDITORONLY_DATA
void UInterchangeGenericLevelPipeline::CacheActiveJointUids()
{
Cached_ActiveJointUids.Reset();
BaseNodeContainer->IterateNodesOfType<UInterchangeSkeletonFactoryNode>([this](const FString& NodeUid, UInterchangeSkeletonFactoryNode* Node)
{
FString RootNodeUid;
if (Node->GetCustomRootJointUid(RootNodeUid))
{
Cached_ActiveJointUids.Add(RootNodeUid);
BaseNodeContainer->IterateNodeChildren(RootNodeUid, [this](const UInterchangeBaseNode* Node)
{
if (const UInterchangeSceneNode* SceneNode = Cast<UInterchangeSceneNode>(Node))
{
TArray<FString> SpecializeTypes;
SceneNode->GetSpecializedTypes(SpecializeTypes);
if (SpecializeTypes.Contains(UE::Interchange::FSceneNodeStaticData::GetJointSpecializeTypeString()))
{
Cached_ActiveJointUids.Add(Node->GetUniqueID());
}
}
});
}
});
}