Add preserving existing Model Prefab objects when reimporting asset

#3122
This commit is contained in:
Wojtek Figat
2025-03-16 12:33:23 +01:00
parent 4a769265b6
commit 05e3149ef4

View File

@@ -24,6 +24,7 @@
#include "Engine/Level/Scripts/ModelPrefab.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Utilities/RectPack.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "AssetsImportingManager.h"
@@ -169,6 +170,33 @@ bool SortMeshGroups(IGrouping<StringView, MeshData*> const& i1, IGrouping<String
return i1.GetKey().Compare(i2.GetKey()) < 0;
}
void CloneObject(rapidjson_flax::StringBuffer& buffer, SceneObject* src, SceneObject* dst, bool stripName = false)
{
// Serialize source
buffer.Clear();
CompactJsonWriter writer(buffer);
writer.StartObject();
const void* defaultInstance = src->GetType().GetDefaultInstance();
src->Serialize(writer, defaultInstance);
writer.EndObject();
// Parse json
rapidjson_flax::Document document;
document.Parse(buffer.GetString(), buffer.GetSize());
// Strip unwanted data
document.RemoveMember("ID");
document.RemoveMember("ParentID");
document.RemoveMember("PrefabID");
document.RemoveMember("PrefabObjectID");
if (stripName)
document.RemoveMember("Name");
// Deserialize destination
auto modifier = Cache::ISerializeModifier.Get();
dst->Deserialize(document, &*modifier);
}
CreateAssetResult ImportModel::Import(CreateAssetContext& context)
{
// Get import options
@@ -677,6 +705,8 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, ModelDa
// Create prefab structure
Dictionary<int32, Actor*> nodeToActor;
Dictionary<Guid, SceneObject*> newPrefabObjects; // Maps prefab object id to the restored and linked object
rapidjson_flax::StringBuffer jsonBuffer;
Array<Actor*> nodeActors;
Actor* rootActor = nullptr;
for (int32 nodeIndex = 0; nodeIndex < data.Nodes.Count(); nodeIndex++)
@@ -767,7 +797,6 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, ModelDa
// Link with object from prefab (if reimporting)
if (prefab)
{
rapidjson_flax::StringBuffer buffer;
for (Actor* a : nodeActors)
{
for (const auto& i : prefab->ObjectsCache)
@@ -779,33 +808,12 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, ModelDa
continue;
// Preserve local changes made in the prefab
{
// Serialize
buffer.Clear();
CompactJsonWriter writer(buffer);
writer.StartObject();
const void* defaultInstance = o->GetType().GetDefaultInstance();
o->Serialize(writer, defaultInstance);
writer.EndObject();
// Parse json
rapidjson_flax::Document document;
document.Parse(buffer.GetString(), buffer.GetSize());
// Strip unwanted data
document.RemoveMember("ID");
document.RemoveMember("ParentID");
document.RemoveMember("PrefabID");
document.RemoveMember("PrefabObjectID");
document.RemoveMember("Name");
// Deserialize object
auto modifier = Cache::ISerializeModifier.Get();
a->Deserialize(document, &*modifier);
}
CloneObject(jsonBuffer, o, a, true);
// Mark as this object already exists in prefab so will be preserved when updating it
a->LinkPrefab(o->GetPrefabID(), o->GetPrefabObjectID());
const Guid prefabObjectId = o->GetPrefabObjectID();
a->LinkPrefab(o->GetPrefabID(), prefabObjectId);
newPrefabObjects.Add(prefabObjectId, a);
break;
}
}
@@ -826,12 +834,43 @@ CreateAssetResult ImportModel::CreatePrefab(CreateAssetContext& context, ModelDa
{
if (i.Value->GetTypeHandle() == modelPrefabScript->GetTypeHandle())
{
modelPrefabScript->LinkPrefab(i.Value->GetPrefabID(), i.Value->GetPrefabObjectID());
const Guid prefabObjectId = i.Value->GetPrefabObjectID();
modelPrefabScript->LinkPrefab(i.Value->GetPrefabID(), prefabObjectId);
newPrefabObjects.Add(prefabObjectId, modelPrefabScript);
break;
}
}
}
}
if (prefab)
{
// Preserve existing objects added by user (eg. colliders, sfx, vfx, scripts)
for (const auto& i : prefab->ObjectsCache)
{
// Skip already restored objects
const Guid prefabObjectId = i.Key;
if (newPrefabObjects.ContainsKey(prefabObjectId))
continue;
SceneObject* defaultObject = i.Value;
// TODO: ignore objects that were imported previously but not now (eg. mesh was removed from source asset)
// Find parent to link
SceneObject* parent;
if (!newPrefabObjects.TryGet(defaultObject->GetParent()->GetPrefabObjectID(), parent))
continue;
// Duplicate object
SceneObject* restoredObject = (SceneObject*)Scripting::NewObject(defaultObject->GetTypeHandle());
if (!restoredObject)
continue;
CloneObject(jsonBuffer, defaultObject, restoredObject);
restoredObject->SetParent((Actor*)parent);
// Link with existing prefab instance
restoredObject->LinkPrefab(i.Value->GetPrefabID(), prefabObjectId);
newPrefabObjects.Add(prefabObjectId, restoredObject);
}
}
// Create prefab instead of native asset
bool failed;