From 300c680c8787474519497ab9140ce9c0fc4d08da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20By=C5=A1ka?= Date: Sat, 8 Feb 2020 11:30:26 +0100 Subject: [PATCH] Adds a basic support for local actor references in Prefabs. --- .../Private/Asset/PrefabricatorAsset.cpp | 22 +++- .../Private/Prefab/PrefabTools.cpp | 114 ++++++++++++------ .../Utils/PrefabricatorFunctionLibrary.cpp | 3 +- .../Public/Asset/PrefabricatorAsset.h | 7 +- .../Public/Prefab/PrefabTools.h | 3 +- 5 files changed, 107 insertions(+), 42 deletions(-) diff --git a/Source/PrefabricatorRuntime/Private/Asset/PrefabricatorAsset.cpp b/Source/PrefabricatorRuntime/Private/Asset/PrefabricatorAsset.cpp index e3b99a27..58b25005 100644 --- a/Source/PrefabricatorRuntime/Private/Asset/PrefabricatorAsset.cpp +++ b/Source/PrefabricatorRuntime/Private/Asset/PrefabricatorAsset.cpp @@ -11,6 +11,7 @@ #include "GameFramework/Actor.h" #include "Internationalization/Regex.h" #include "Misc/PackageName.h" +#include "PrefabricatorAssetUserData.h" DEFINE_LOG_CATEGORY_STATIC(LogPrefabricatorAsset, Log, All); @@ -166,6 +167,17 @@ void UPrefabricatorProperty::SaveReferencedAssetValues() Mapping.AssetReference = SoftPath; //if (Mapping.AssetReference.IsValid()) { + //JB: If possible we store the AssetUserData.ItemID + AActor* Actor = Cast(SoftPath.ResolveObject()); + if (Actor && Actor->GetRootComponent()) + { + //JB: This could be also passed to the function, but I tried to change the API as little as possible + UPrefabricatorAssetUserData* AssetUserData = Actor->GetRootComponent()->GetAssetUserData(); + if (AssetUserData) + { + Mapping.AssetItemID = AssetUserData->ItemID; + } + } //FString ObjectPathString; //FPackageName::ParseExportTextPath(AssetPath, &Mapping.AssetClassName, &ObjectPathString); Mapping.AssetClassName = ClassName; @@ -177,7 +189,7 @@ void UPrefabricatorProperty::SaveReferencedAssetValues() } } -void UPrefabricatorProperty::LoadReferencedAssetValues() +void UPrefabricatorProperty::LoadReferencedAssetValues(const TMap& InChildActors) { SCOPE_CYCLE_COUNTER(STAT_LoadReferencedAssetValues); bool bModified = false; @@ -191,6 +203,14 @@ void UPrefabricatorProperty::LoadReferencedAssetValues() { //SCOPE_CYCLE_COUNTER(STAT_LoadReferencedAssetValues_GetAssetPathName); ReferencedPath = Mapping.AssetReference.GetAssetPathName(); + + // JB: We try to find the corresponding actor inside the prefab using the stored ItemID. + if (InChildActors.Contains(Mapping.AssetItemID)) + { + AActor* ReferencedActor = *InChildActors.Find(Mapping.AssetItemID); + ReferencedPath = *ReferencedActor->GetPathName(); + } + if (ReferencedPath.ToString().IsEmpty()) { continue; } diff --git a/Source/PrefabricatorRuntime/Private/Prefab/PrefabTools.cpp b/Source/PrefabricatorRuntime/Private/Prefab/PrefabTools.cpp index e1a0891f..247c1f6b 100644 --- a/Source/PrefabricatorRuntime/Private/Prefab/PrefabTools.cpp +++ b/Source/PrefabricatorRuntime/Private/Prefab/PrefabTools.cpp @@ -229,6 +229,9 @@ void FPrefabTools::SaveStateToPrefabAsset(APrefabActor* PrefabActor) } } + // JB: Map storing all child actors based on their index in the PrefabAsset->ActorData array. + TMap ChildrenToStore; + // JB: Prepares the actor data for all child actors and makes sure that all have ItemID. for (AActor* ChildActor : Children) { if (ChildActor && ChildActor->GetRootComponent()) { UPrefabricatorAssetUserData* ChildUserData = ChildActor->GetRootComponent()->GetAssetUserData(); @@ -244,9 +247,17 @@ void FPrefabTools::SaveStateToPrefabAsset(APrefabActor* PrefabActor) int32 NewItemIndex = PrefabAsset->ActorData.AddDefaulted(); FPrefabricatorActorData& ActorData = PrefabAsset->ActorData[NewItemIndex]; ActorData.PrefabItemID = ItemID; - SaveActorState(ChildActor, PrefabActor, ActorData); + ChildrenToStore.Add(NewItemIndex, ChildActor); } } + + // JB: Stores the children actors state. + // JB: We need to do it after all the actor data are prepared in order to correctly reference the actors. + for (auto& ChildToStoreElement : ChildrenToStore) + { + SaveActorState(ChildToStoreElement.Value, PrefabActor, PrefabAsset->ActorData[ChildToStoreElement.Key]); + } + PrefabAsset->Version = (uint32)EPrefabricatorAssetVersion::LatestVersion; PrefabActor->PrefabComponent->UpdateBounds(); @@ -301,7 +312,8 @@ namespace { return false; } - void DeserializeFields(UObject* InObjToDeserialize, const TArray& InProperties) { + // JB: The InChildActors map contains all actors spawned by the prefab stored by their ItemID. + void DeserializeFields(UObject* InObjToDeserialize, const TArray& InProperties, const TMap& InChildActors) { if (!InObjToDeserialize) return; TMap PropertiesByName; @@ -326,7 +338,7 @@ namespace { if (PrefabProperty) { { SCOPE_CYCLE_COUNTER(STAT_DeserializeFields_Iterate_LoadValue); - PrefabProperty->LoadReferencedAssetValues(); + PrefabProperty->LoadReferencedAssetValues(InChildActors); } { SCOPE_CYCLE_COUNTER(STAT_DeserializeFields_Iterate_SetValue); @@ -372,6 +384,7 @@ namespace { for (const UProperty* Property : PropertiesToSerialize) { if (!Property) continue; + // JB: This was already checked few lines above and cannot ever happen - you may consider removing it. if (FPrefabTools::ShouldIgnorePropertySerialization(Property->GetFName())) { continue; } @@ -476,6 +489,7 @@ void FPrefabTools::SaveActorState(AActor* InActor, APrefabActor* PrefabActor, FP int32 ComponentDataIdx = OutActorData.Components.AddDefaulted(); FPrefabricatorComponentData& ComponentData = OutActorData.Components[ComponentDataIdx]; ComponentData.ComponentName = Component->GetPathName(InActor); + // JB: As far as I can tell ComponentData.RelativeTransform is never used - you may consider removing it to save some disk space. if (USceneComponent* SceneComponent = Cast(Component)) { ComponentData.RelativeTransform = SceneComponent->GetComponentTransform(); } @@ -488,7 +502,7 @@ void FPrefabTools::SaveActorState(AActor* InActor, APrefabActor* PrefabActor, FP DumpSerializedData(OutActorData); } -void FPrefabTools::LoadActorState(AActor* InActor, const FPrefabricatorActorData& InActorData, const FPrefabLoadSettings& InSettings) +void FPrefabTools::LoadActorState(AActor* InActor, const FPrefabricatorActorData& InActorData, const FPrefabLoadSettings& InSettings, const TMap& InChildActors) { if (!InActor) { return; @@ -502,7 +516,7 @@ void FPrefabTools::LoadActorState(AActor* InActor, const FPrefabricatorActorData { //SCOPE_CYCLE_COUNTER(STAT_LoadStateFromPrefabAsset_DeserializeFieldsActor); - DeserializeFields(InActor, InActorData.Properties); + DeserializeFields(InActor, InActorData.Properties, InChildActors); } TMap ComponentsByName; @@ -526,7 +540,7 @@ void FPrefabTools::LoadActorState(AActor* InActor, const FPrefabricatorActorData { //SCOPE_CYCLE_COUNTER(STAT_LoadStateFromPrefabAsset_DeserializeFieldsComponents); - DeserializeFields(Component, ComponentData.Properties); + DeserializeFields(Component, ComponentData.Properties, InChildActors); } { @@ -637,6 +651,14 @@ void FPrefabTools::LoadStateFromPrefabAsset(APrefabActor* PrefabActor, const FPr } } + // JB: A map storing all spawned actors based on their index in the PrefabAsset->ActorData array. + TMap SpawnedActors; + // JB: A map storing all actor templates. + TMap Templates; + // JB: A map storing all spawned actors based on their ItemID. + TMap ChildActors; + // JB: The index used to access the correct ActorData. + int32 ActorIndex = 0; for (FPrefabricatorActorData& ActorItemData : PrefabAsset->ActorData) { // Handle backward compatibility { @@ -665,10 +687,11 @@ void FPrefabTools::LoadStateFromPrefabAsset(APrefabActor* PrefabActor, const FPr } } + // JB: We first spawn the actors as placeholders only. + AActor* Template = nullptr; if (!ChildActor) { TSharedPtr Service = FPrefabricatorService::Get(); if (Service.IsValid()) { - AActor* Template = nullptr; if (InState.IsValid()) { TWeakObjectPtr* SearchResult = InState->PrefabItemTemplates.Find(ActorItemData.PrefabItemID); if (SearchResult) { @@ -680,18 +703,6 @@ void FPrefabTools::LoadStateFromPrefabAsset(APrefabActor* PrefabActor, const FPr } ChildActor = Service->SpawnActor(ActorClass, FTransform::Identity, PrefabActor->GetLevel(), Template); - if (!Template) { - LoadActorState(ChildActor, ActorItemData, InSettings); - if (InState.IsValid()) { - InState->PrefabItemTemplates.Add(ActorItemData.PrefabItemID, ChildActor); - InState->_Stat_SlowSpawns++; - } - } - else { - if (InState.IsValid()) { - InState->_Stat_FastSpawns++; - } - } } } else { @@ -701,29 +712,56 @@ void FPrefabTools::LoadStateFromPrefabAsset(APrefabActor* PrefabActor, const FPr } if (ChildActor) { - ParentActors(PrefabActor, ChildActor); - AssignAssetUserData(ChildActor, ActorItemData.PrefabItemID, PrefabActor); + // JB: Fills the data maps with the proper references. + SpawnedActors.Add(ActorIndex, ChildActor); + Templates.Add(ActorIndex, Template); + ChildActors.Add(ActorItemData.PrefabItemID, ChildActor); + } + ActorIndex++; + } - // Set the transform - FTransform WorldTransform = ActorItemData.RelativeTransform * PrefabActor->GetTransform(); + // JB: Now we load the saved actor data into the actors. + for (auto& SpawnedActorElement : SpawnedActors) + { + FPrefabricatorActorData& ActorItemData = PrefabAsset->ActorData[SpawnedActorElement.Key]; + AActor* ChildActor = SpawnedActorElement.Value; + AActor* Template = Templates[SpawnedActorElement.Key]; + + if (!Template) { + LoadActorState(ChildActor, ActorItemData, InSettings, ChildActors); + if (InState.IsValid()) { + InState->PrefabItemTemplates.Add(ActorItemData.PrefabItemID, ChildActor); + InState->_Stat_SlowSpawns++; + } + } + else { + if (InState.IsValid()) { + InState->_Stat_FastSpawns++; + } + } + ParentActors(PrefabActor, ChildActor); + AssignAssetUserData(ChildActor, ActorItemData.PrefabItemID, PrefabActor); + + // Set the transform + FTransform WorldTransform = ActorItemData.RelativeTransform * PrefabActor->GetTransform(); + if (ChildActor->GetRootComponent()) { + EComponentMobility::Type OldChildMobility = EComponentMobility::Movable; if (ChildActor->GetRootComponent()) { - EComponentMobility::Type OldChildMobility = EComponentMobility::Movable; - if (ChildActor->GetRootComponent()) { - OldChildMobility = ChildActor->GetRootComponent()->Mobility; - } - ChildActor->GetRootComponent()->SetMobility(EComponentMobility::Movable); - ChildActor->SetActorTransform(WorldTransform); - ChildActor->GetRootComponent()->SetMobility(OldChildMobility); + OldChildMobility = ChildActor->GetRootComponent()->Mobility; } + ChildActor->GetRootComponent()->SetMobility(EComponentMobility::Movable); + ChildActor->SetActorTransform(WorldTransform); + ChildActor->GetRootComponent()->SetMobility(OldChildMobility); + } - if (APrefabActor* ChildPrefab = Cast(ChildActor)) { - if (InSettings.bRandomizeNestedSeed && InSettings.Random) { - // This is a nested child prefab. Randomize the seed of the child prefab - ChildPrefab->Seed = FPrefabTools::GetRandomSeed(*InSettings.Random); - } - if (InSettings.bSynchronousBuild) { - LoadStateFromPrefabAsset(ChildPrefab, InSettings, InState); - } + // JB: TODO: spawn all nested prefab actors already above in order to allow referencing them. + if (APrefabActor* ChildPrefab = Cast(ChildActor)) { + if (InSettings.bRandomizeNestedSeed && InSettings.Random) { + // This is a nested child prefab. Randomize the seed of the child prefab + ChildPrefab->Seed = FPrefabTools::GetRandomSeed(*InSettings.Random); + } + if (InSettings.bSynchronousBuild) { + LoadStateFromPrefabAsset(ChildPrefab, InSettings, InState); } } } diff --git a/Source/PrefabricatorRuntime/Private/Utils/PrefabricatorFunctionLibrary.cpp b/Source/PrefabricatorRuntime/Private/Utils/PrefabricatorFunctionLibrary.cpp index a05f9442..a0b9114c 100644 --- a/Source/PrefabricatorRuntime/Private/Utils/PrefabricatorFunctionLibrary.cpp +++ b/Source/PrefabricatorRuntime/Private/Utils/PrefabricatorFunctionLibrary.cpp @@ -13,7 +13,8 @@ APrefabActor* UPrefabricatorBlueprintLibrary::SpawnPrefab(const UObject* WorldCo { APrefabActor* PrefabActor = nullptr; UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull); - if (World) { + // JB: Added check if the Prefab is not nullptr. + if (World && Prefab) { if (Prefab->bReplicates) { FActorSpawnParameters SpawnParams; PrefabActor = World->SpawnActor(AReplicablePrefabActor::StaticClass(), Transform); diff --git a/Source/PrefabricatorRuntime/Public/Asset/PrefabricatorAsset.h b/Source/PrefabricatorRuntime/Public/Asset/PrefabricatorAsset.h index d2b8de74..ac15d716 100644 --- a/Source/PrefabricatorRuntime/Public/Asset/PrefabricatorAsset.h +++ b/Source/PrefabricatorRuntime/Public/Asset/PrefabricatorAsset.h @@ -18,6 +18,10 @@ struct PREFABRICATORRUNTIME_API FPrefabricatorPropertyAssetMapping { UPROPERTY() FName AssetObjectPath; + // JB: The ItemID of the referenced ID. + UPROPERTY() + FGuid AssetItemID; + UPROPERTY() bool bUseQuotes = false; }; @@ -36,7 +40,8 @@ class PREFABRICATORRUNTIME_API UPrefabricatorProperty : public UObject { TArray AssetSoftReferenceMappings; void SaveReferencedAssetValues(); - void LoadReferencedAssetValues(); + // JB: The InChildActors map contains all actors spawned by the prefab stored by their ItemID. + void LoadReferencedAssetValues(const TMap& InChildActors); }; USTRUCT() diff --git a/Source/PrefabricatorRuntime/Public/Prefab/PrefabTools.h b/Source/PrefabricatorRuntime/Public/Prefab/PrefabTools.h index 3e72f20b..3b7ee70c 100644 --- a/Source/PrefabricatorRuntime/Public/Prefab/PrefabTools.h +++ b/Source/PrefabricatorRuntime/Public/Prefab/PrefabTools.h @@ -51,7 +51,8 @@ class PREFABRICATORRUNTIME_API FPrefabTools { private: static void SaveActorState(AActor* InActor, APrefabActor* PrefabActor, FPrefabricatorActorData& OutActorData); - static void LoadActorState(AActor* InActor, const FPrefabricatorActorData& InActorData, const FPrefabLoadSettings& InSettings); + // JB: The InChildActors map contains all actors spawned by the prefab stored by their ItemID. + static void LoadActorState(AActor* InActor, const FPrefabricatorActorData& InActorData, const FPrefabLoadSettings& InSettings, const TMap& InChildActors); };