From e43514558726b982a0f8e5b2ef1ad5b53b61e092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20By=C5=A1ka?= Date: Sat, 8 Feb 2020 11:41:42 +0100 Subject: [PATCH 1/2] Disables the serialization of RelativeLocation for root components as it was causing problems with PhysicX when spawning multiple prefabs at Runtime. --- .../Private/Prefab/PrefabTools.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Source/PrefabricatorRuntime/Private/Prefab/PrefabTools.cpp b/Source/PrefabricatorRuntime/Private/Prefab/PrefabTools.cpp index e1a0891f..4a5f2a47 100644 --- a/Source/PrefabricatorRuntime/Private/Prefab/PrefabTools.cpp +++ b/Source/PrefabricatorRuntime/Private/Prefab/PrefabTools.cpp @@ -298,6 +298,22 @@ namespace { } } + // JB: We skip serialization of RelativeLocation for root components. + // JB: The root component relative location is redundant as it equals to the actor location. + // JB: Having it set during deserialization causes problems with PhysicX when a large number of prefabs are spawned at runtime. + // JB: Restoring the relative location will essentially put all spawned actors on top of each other even if the prefabs are spawned at different places. + // JB: In such a case, the physics has to deal with the overlaps (or at least I think so) and significantly slows down. + // JB: Especially if the assets have a large number of collisions. + USceneComponent* SceneComponent = Cast(ObjToSerialize); + if(SceneComponent && Cast(SceneComponent->GetAttachParent())) + { + if (Property->GetName().Equals("RelativeLocation")) + { + return true; + } + } + + return false; } @@ -679,7 +695,8 @@ void FPrefabTools::LoadStateFromPrefabAsset(APrefabActor* PrefabActor, const FPr } } - ChildActor = Service->SpawnActor(ActorClass, FTransform::Identity, PrefabActor->GetLevel(), Template); + //JB: Spawning actors on top of each other may cause problems with PhysicX (as it needs to compute the overlaps). + ChildActor = Service->SpawnActor(ActorClass, PrefabActor->GetActorTransform(), PrefabActor->GetLevel(), Template); if (!Template) { LoadActorState(ChildActor, ActorItemData, InSettings); if (InState.IsValid()) { From 6dfc14522e9ba397c6481c5fe8fe6c2e9b5039fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20By=C5=A1ka?= Date: Sun, 23 Feb 2020 14:07:09 +0100 Subject: [PATCH 2/2] Fixes an issue with components simulating physics and physics constraints when spawning prefab at runtime. --- .../Private/Prefab/PrefabTools.cpp | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/Source/PrefabricatorRuntime/Private/Prefab/PrefabTools.cpp b/Source/PrefabricatorRuntime/Private/Prefab/PrefabTools.cpp index 4a5f2a47..bc72b4f9 100644 --- a/Source/PrefabricatorRuntime/Private/Prefab/PrefabTools.cpp +++ b/Source/PrefabricatorRuntime/Private/Prefab/PrefabTools.cpp @@ -359,6 +359,7 @@ namespace { } UPrefabricatorAsset* PrefabAsset = Cast(PrefabActor->PrefabComponent->PrefabAssetInterface.LoadSynchronous()); + USceneComponent* SceneComponent = Cast(ObjToSerialize); if (!PrefabAsset) { return; @@ -531,12 +532,28 @@ void FPrefabTools::LoadActorState(AActor* InActor, const FPrefabricatorActorData for (const FPrefabricatorComponentData& ComponentData : InActorData.Components) { if (UActorComponent** SearchResult = ComponentsByName.Find(ComponentData.ComponentName)) { UActorComponent* Component = *SearchResult; + USceneComponent* SceneComponent = Cast(Component); + + // JB: We store the world location in case we would be restoring a component simulating physics. + // JB: This is necessary only for prefab spawned at runtime. + FVector WorldLocation = FVector::ZeroVector; + if (InActor->HasActorBegunPlay()) { + if (SceneComponent) + { + WorldLocation = SceneComponent->GetComponentLocation(); + } + } + bool bPreviouslyRegister; { //SCOPE_CYCLE_COUNTER(STAT_LoadStateFromPrefabAsset_UnregisterComponent); bPreviouslyRegister = Component->IsRegistered(); if (InSettings.bUnregisterComponentsBeforeLoading && bPreviouslyRegister) { Component->UnregisterComponent(); + // JB: Some of the components (e.g., UPhysicsConstraintComponent) also require re-initialization. + if (Component->HasBeenInitialized()) { + Component->UninitializeComponent(); + } } } @@ -548,7 +565,14 @@ void FPrefabTools::LoadActorState(AActor* InActor, const FPrefabricatorActorData { //SCOPE_CYCLE_COUNTER(STAT_LoadStateFromPrefabAsset_RegisterComponent); if (InSettings.bUnregisterComponentsBeforeLoading && bPreviouslyRegister) { + // JB: Register component will also initialize component if necessary. Component->RegisterComponent(); + // JB: Components that are simulating physics are detached from the actor on register. + // JB: Restoring their relative location above will cause them to be spawned at a wrong location so we fix it. + // JB: This is necessary only for prefab spawned at runtime. + if (InActor->HasActorBegunPlay() && SceneComponent->IsSimulatingPhysics()) { + SceneComponent->SetRelativeLocation(WorldLocation); + } } } } @@ -696,7 +720,8 @@ void FPrefabTools::LoadStateFromPrefabAsset(APrefabActor* PrefabActor, const FPr } //JB: Spawning actors on top of each other may cause problems with PhysicX (as it needs to compute the overlaps). - ChildActor = Service->SpawnActor(ActorClass, PrefabActor->GetActorTransform(), PrefabActor->GetLevel(), Template); + FTransform WorldTransform = ActorItemData.RelativeTransform * PrefabActor->GetTransform(); + ChildActor = Service->SpawnActor(ActorClass, WorldTransform, PrefabActor->GetLevel(), Template); if (!Template) { LoadActorState(ChildActor, ActorItemData, InSettings); if (InState.IsValid()) {