Refactor prefab instances loading to improve refs loading between prefab objects
This commit is contained in:
@@ -1593,6 +1593,7 @@ bool Actor::FromBytes(const Span<byte>& data, Array<Actor*>& output, ISerializeM
|
||||
modifier->EngineBuild = engineBuild;
|
||||
CollectionPoolCache<ActorsCache::SceneObjectsListType>::ScopeCache sceneObjects = ActorsCache::SceneObjectsListCache.Get();
|
||||
sceneObjects->Resize(objectsCount);
|
||||
SceneObjectsFactory::Context context(modifier);
|
||||
|
||||
// Deserialize objects
|
||||
Scripting::ObjectsLookupIdMapping.Set(&modifier->IdsMapping);
|
||||
@@ -1623,7 +1624,7 @@ bool Actor::FromBytes(const Span<byte>& data, Array<Actor*>& output, ISerializeM
|
||||
}
|
||||
|
||||
// Create object
|
||||
auto obj = SceneObjectsFactory::Spawn(document, modifier);
|
||||
auto obj = SceneObjectsFactory::Spawn(context, document);
|
||||
sceneObjects->At(i) = obj;
|
||||
if (obj == nullptr)
|
||||
{
|
||||
@@ -1668,7 +1669,7 @@ bool Actor::FromBytes(const Span<byte>& data, Array<Actor*>& output, ISerializeM
|
||||
// Deserialize object
|
||||
auto obj = sceneObjects->At(i);
|
||||
if (obj)
|
||||
SceneObjectsFactory::Deserialize(obj, document, modifier);
|
||||
SceneObjectsFactory::Deserialize(context, obj, document);
|
||||
else
|
||||
SceneObjectsFactory::HandleObjectDeserializationError(document);
|
||||
}
|
||||
|
||||
@@ -933,42 +933,35 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
// Fire event
|
||||
CallSceneEvent(SceneEventType::OnSceneLoading, scene, sceneId);
|
||||
|
||||
// Maps the loaded actor object to the json data with the RemovedObjects array (used to skip restoring objects removed per prefab instance)
|
||||
SceneObjectsFactory::ActorToRemovedObjectsDataLookup actorToRemovedObjectsData;
|
||||
|
||||
// Loaded scene objects list
|
||||
CollectionPoolCache<ActorsCache::SceneObjectsListType>::ScopeCache sceneObjects = ActorsCache::SceneObjectsListCache.Get();
|
||||
sceneObjects->Resize(objectsCount);
|
||||
sceneObjects->At(0) = scene;
|
||||
|
||||
SceneObjectsFactory::Context context(modifier.Value);
|
||||
{
|
||||
PROFILE_CPU_NAMED("Spawn");
|
||||
|
||||
// Spawn all scene objects
|
||||
for (int32 i = 1; i < objectsCount; i++) // start from 1. at index [0] was scene
|
||||
{
|
||||
auto& objData = data[i];
|
||||
auto obj = SceneObjectsFactory::Spawn(objData, modifier.Value);
|
||||
auto& stream = data[i];
|
||||
auto obj = SceneObjectsFactory::Spawn(context, stream);
|
||||
sceneObjects->At(i) = obj;
|
||||
if (obj)
|
||||
{
|
||||
// Register object so it can be later referenced during deserialization (FindObject will work to link references between objects)
|
||||
obj->RegisterObject();
|
||||
|
||||
// Special case for actors
|
||||
if (auto actor = dynamic_cast<Actor*>(obj))
|
||||
{
|
||||
// Check for RemovedObjects listing
|
||||
const auto removedObjects = SERIALIZE_FIND_MEMBER(objData, "RemovedObjects");
|
||||
if (removedObjects != objData.MemberEnd())
|
||||
{
|
||||
actorToRemovedObjectsData.Add(actor, &removedObjects->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
SceneObjectsFactory::HandleObjectDeserializationError(stream);
|
||||
}
|
||||
}
|
||||
|
||||
SceneObjectsFactory::PrefabSyncData prefabSyncData(*sceneObjects.Value, data, modifier.Value);
|
||||
|
||||
SceneObjectsFactory::SetupPrefabInstances(context, prefabSyncData);
|
||||
|
||||
// TODO: resave and force sync scenes during game cooking so this step could be skipped in game
|
||||
SceneObjectsFactory::SynchronizeNewPrefabInstances(context, prefabSyncData);
|
||||
|
||||
// /\ all above this has to be done on an any thread
|
||||
// \/ all below this has to be done on multiple threads at once
|
||||
|
||||
@@ -984,9 +977,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
auto& objData = data[i];
|
||||
auto obj = sceneObjects->At(i);
|
||||
if (obj)
|
||||
SceneObjectsFactory::Deserialize(obj, objData, modifier.Value);
|
||||
else
|
||||
SceneObjectsFactory::HandleObjectDeserializationError(objData);
|
||||
SceneObjectsFactory::Deserialize(context, obj, objData);
|
||||
}
|
||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||
}
|
||||
@@ -994,11 +985,15 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
// /\ all above this has to be done on multiple threads at once
|
||||
// \/ all below this has to be done on an any thread
|
||||
|
||||
// Synchronize prefab instances (prefab may have objects removed or reordered so deserialized instances need to synchronize with it)
|
||||
// TODO: resave and force sync scenes during game cooking so this step could be skipped in game
|
||||
SceneObjectsFactory::SynchronizePrefabInstances(context, prefabSyncData);
|
||||
|
||||
// Call post load event to connect all scene actors
|
||||
{
|
||||
PROFILE_CPU_NAMED("Post Load");
|
||||
|
||||
for (int32 i = 0; i < objectsCount; i++)
|
||||
for (int32 i = 0; i < sceneObjects->Count(); i++)
|
||||
{
|
||||
SceneObject* obj = sceneObjects->At(i);
|
||||
if (obj)
|
||||
@@ -1006,17 +1001,12 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
}
|
||||
}
|
||||
|
||||
// Synchronize prefab instances (prefab may have new objects added or some removed so deserialized instances need to synchronize with it)
|
||||
// TODO: resave and force sync scenes during game cooking so this step could be skipped in game
|
||||
SceneObjectsFactory::SynchronizePrefabInstances(*sceneObjects.Value, actorToRemovedObjectsData, modifier.Value);
|
||||
|
||||
// Delete objects without parent
|
||||
for (int32 i = 1; i < objectsCount; i++)
|
||||
{
|
||||
SceneObject* obj = sceneObjects->At(i);
|
||||
if (obj && obj->GetParent() == nullptr)
|
||||
{
|
||||
sceneObjects->At(i) = nullptr;
|
||||
LOG(Warning, "Scene object {0} {1} has missing parent object after load. Removing it.", obj->GetID(), obj->ToString());
|
||||
obj->DeleteObject();
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ public:
|
||||
rapidjson_flax::Document Data;
|
||||
|
||||
/// <summary>
|
||||
/// The mapping from prefab instance object id to serialized objects array index (in Data)
|
||||
/// The mapping from prefab instance object id to serialized objects array index (in Data).
|
||||
/// </summary>
|
||||
Dictionary<Guid, int32> PrefabInstanceIdToDataIndex;
|
||||
|
||||
@@ -322,6 +322,26 @@ bool PrefabInstanceData::SynchronizePrefabInstances(Array<PrefabInstanceData>& p
|
||||
modifier->IdsMapping.Add(newPrefabObjectIds[i], Guid::New());
|
||||
}
|
||||
|
||||
// Create new objects added to prefab
|
||||
int32 deserializeSceneObjectIndex = sceneObjects->Count();
|
||||
SceneObjectsFactory::Context context(modifier.Value);
|
||||
for (int32 i = 0; i < newPrefabObjectIds.Count(); i++)
|
||||
{
|
||||
const Guid prefabObjectId = newPrefabObjectIds[i];
|
||||
const ISerializable::DeserializeStream* data;
|
||||
if (!prefabObjectIdToDiffData.TryGet(prefabObjectId, data))
|
||||
{
|
||||
LOG(Warning, "Missing object linkage to the prefab object diff data.");
|
||||
continue;
|
||||
}
|
||||
|
||||
SceneObject* obj = SceneObjectsFactory::Spawn(context, *(ISerializable::DeserializeStream*)data);
|
||||
if (!obj)
|
||||
continue;
|
||||
obj->RegisterObject();
|
||||
sceneObjects->Add(obj);
|
||||
}
|
||||
|
||||
// Apply modifications
|
||||
for (int32 i = existingObjectsCount - 1; i >= 0; i--)
|
||||
{
|
||||
@@ -349,25 +369,6 @@ bool PrefabInstanceData::SynchronizePrefabInstances(Array<PrefabInstanceData>& p
|
||||
}
|
||||
}
|
||||
|
||||
// Create new objects added to prefab
|
||||
int32 deserializeSceneObjectIndex = sceneObjects->Count();
|
||||
for (int32 i = 0; i < newPrefabObjectIds.Count(); i++)
|
||||
{
|
||||
const Guid prefabObjectId = newPrefabObjectIds[i];
|
||||
const ISerializable::DeserializeStream* data;
|
||||
if (!prefabObjectIdToDiffData.TryGet(prefabObjectId, data))
|
||||
{
|
||||
LOG(Warning, "Missing object linkage to the prefab object diff data.");
|
||||
continue;
|
||||
}
|
||||
|
||||
SceneObject* obj = SceneObjectsFactory::Spawn(*(ISerializable::DeserializeStream*)data, modifier.Value);
|
||||
if (!obj)
|
||||
continue;
|
||||
obj->RegisterObject();
|
||||
sceneObjects->Add(obj);
|
||||
}
|
||||
|
||||
// Deserialize new objects added to prefab
|
||||
for (int32 i = 0; i < newPrefabObjectIds.Count(); i++)
|
||||
{
|
||||
@@ -377,7 +378,7 @@ bool PrefabInstanceData::SynchronizePrefabInstances(Array<PrefabInstanceData>& p
|
||||
continue;
|
||||
|
||||
SceneObject* obj = sceneObjects->At(deserializeSceneObjectIndex);
|
||||
SceneObjectsFactory::Deserialize(obj, *(ISerializable::DeserializeStream*)data, modifier.Value);
|
||||
SceneObjectsFactory::Deserialize(context, obj, *(ISerializable::DeserializeStream*)data);
|
||||
|
||||
// Link new prefab instance to prefab and prefab object
|
||||
obj->LinkPrefab(prefabId, prefabObjectId);
|
||||
@@ -492,6 +493,9 @@ bool PrefabInstanceData::SynchronizePrefabInstances(Array<PrefabInstanceData>& p
|
||||
|
||||
bool PrefabInstanceData::SynchronizePrefabInstances(Array<PrefabInstanceData>& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, rapidjson_flax::StringBuffer& tmpBuffer, const Array<Guid>& oldObjectsIds, const Array<Guid>& newObjectIds)
|
||||
{
|
||||
if (prefabInstancesData.IsEmpty())
|
||||
return false;
|
||||
|
||||
// Fully serialize default instance scene objects (accumulate all prefab and nested prefabs changes into a single linear list of objects)
|
||||
rapidjson_flax::Document defaultInstanceData;
|
||||
{
|
||||
@@ -852,9 +856,10 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
// Create prefab objects
|
||||
auto& data = *Data;
|
||||
sceneObjects->Resize(ObjectsCount + newPrefabInstanceIdToDataIndex.Count());
|
||||
SceneObjectsFactory::Context context(modifier.Value);
|
||||
for (int32 i = 0; i < ObjectsCount; i++)
|
||||
{
|
||||
SceneObject* obj = SceneObjectsFactory::Spawn(data[i], modifier.Value);
|
||||
SceneObject* obj = SceneObjectsFactory::Spawn(context, data[i]);
|
||||
sceneObjects->At(i) = obj;
|
||||
if (!obj)
|
||||
{
|
||||
@@ -871,7 +876,7 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
for (auto i = newPrefabInstanceIdToDataIndex.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
const int32 dataIndex = i->Value;
|
||||
SceneObject* obj = SceneObjectsFactory::Spawn(diffDataDocument[dataIndex], modifier.Value);
|
||||
SceneObject* obj = SceneObjectsFactory::Spawn(context, diffDataDocument[dataIndex]);
|
||||
sceneObjects->At(newPrefabInstanceIdToDataIndexStart + newPrefabInstanceIdToDataIndexCounter++) = obj;
|
||||
if (!obj)
|
||||
{
|
||||
@@ -888,7 +893,7 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
SceneObject* obj = sceneObjects->At(i);
|
||||
if (!obj)
|
||||
continue;
|
||||
SceneObjectsFactory::Deserialize(obj, data[i], modifier.Value);
|
||||
SceneObjectsFactory::Deserialize(context, obj, data[i]);
|
||||
|
||||
int32 dataIndex;
|
||||
if (diffPrefabObjectIdToDataIndex.TryGet(obj->GetSceneObjectId(), dataIndex))
|
||||
@@ -919,11 +924,6 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
sceneObjects->At(i) = nullptr;
|
||||
}
|
||||
}
|
||||
for (int32 i = 0; i < sceneObjects->Count(); i++)
|
||||
{
|
||||
if (sceneObjects->At(i) == nullptr)
|
||||
sceneObjects->RemoveAtKeepOrder(i);
|
||||
}
|
||||
|
||||
// Deserialize new prefab objects
|
||||
newPrefabInstanceIdToDataIndexCounter = 0;
|
||||
@@ -933,7 +933,7 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
SceneObject* obj = sceneObjects->At(newPrefabInstanceIdToDataIndexStart + newPrefabInstanceIdToDataIndexCounter++);
|
||||
if (!obj)
|
||||
continue;
|
||||
SceneObjectsFactory::Deserialize(obj, diffDataDocument[dataIndex], modifier.Value);
|
||||
SceneObjectsFactory::Deserialize(context, obj, diffDataDocument[dataIndex]);
|
||||
}
|
||||
for (int32 j = 0; j < targetObjects->Count(); j++)
|
||||
{
|
||||
@@ -954,6 +954,11 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int32 i = 0; i < sceneObjects->Count(); i++)
|
||||
{
|
||||
if (sceneObjects->At(i) == nullptr)
|
||||
sceneObjects->RemoveAtKeepOrder(i);
|
||||
}
|
||||
|
||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||
if (sceneObjects.Value->IsEmpty())
|
||||
|
||||
@@ -123,8 +123,6 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, Dictionary<Guid
|
||||
// Prepare
|
||||
CollectionPoolCache<ActorsCache::SceneObjectsListType>::ScopeCache sceneObjects = ActorsCache::SceneObjectsListCache.Get();
|
||||
sceneObjects->Resize(objectsCount);
|
||||
CollectionPoolCache<ActorsCache::SceneObjectsListType>::ScopeCache prefabDataIndexToSceneObject = ActorsCache::SceneObjectsListCache.Get();
|
||||
prefabDataIndexToSceneObject->Resize(objectsCount);
|
||||
CollectionPoolCache<ISerializeModifier, Cache::ISerializeModifierClearCallback>::ScopeCache modifier = Cache::ISerializeModifier.Get();
|
||||
modifier->IdsMapping.EnsureCapacity(prefab->ObjectsIds.Count() * 4);
|
||||
for (int32 i = 0; i < prefab->ObjectsIds.Count(); i++)
|
||||
@@ -136,34 +134,35 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, Dictionary<Guid
|
||||
objectsCache->Clear();
|
||||
objectsCache->SetCapacity(prefab->ObjectsDataCache.Capacity());
|
||||
}
|
||||
auto& data = *prefab->Data;
|
||||
SceneObjectsFactory::Context context(modifier.Value);
|
||||
|
||||
// Deserialize prefab objects
|
||||
auto& data = *prefab->Data;
|
||||
Scripting::ObjectsLookupIdMapping.Set(&modifier.Value->IdsMapping);
|
||||
for (int32 i = 0; i < objectsCount; i++)
|
||||
{
|
||||
auto& stream = data[i];
|
||||
|
||||
SceneObject* obj = SceneObjectsFactory::Spawn(stream, modifier.Value);
|
||||
prefabDataIndexToSceneObject->operator[](i) = obj;
|
||||
auto obj = SceneObjectsFactory::Spawn(context, stream);
|
||||
sceneObjects->At(i) = obj;
|
||||
if (obj)
|
||||
{
|
||||
obj->RegisterObject();
|
||||
}
|
||||
else
|
||||
{
|
||||
SceneObjectsFactory::HandleObjectDeserializationError(stream);
|
||||
}
|
||||
}
|
||||
SceneObjectsFactory::PrefabSyncData prefabSyncData(*sceneObjects.Value, data, modifier.Value);
|
||||
if (withSynchronization)
|
||||
{
|
||||
// Synchronize new prefab instances (prefab may have new objects added so deserialized instances need to synchronize with it)
|
||||
// TODO: resave and force sync prefabs during game cooking so this step could be skipped in game
|
||||
SceneObjectsFactory::SynchronizeNewPrefabInstances(context, prefabSyncData);
|
||||
Scripting::ObjectsLookupIdMapping.Set(&modifier.Value->IdsMapping);
|
||||
}
|
||||
for (int32 i = 0; i < objectsCount; i++)
|
||||
{
|
||||
auto& stream = data[i];
|
||||
SceneObject* obj = prefabDataIndexToSceneObject->At(i);
|
||||
SceneObject* obj = sceneObjects->At(i);
|
||||
if (obj)
|
||||
{
|
||||
SceneObjectsFactory::Deserialize(obj, stream, modifier.Value);
|
||||
}
|
||||
SceneObjectsFactory::Deserialize(context, obj, stream);
|
||||
}
|
||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||
|
||||
@@ -198,26 +197,8 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, Dictionary<Guid
|
||||
// Synchronize prefab instances (prefab may have new objects added or some removed so deserialized instances need to synchronize with it)
|
||||
if (withSynchronization)
|
||||
{
|
||||
// Maps the loaded actor object to the json data with the RemovedObjects array (used to skip restoring objects removed per prefab instance)
|
||||
SceneObjectsFactory::ActorToRemovedObjectsDataLookup actorToRemovedObjectsData;
|
||||
for (int32 i = 0; i < objectsCount; i++)
|
||||
{
|
||||
auto& stream = data[i];
|
||||
Actor* actor = dynamic_cast<Actor*>(prefabDataIndexToSceneObject->At(i));
|
||||
if (!actor)
|
||||
continue;
|
||||
|
||||
// Check for RemovedObjects listing
|
||||
const auto removedObjects = stream.FindMember("RemovedObjects");
|
||||
if (removedObjects != stream.MemberEnd())
|
||||
{
|
||||
actorToRemovedObjectsData.Add(actor, &removedObjects->value);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: consider caching actorToRemovedObjectsData per prefab
|
||||
|
||||
SceneObjectsFactory::SynchronizePrefabInstances(*sceneObjects.Value, actorToRemovedObjectsData, modifier.Value);
|
||||
// TODO: resave and force sync scenes during game cooking so this step could be skipped in game
|
||||
SceneObjectsFactory::SynchronizePrefabInstances(context, prefabSyncData);
|
||||
}
|
||||
|
||||
// Delete objects without parent or with invalid linkage to the prefab
|
||||
@@ -274,7 +255,7 @@ Actor* PrefabManager::SpawnPrefab(Prefab* prefab, Actor* parent, Dictionary<Guid
|
||||
for (int32 i = 0; i < objectsCount; i++)
|
||||
{
|
||||
auto& stream = data[i];
|
||||
SceneObject* obj = prefabDataIndexToSceneObject->At(i);
|
||||
SceneObject* obj = sceneObjects->At(i);
|
||||
if (!obj)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -8,15 +8,21 @@
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#include "Engine/Serialization/JsonTools.h"
|
||||
#include "Engine/Serialization/ISerializeModifier.h"
|
||||
#include "Engine/Serialization/SerializationFwd.h"
|
||||
#include "Engine/Serialization/JsonWriters.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Threading/ThreadLocal.h"
|
||||
|
||||
SceneObject* SceneObjectsFactory::Spawn(ISerializable::DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
SceneObjectsFactory::Context::Context(ISerializeModifier* modifier)
|
||||
: Modifier(modifier)
|
||||
{
|
||||
}
|
||||
|
||||
SceneObject* SceneObjectsFactory::Spawn(Context& context, ISerializable::DeserializeStream& stream)
|
||||
{
|
||||
// Get object id
|
||||
Guid id = JsonTools::GetGuid(stream, "ID");
|
||||
modifier->IdsMapping.TryGet(id, id);
|
||||
context.Modifier->IdsMapping.TryGet(id, id);
|
||||
if (!id.IsValid())
|
||||
{
|
||||
LOG(Warning, "Invalid object id.");
|
||||
@@ -59,10 +65,10 @@ SceneObject* SceneObjectsFactory::Spawn(ISerializable::DeserializeStream& stream
|
||||
}
|
||||
|
||||
// Map prefab object ID to the deserialized instance ID
|
||||
modifier->IdsMapping[prefabObjectId] = id;
|
||||
context.Modifier->IdsMapping[prefabObjectId] = id;
|
||||
|
||||
// Create prefab instance (recursive prefab loading to support nested prefabs)
|
||||
obj = Spawn(*(ISerializable::DeserializeStream*)prefabData, modifier);
|
||||
obj = Spawn(context, *(ISerializable::DeserializeStream*)prefabData);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -139,7 +145,7 @@ SceneObject* SceneObjectsFactory::Spawn(ISerializable::DeserializeStream& stream
|
||||
return obj;
|
||||
}
|
||||
|
||||
void SceneObjectsFactory::Deserialize(SceneObject* obj, ISerializable::DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
void SceneObjectsFactory::Deserialize(Context& context, SceneObject* obj, ISerializable::DeserializeStream& stream)
|
||||
{
|
||||
// Check for prefab instance
|
||||
Guid prefabObjectId;
|
||||
@@ -175,171 +181,21 @@ void SceneObjectsFactory::Deserialize(SceneObject* obj, ISerializable::Deseriali
|
||||
}
|
||||
|
||||
// Deserialize prefab data (recursive prefab loading to support nested prefabs)
|
||||
Deserialize(obj, *(ISerializable::DeserializeStream*)prefabData, modifier);
|
||||
Deserialize(context, obj, *(ISerializable::DeserializeStream*)prefabData);
|
||||
}
|
||||
|
||||
int32 instanceIndex;
|
||||
if (context.ObjectToInstance.TryGet(obj->GetID(), instanceIndex) && instanceIndex != context.CurrentInstance)
|
||||
{
|
||||
// Apply the current prefab instance objects ids table to resolve references inside a prefab properly
|
||||
context.CurrentInstance = instanceIndex;
|
||||
auto& instance = context.Instances[instanceIndex];
|
||||
for (auto& e : instance.IdsMapping)
|
||||
context.Modifier->IdsMapping[e.Key] = e.Value;
|
||||
}
|
||||
|
||||
// Load data
|
||||
obj->Deserialize(stream, modifier);
|
||||
}
|
||||
|
||||
bool Contains(Actor* actor, const SceneObjectsFactory::ActorToRemovedObjectsDataLookup& actorToRemovedObjectsData, const Guid& prefabObjectId)
|
||||
{
|
||||
// Check if actor has any removed objects registered
|
||||
const rapidjson_flax::Value* data;
|
||||
if (actorToRemovedObjectsData.TryGet(actor, data))
|
||||
{
|
||||
const int32 size = static_cast<int32>(data->Size());
|
||||
for (int32 i = 0; i < size; i++)
|
||||
{
|
||||
if (JsonTools::GetGuid(data->operator[](i)) == prefabObjectId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SceneObjectsFactory::SynchronizePrefabInstances(Array<SceneObject*>& sceneObjects, const ActorToRemovedObjectsDataLookup& actorToRemovedObjectsData, ISerializeModifier* modifier)
|
||||
{
|
||||
PROFILE_CPU_NAMED("SynchronizePrefabInstances");
|
||||
|
||||
Scripting::ObjectsLookupIdMapping.Set(&modifier->IdsMapping);
|
||||
|
||||
// Check all objects with prefab linkage for moving to a proper parent
|
||||
const int32 objectsToCheckCount = sceneObjects.Count();
|
||||
for (int32 i = 0; i < objectsToCheckCount; i++)
|
||||
{
|
||||
SceneObject* obj = sceneObjects[i];
|
||||
if (!obj)
|
||||
continue;
|
||||
SceneObject* parent = obj->GetParent();
|
||||
const Guid prefabId = obj->GetPrefabID();
|
||||
if (parent == nullptr || !obj->HasPrefabLink() || !parent->HasPrefabLink() || parent->GetPrefabID() != prefabId)
|
||||
continue;
|
||||
const Guid prefabObjectId = obj->GetPrefabObjectID();
|
||||
const Guid parentPrefabObjectId = parent->GetPrefabObjectID();
|
||||
|
||||
// Load prefab
|
||||
auto prefab = Content::LoadAsync<Prefab>(prefabId);
|
||||
if (prefab == nullptr)
|
||||
{
|
||||
LOG(Warning, "Missing prefab with id={0}.", prefabId);
|
||||
continue;
|
||||
}
|
||||
if (prefab->WaitForLoaded())
|
||||
{
|
||||
LOG(Warning, "Failed to load prefab {0}.", prefab->ToString());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the actual parent object stored in the prefab data
|
||||
const ISerializable::DeserializeStream* objData;
|
||||
Guid actualParentPrefabId;
|
||||
if (!prefab->ObjectsDataCache.TryGet(prefabObjectId, objData) || !JsonTools::GetGuidIfValid(actualParentPrefabId, *objData, "ParentID"))
|
||||
continue;
|
||||
|
||||
// Validate
|
||||
if (actualParentPrefabId != parentPrefabObjectId)
|
||||
{
|
||||
// Invalid connection object found!
|
||||
LOG(Info, "Object {0} has invalid parent object {4} -> {5} (PrefabObjectID: {1}, PrefabID: {2}, Path: {3})", obj->GetSceneObjectId(), prefabObjectId, prefab->GetID(), prefab->GetPath(), parentPrefabObjectId, actualParentPrefabId);
|
||||
|
||||
// Map actual prefab object id to the current scene objects collection
|
||||
modifier->IdsMapping.TryGet(actualParentPrefabId, actualParentPrefabId);
|
||||
|
||||
// Find parent
|
||||
const auto actualParent = Scripting::FindObject<Actor>(actualParentPrefabId);
|
||||
if (!actualParent)
|
||||
{
|
||||
LOG(Warning, "The actual parent is missing.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reparent
|
||||
obj->SetParent(actualParent, false);
|
||||
}
|
||||
|
||||
// Preserve order in parent (values from prefab are used)
|
||||
if (i != 0)
|
||||
{
|
||||
const auto defaultInstance = prefab->GetDefaultInstance(obj->GetPrefabObjectID());
|
||||
if (defaultInstance)
|
||||
{
|
||||
obj->SetOrderInParent(defaultInstance->GetOrderInParent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check all actors with prefab linkage for adding missing objects
|
||||
for (int32 i = 0; i < objectsToCheckCount; i++)
|
||||
{
|
||||
Actor* actor = dynamic_cast<Actor*>(sceneObjects[i]);
|
||||
if (!actor || !actor->HasPrefabLink())
|
||||
continue;
|
||||
const Guid actorId = actor->GetID();
|
||||
const Guid prefabId = actor->GetPrefabID();
|
||||
const Guid actorPrefabObjectId = actor->GetPrefabObjectID();
|
||||
|
||||
// Map prefab object id to this actor so the new objects gets added to it
|
||||
modifier->IdsMapping[actorPrefabObjectId] = actorId;
|
||||
|
||||
// Load prefab
|
||||
auto prefab = Content::LoadAsync<Prefab>(prefabId);
|
||||
if (prefab == nullptr)
|
||||
{
|
||||
LOG(Warning, "Missing prefab with id={0}.", prefabId);
|
||||
continue;
|
||||
}
|
||||
if (prefab->WaitForLoaded())
|
||||
{
|
||||
LOG(Warning, "Failed to load prefab {0}.", prefab->ToString());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the given actor has new children or scripts added (inside the prefab that it uses)
|
||||
// TODO: consider caching prefab objects structure maybe to boost this logic?
|
||||
for (auto it = prefab->ObjectsDataCache.Begin(); it.IsNotEnd(); ++it)
|
||||
{
|
||||
// Use only objects that are linked to the current actor
|
||||
const Guid parentId = JsonTools::GetGuid(*it->Value, "ParentID");
|
||||
if (parentId != actorPrefabObjectId)
|
||||
continue;
|
||||
|
||||
// Use only objects that are missing
|
||||
const Guid prefabObjectId = JsonTools::GetGuid(*it->Value, "ID");
|
||||
if (actor->GetChildByPrefabObjectId(prefabObjectId) != nullptr ||
|
||||
actor->GetScriptByPrefabObjectId(prefabObjectId) != nullptr)
|
||||
continue;
|
||||
|
||||
// Skip if object was marked to be removed per instance
|
||||
if (Contains(actor, actorToRemovedObjectsData, prefabObjectId))
|
||||
continue;
|
||||
|
||||
// Create instance (including all children)
|
||||
Scripting::ObjectsLookupIdMapping.Set(&modifier->IdsMapping);
|
||||
SynchronizeNewPrefabInstance(prefab, actor, prefabObjectId, sceneObjects, modifier);
|
||||
}
|
||||
}
|
||||
|
||||
// Call post load event to the new objects
|
||||
for (int32 i = objectsToCheckCount; i < sceneObjects.Count(); i++)
|
||||
{
|
||||
SceneObject* obj = sceneObjects[i];
|
||||
|
||||
// Preserve order in parent (values from prefab are used)
|
||||
auto prefab = Content::LoadAsync<Prefab>(obj->GetPrefabID());
|
||||
const auto defaultInstance = prefab && prefab->IsLoaded() ? prefab->GetDefaultInstance(obj->GetPrefabObjectID()) : nullptr;
|
||||
if (defaultInstance)
|
||||
{
|
||||
obj->SetOrderInParent(defaultInstance->GetOrderInParent());
|
||||
}
|
||||
|
||||
obj->PostLoad();
|
||||
}
|
||||
|
||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||
obj->Deserialize(stream, context.Modifier);
|
||||
}
|
||||
|
||||
void SceneObjectsFactory::HandleObjectDeserializationError(const ISerializable::DeserializeStream& value)
|
||||
@@ -434,7 +290,248 @@ Actor* SceneObjectsFactory::CreateActor(int32 typeId, const Guid& id)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SceneObjectsFactory::SynchronizeNewPrefabInstance(Prefab* prefab, Actor* actor, const Guid& prefabObjectId, Array<SceneObject*>& sceneObjects, ISerializeModifier* modifier)
|
||||
SceneObjectsFactory::PrefabSyncData::PrefabSyncData(Array<SceneObject*>& sceneObjects, const ISerializable::DeserializeStream& data, ISerializeModifier* modifier)
|
||||
: SceneObjects(sceneObjects)
|
||||
, Data(data)
|
||||
, Modifier(modifier)
|
||||
, InitialCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
void SceneObjectsFactory::SetupPrefabInstances(Context& context, PrefabSyncData& data)
|
||||
{
|
||||
PROFILE_CPU_NAMED("SetupPrefabInstances");
|
||||
const int32 count = data.Data.Size();
|
||||
ASSERT(count <= data.SceneObjects.Count())
|
||||
for (int32 i = 0; i < count; i++)
|
||||
{
|
||||
SceneObject* obj = data.SceneObjects[i];
|
||||
if (!obj)
|
||||
continue;
|
||||
const auto& stream = data.Data[i];
|
||||
Guid prefabObjectId, prefabId;
|
||||
if (!JsonTools::GetGuidIfValid(prefabObjectId, stream, "PrefabObjectID"))
|
||||
continue;
|
||||
if (!JsonTools::GetGuidIfValid(prefabId, stream, "PrefabID"))
|
||||
continue;
|
||||
const Guid parentId = JsonTools::GetGuid(stream, "ParentID");
|
||||
const Guid id = obj->GetID();
|
||||
auto prefab = Content::LoadAsync<Prefab>(prefabId);
|
||||
|
||||
// Check if it's parent is in the same prefab
|
||||
int32 index;
|
||||
if (context.ObjectToInstance.TryGet(parentId, index) && context.Instances[index].Prefab == prefab)
|
||||
{
|
||||
// Use parent object as prefab instance
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use new prefab instance
|
||||
index = context.Instances.Count();
|
||||
auto& e = context.Instances.AddOne();
|
||||
e.Prefab = prefab;
|
||||
e.RootId = id;
|
||||
}
|
||||
context.ObjectToInstance[id] = index;
|
||||
|
||||
// Add to the prefab instance IDs mapping
|
||||
auto& prefabInstance = context.Instances[index];
|
||||
prefabInstance.IdsMapping[prefabObjectId] = id;
|
||||
}
|
||||
}
|
||||
|
||||
void SceneObjectsFactory::SynchronizeNewPrefabInstances(Context& context, PrefabSyncData& data)
|
||||
{
|
||||
PROFILE_CPU_NAMED("SynchronizeNewPrefabInstances");
|
||||
|
||||
Scripting::ObjectsLookupIdMapping.Set(&data.Modifier->IdsMapping);
|
||||
data.InitialCount = data.SceneObjects.Count();
|
||||
|
||||
// Check all actors with prefab linkage for adding missing objects
|
||||
for (int32 i = 0; i < data.InitialCount; i++)
|
||||
{
|
||||
Actor* actor = dynamic_cast<Actor*>(data.SceneObjects[i]);
|
||||
if (!actor)
|
||||
continue;
|
||||
const auto& stream = data.Data[i];
|
||||
Guid actorId, actorPrefabObjectId, prefabId;
|
||||
if (!JsonTools::GetGuidIfValid(actorPrefabObjectId, stream, "PrefabObjectID"))
|
||||
continue;
|
||||
if (!JsonTools::GetGuidIfValid(prefabId, stream, "PrefabID"))
|
||||
continue;
|
||||
if (!JsonTools::GetGuidIfValid(actorId, stream, "ID"))
|
||||
continue;
|
||||
const Guid actorParentId = JsonTools::GetGuid(stream, "ParentID");
|
||||
|
||||
// Map prefab object id to this actor so the new objects gets added to it
|
||||
data.Modifier->IdsMapping[actorPrefabObjectId] = actor->GetID();
|
||||
|
||||
// Load prefab
|
||||
auto prefab = Content::LoadAsync<Prefab>(prefabId);
|
||||
if (prefab == nullptr)
|
||||
{
|
||||
LOG(Warning, "Missing prefab with id={0}.", prefabId);
|
||||
continue;
|
||||
}
|
||||
if (prefab->WaitForLoaded())
|
||||
{
|
||||
LOG(Warning, "Failed to load prefab {0}.", prefab->ToString());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for RemovedObjects list
|
||||
const auto removedObjects = SERIALIZE_FIND_MEMBER(stream, "RemovedObjects");
|
||||
|
||||
// Check if the given actor has new children or scripts added (inside the prefab that it uses)
|
||||
// TODO: consider caching prefab objects structure maybe to boost this logic?
|
||||
for (auto it = prefab->ObjectsDataCache.Begin(); it.IsNotEnd(); ++it)
|
||||
{
|
||||
// Use only objects that are linked to the current actor
|
||||
const Guid parentId = JsonTools::GetGuid(*it->Value, "ParentID");
|
||||
if (parentId != actorPrefabObjectId)
|
||||
continue;
|
||||
|
||||
// Skip if object was marked to be removed per instance
|
||||
const Guid prefabObjectId = JsonTools::GetGuid(*it->Value, "ID");
|
||||
if (removedObjects != stream.MemberEnd())
|
||||
{
|
||||
auto& list = removedObjects->value;
|
||||
const int32 size = static_cast<int32>(list.Size());
|
||||
bool removed = false;
|
||||
for (int32 j = 0; j < size; j++)
|
||||
{
|
||||
if (JsonTools::GetGuid(list[j]) == prefabObjectId)
|
||||
{
|
||||
removed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (removed)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use only objects that are missing
|
||||
bool spawned = false;
|
||||
for (int32 j = i + 1; j < data.InitialCount; j++)
|
||||
{
|
||||
const auto& jData = data.Data[j];
|
||||
const Guid jParentId = JsonTools::GetGuid(jData, "ParentID");
|
||||
//if (jParentId == actorParentId)
|
||||
// break;
|
||||
if (jParentId != actorId)
|
||||
continue;
|
||||
const Guid jPrefabObjectId = JsonTools::GetGuid(jData, "PrefabObjectID");
|
||||
if (jPrefabObjectId != prefabObjectId)
|
||||
continue;
|
||||
|
||||
// This object exists in the saved scene objects list
|
||||
spawned = true;
|
||||
break;
|
||||
}
|
||||
if (spawned)
|
||||
continue;
|
||||
|
||||
// Create instance (including all children)
|
||||
Scripting::ObjectsLookupIdMapping.Set(&data.Modifier->IdsMapping);
|
||||
SynchronizeNewPrefabInstance(context, data, prefab, actor, prefabObjectId);
|
||||
}
|
||||
}
|
||||
|
||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||
}
|
||||
|
||||
void SceneObjectsFactory::SynchronizePrefabInstances(Context& context, PrefabSyncData& data)
|
||||
{
|
||||
PROFILE_CPU_NAMED("SynchronizePrefabInstances");
|
||||
|
||||
// Check all objects with prefab linkage for moving to a proper parent
|
||||
for (int32 i = 0; i < data.InitialCount; i++)
|
||||
{
|
||||
SceneObject* obj = data.SceneObjects[i];
|
||||
if (!obj)
|
||||
continue;
|
||||
SceneObject* parent = obj->GetParent();
|
||||
const Guid prefabId = obj->GetPrefabID();
|
||||
if (parent == nullptr || !obj->HasPrefabLink() || !parent->HasPrefabLink() || parent->GetPrefabID() != prefabId)
|
||||
continue;
|
||||
const Guid prefabObjectId = obj->GetPrefabObjectID();
|
||||
const Guid parentPrefabObjectId = parent->GetPrefabObjectID();
|
||||
|
||||
// Load prefab
|
||||
auto prefab = Content::LoadAsync<Prefab>(prefabId);
|
||||
if (prefab == nullptr)
|
||||
{
|
||||
LOG(Warning, "Missing prefab with id={0}.", prefabId);
|
||||
continue;
|
||||
}
|
||||
if (prefab->WaitForLoaded())
|
||||
{
|
||||
LOG(Warning, "Failed to load prefab {0}.", prefab->ToString());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the actual parent object stored in the prefab data
|
||||
const ISerializable::DeserializeStream* objData;
|
||||
Guid actualParentPrefabId;
|
||||
if (!prefab->ObjectsDataCache.TryGet(prefabObjectId, objData) || !JsonTools::GetGuidIfValid(actualParentPrefabId, *objData, "ParentID"))
|
||||
continue;
|
||||
|
||||
// Validate
|
||||
if (actualParentPrefabId != parentPrefabObjectId)
|
||||
{
|
||||
// Invalid connection object found!
|
||||
LOG(Info, "Object {0} has invalid parent object {4} -> {5} (PrefabObjectID: {1}, PrefabID: {2}, Path: {3})", obj->GetSceneObjectId(), prefabObjectId, prefab->GetID(), prefab->GetPath(), parentPrefabObjectId, actualParentPrefabId);
|
||||
|
||||
// Map actual prefab object id to the current scene objects collection
|
||||
data.Modifier->IdsMapping.TryGet(actualParentPrefabId, actualParentPrefabId);
|
||||
|
||||
// Find parent
|
||||
const auto actualParent = Scripting::FindObject<Actor>(actualParentPrefabId);
|
||||
if (!actualParent)
|
||||
{
|
||||
LOG(Warning, "The actual parent is missing.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reparent
|
||||
obj->SetParent(actualParent, false);
|
||||
}
|
||||
|
||||
// Preserve order in parent (values from prefab are used)
|
||||
if (i != 0)
|
||||
{
|
||||
const auto defaultInstance = prefab->GetDefaultInstance(obj->GetPrefabObjectID());
|
||||
if (defaultInstance)
|
||||
{
|
||||
obj->SetOrderInParent(defaultInstance->GetOrderInParent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Scripting::ObjectsLookupIdMapping.Set(&data.Modifier->IdsMapping);
|
||||
|
||||
// Synchronize new prefab objects
|
||||
for (int32 i = 0; i < data.NewObjects.Count(); i++)
|
||||
{
|
||||
SceneObject* obj = data.SceneObjects[data.InitialCount + i];
|
||||
auto& newObj = data.NewObjects[i];
|
||||
|
||||
// Deserialize object with prefab data
|
||||
Deserialize(context, obj, *(ISerializable::DeserializeStream*)newObj.PrefabData);
|
||||
obj->LinkPrefab(newObj.Prefab->GetID(), newObj.PrefabObjectId);
|
||||
|
||||
// Preserve order in parent (values from prefab are used)
|
||||
const auto defaultInstance = newObj.Prefab->GetDefaultInstance(newObj.PrefabObjectId);
|
||||
if (defaultInstance)
|
||||
{
|
||||
obj->SetOrderInParent(defaultInstance->GetOrderInParent());
|
||||
}
|
||||
}
|
||||
|
||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||
}
|
||||
|
||||
void SceneObjectsFactory::SynchronizeNewPrefabInstance(Context& context, PrefabSyncData& data, Prefab* prefab, Actor* actor, const Guid& prefabObjectId)
|
||||
{
|
||||
PROFILE_CPU_NAMED("SynchronizeNewPrefabInstance");
|
||||
|
||||
@@ -450,22 +547,33 @@ void SceneObjectsFactory::SynchronizeNewPrefabInstance(Prefab* prefab, Actor* ac
|
||||
}
|
||||
|
||||
// Map prefab object ID to the new prefab object instance
|
||||
modifier->IdsMapping[prefabObjectId] = Guid::New();
|
||||
Scripting::ObjectsLookupIdMapping.Set(&modifier->IdsMapping);
|
||||
Guid id = Guid::New();
|
||||
data.Modifier->IdsMapping[prefabObjectId] = id;
|
||||
|
||||
// Create prefab instance (recursive prefab loading to support nested prefabs)
|
||||
auto child = Spawn(*(ISerializable::DeserializeStream*)prefabData, modifier);
|
||||
auto child = Spawn(context, *(ISerializable::DeserializeStream*)prefabData);
|
||||
if (!child)
|
||||
{
|
||||
LOG(Warning, "Failed to create object {1} from prefab {0}.", prefab->ToString(), prefabObjectId);
|
||||
return;
|
||||
}
|
||||
child->RegisterObject();
|
||||
Deserialize(child, *(ISerializable::DeserializeStream*)prefabData, modifier);
|
||||
|
||||
// Register object
|
||||
child->LinkPrefab(prefab->GetID(), prefabObjectId);
|
||||
sceneObjects.Add(child);
|
||||
child->RegisterObject();
|
||||
data.SceneObjects.Add(child);
|
||||
auto& newObj = data.NewObjects.AddOne();
|
||||
newObj.Prefab = prefab;
|
||||
newObj.PrefabData = prefabData;
|
||||
newObj.PrefabObjectId = prefabObjectId;
|
||||
newObj.Id = id;
|
||||
int32 instanceIndex = -1;
|
||||
if (context.ObjectToInstance.TryGet(actor->GetID(), instanceIndex) && context.Instances[instanceIndex].Prefab == prefab)
|
||||
{
|
||||
// Add to the prefab instance IDs mapping
|
||||
context.ObjectToInstance[id] = instanceIndex;
|
||||
auto& prefabInstance = context.Instances[instanceIndex];
|
||||
prefabInstance.IdsMapping[prefabObjectId] = id;
|
||||
}
|
||||
|
||||
// Use loop to add even more objects to added objects (prefab can have one new object that has another child, we need to add that child)
|
||||
// TODO: prefab could cache lookup object id -> children ids
|
||||
@@ -475,7 +583,7 @@ void SceneObjectsFactory::SynchronizeNewPrefabInstance(Prefab* prefab, Actor* ac
|
||||
if (JsonTools::GetGuidIfValid(qParentId, *q->Value, "ParentID") && qParentId == prefabObjectId)
|
||||
{
|
||||
const Guid qPrefabObjectId = JsonTools::GetGuid(*q->Value, "ID");
|
||||
SynchronizeNewPrefabInstance(prefab, actor, qPrefabObjectId, sceneObjects, modifier);
|
||||
SynchronizeNewPrefabInstance(context, data, prefab, actor, qPrefabObjectId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,43 +3,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "SceneObject.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for scene objects creation and deserialization utilities.
|
||||
/// </summary>
|
||||
class SceneObjectsFactory
|
||||
class FLAXENGINE_API SceneObjectsFactory
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Dictionary<Actor*, const rapidjson_flax::Value*, HeapAllocation> ActorToRemovedObjectsDataLookup;
|
||||
struct PrefabInstance
|
||||
{
|
||||
Guid RootId;
|
||||
Prefab* Prefab;
|
||||
Dictionary<Guid, Guid> IdsMapping;
|
||||
};
|
||||
|
||||
public:
|
||||
struct Context
|
||||
{
|
||||
ISerializeModifier* Modifier;
|
||||
int32 CurrentInstance = -1;
|
||||
Array<PrefabInstance> Instances;
|
||||
Dictionary<Guid, int32> ObjectToInstance;
|
||||
|
||||
Context(ISerializeModifier* modifier);
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Creates the scene object from the specified data value. Does not perform deserialization.
|
||||
/// </summary>
|
||||
/// <param name="context">The serialization context.</param>
|
||||
/// <param name="stream">The serialized data stream.</param>
|
||||
/// <param name="modifier">The serialization modifier. Cannot be null.</param>
|
||||
static SceneObject* Spawn(ISerializable::DeserializeStream& stream, ISerializeModifier* modifier);
|
||||
static SceneObject* Spawn(Context& context, ISerializable::DeserializeStream& stream);
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the scene object from the specified data value.
|
||||
/// </summary>
|
||||
/// <param name="context">The serialization context.</param>
|
||||
/// <param name="obj">The instance to deserialize.</param>
|
||||
/// <param name="stream">The serialized data stream.</param>
|
||||
/// <param name="modifier">The serialization modifier. Cannot be null.</param>
|
||||
static void Deserialize(SceneObject* obj, ISerializable::DeserializeStream& stream, ISerializeModifier* modifier);
|
||||
|
||||
/// <summary>
|
||||
/// Synchronizes the prefab instances. Prefabs may have new objects added or some removed so deserialized instances need to synchronize with it. Handles also changing prefab object parent in the instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Should be called after scene objects deserialization and PostLoad event when scene objects hierarchy is ready (parent-child relation exists). But call it before Init and BeginPlay events.
|
||||
/// </remarks>
|
||||
/// <param name="sceneObjects">The loaded scene objects. Collection will be modified after usage.</param>
|
||||
/// <param name="actorToRemovedObjectsData">Maps the loaded actor object to the json data with the RemovedObjects array (used to skip restoring objects removed per prefab instance).</param>
|
||||
/// <param name="modifier">The objects deserialization modifier. Collection will be modified after usage.</param>
|
||||
static void SynchronizePrefabInstances(Array<SceneObject*>& sceneObjects, const ActorToRemovedObjectsDataLookup& actorToRemovedObjectsData, ISerializeModifier* modifier);
|
||||
static void Deserialize(Context& context, SceneObject* obj, ISerializable::DeserializeStream& stream);
|
||||
|
||||
/// <summary>
|
||||
/// Handles the object deserialization error.
|
||||
@@ -56,7 +59,64 @@ public:
|
||||
/// <returns>The created actor, or null if failed.</returns>
|
||||
static Actor* CreateActor(int32 typeId, const Guid& id);
|
||||
|
||||
public:
|
||||
|
||||
struct PrefabSyncData
|
||||
{
|
||||
friend SceneObjectsFactory;
|
||||
// The created scene objects. Collection can be modified (eg. for spawning missing objects).
|
||||
Array<SceneObject*>& SceneObjects;
|
||||
// The scene objects data.
|
||||
const ISerializable::DeserializeStream& Data;
|
||||
// The objects deserialization modifier. Collection will be modified (eg. for spawned objects mapping).
|
||||
ISerializeModifier* Modifier;
|
||||
|
||||
PrefabSyncData(Array<SceneObject*>& sceneObjects, const ISerializable::DeserializeStream& data, ISerializeModifier* modifier);
|
||||
|
||||
private:
|
||||
struct NewObj
|
||||
{
|
||||
Prefab* Prefab;
|
||||
const ISerializable::DeserializeStream* PrefabData;
|
||||
Guid PrefabObjectId;
|
||||
Guid Id;
|
||||
};
|
||||
|
||||
int32 InitialCount;
|
||||
Array<NewObj> NewObjects;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the prefab instances inside the scene objects for proper references deserialization.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Should be called after spawning scene objects but before scene objects deserialization.
|
||||
/// </remarks>
|
||||
/// <param name="context">The serialization context.</param>
|
||||
/// <param name="data">The sync data.</param>
|
||||
static void SetupPrefabInstances(Context& context, PrefabSyncData& data);
|
||||
|
||||
/// <summary>
|
||||
/// Synchronizes the new prefab instances by spawning missing objects that were added to prefab but were not saved with scene objects collection.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Should be called after spawning scene objects but before scene objects deserialization and PostLoad event when scene objects hierarchy is ready (parent-child relation exists). But call it before Init and BeginPlay events.
|
||||
/// </remarks>
|
||||
/// <param name="context">The serialization context.</param>
|
||||
/// <param name="data">The sync data.</param>
|
||||
static void SynchronizeNewPrefabInstances(Context& context, PrefabSyncData& data);
|
||||
|
||||
/// <summary>
|
||||
/// Synchronizes the prefab instances. Prefabs may have objects removed so deserialized instances need to synchronize with it. Handles also changing prefab object parent in the instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Should be called after scene objects deserialization and PostLoad event when scene objects hierarchy is ready (parent-child relation exists). But call it before Init and BeginPlay events.
|
||||
/// </remarks>
|
||||
/// <param name="context">The serialization context.</param>
|
||||
/// <param name="data">The sync data.</param>
|
||||
static void SynchronizePrefabInstances(Context& context, PrefabSyncData& data);
|
||||
|
||||
private:
|
||||
|
||||
static void SynchronizeNewPrefabInstance(Prefab* prefab, Actor* actor, const Guid& prefabObjectId, Array<SceneObject*>& sceneObjects, ISerializeModifier* modifier);
|
||||
static void SynchronizeNewPrefabInstance(Context& context, PrefabSyncData& data, Prefab* prefab, Actor* actor, const Guid& prefabObjectId);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user