MetaCore/Source/MetaCoreScene/Private/MetaCoreSceneSerializer.cpp
2026-07-27 17:02:26 +08:00

1552 lines
103 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "MetaCoreScene/MetaCoreSceneSerializer.h"
#include "MetaCoreFoundation/MetaCoreReflection.h"
#include "MetaCoreScene/MetaCoreSceneWorld.h"
#include <nlohmann/json.hpp>
#include <fstream>
#include <iostream>
#include <sstream>
#include <span>
#include <unordered_map>
using json = nlohmann::json;
namespace MetaCore {
// 辅助方法:将 glm::vec3 序列化为 JSON 数组 [x, y, z]
static json Vec3ToJson(const glm::vec3& vec) {
return { vec.x, vec.y, vec.z };
}
// 辅助方法:从 JSON 数组 [x, y, z] 反序列化出 glm::vec3
static glm::vec3 JsonToVec3(const json& j) {
if (j.is_array() && j.size() >= 3) {
return glm::vec3(j[0].get<float>(), j[1].get<float>(), j[2].get<float>());
}
return glm::vec3(0.0f);
}
// 辅助方法:将 MetaCoreAssetGuid 转换为 String方便文本化存储
static std::string GuidToString(const MetaCoreAssetGuid& guid) {
return guid.IsValid() ? guid.ToString() : "";
}
// 辅助方法:从 String 反解析为 MetaCoreAssetGuid
static MetaCoreAssetGuid StringToGuid(const std::string& str) {
return str.empty() ? MetaCoreAssetGuid{} : MetaCoreAssetGuid::Parse(str).value_or(MetaCoreAssetGuid{});
}
static json ObjectReferenceToJson(const MetaCoreObjectReference& reference) {
return {
{"SceneGuid", GuidToString(reference.SceneGuid)},
{"StableObjectGuid", GuidToString(reference.StableObjectGuid)},
{"Strength", static_cast<int>(reference.Strength)}
};
}
static MetaCoreObjectReference JsonToObjectReference(const json& value) {
MetaCoreObjectReference result;
if (!value.is_object()) return result;
result.SceneGuid = StringToGuid(value.value("SceneGuid", std::string{}));
result.StableObjectGuid = StringToGuid(value.value("StableObjectGuid", std::string{}));
result.Strength = static_cast<MetaCoreObjectReferenceStrength>(value.value("Strength", 0));
return result;
}
template <typename T>
static std::string ReflectedValueToHex(const T& value, const MetaCoreTypeRegistry& registry) {
const auto bytes = MetaCoreSerializeToBytes(value, registry); if (!bytes) return {};
constexpr char digits[] = "0123456789abcdef"; std::string result; result.resize(bytes->size()*2U);
for(std::size_t i=0;i<bytes->size();++i){const auto valueByte=std::to_integer<unsigned int>((*bytes)[i]);result[i*2U]=digits[valueByte>>4U];result[i*2U+1U]=digits[valueByte&15U];}return result;
}
template <typename T>
static std::optional<T> ReflectedValueFromHex(const json& value, const MetaCoreTypeRegistry& registry) {
if(!value.is_string())return std::nullopt;const std::string hex=value.get<std::string>();if((hex.size()&1U)!=0U)return std::nullopt;
const auto nibble=[](char c)->int{if(c>='0'&&c<='9')return c-'0';if(c>='a'&&c<='f')return c-'a'+10;if(c>='A'&&c<='F')return c-'A'+10;return -1;};
std::vector<std::byte> bytes(hex.size()/2U);for(std::size_t i=0;i<bytes.size();++i){const int hi=nibble(hex[i*2U]),lo=nibble(hex[i*2U+1U]);if(hi<0||lo<0)return std::nullopt;bytes[i]=static_cast<std::byte>((hi<<4)|lo);}T result{};if(!MetaCoreDeserializeFromBytes(std::span<const std::byte>(bytes.data(),bytes.size()),result,registry))return std::nullopt;return result;
}
static json SceneEnvironmentToJson(const MetaCoreSceneEnvironmentSettings& environment) {
json environmentJson = json::object();
environmentJson["Source"] = static_cast<int>(environment.Source);
environmentJson["IblKtxPath"] = environment.IblKtxPath.generic_string();
environmentJson["SkyboxKtxPath"] = environment.SkyboxKtxPath.generic_string();
environmentJson["SkyboxVisible"] = environment.SkyboxVisible;
environmentJson["RotationDegrees"] = environment.RotationDegrees;
environmentJson["IndirectLightIntensity"] = environment.IndirectLightIntensity;
environmentJson["SkyboxIntensity"] = environment.SkyboxIntensity;
environmentJson["EnvironmentProfileGuid"] = GuidToString(environment.EnvironmentProfileGuid);
environmentJson["PostProcessProfileGuid"] = GuidToString(environment.PostProcessProfileGuid);
return environmentJson;
}
static MetaCoreSceneEnvironmentSettings JsonToSceneEnvironment(const json& environmentJson) {
MetaCoreSceneEnvironmentSettings environment;
if (!environmentJson.is_object()) {
return environment;
}
if (environmentJson.contains("Source")) environment.Source = static_cast<MetaCoreSceneEnvironmentSource>(environmentJson["Source"].get<int>());
if (environmentJson.contains("IblKtxPath")) environment.IblKtxPath = environmentJson["IblKtxPath"].get<std::string>();
if (environmentJson.contains("SkyboxKtxPath")) environment.SkyboxKtxPath = environmentJson["SkyboxKtxPath"].get<std::string>();
if (environmentJson.contains("SkyboxVisible")) environment.SkyboxVisible = environmentJson["SkyboxVisible"].get<bool>();
if (environmentJson.contains("RotationDegrees")) environment.RotationDegrees = environmentJson["RotationDegrees"].get<float>();
if (environmentJson.contains("IndirectLightIntensity")) environment.IndirectLightIntensity = environmentJson["IndirectLightIntensity"].get<float>();
if (environmentJson.contains("SkyboxIntensity")) environment.SkyboxIntensity = environmentJson["SkyboxIntensity"].get<float>();
if (environmentJson.contains("EnvironmentProfileGuid")) environment.EnvironmentProfileGuid = StringToGuid(environmentJson["EnvironmentProfileGuid"].get<std::string>());
if (environmentJson.contains("PostProcessProfileGuid")) environment.PostProcessProfileGuid = StringToGuid(environmentJson["PostProcessProfileGuid"].get<std::string>());
return environment;
}
// 辅助方法:将单个 MetaCoreGameObjectData 转换为 json 格式
static json GameObjectDataToJson(const MetaCoreGameObjectData& obj, const MetaCoreTypeRegistry& registry) {
json objJson = json::object();
const MetaCoreStructDescriptor* objDesc = registry.FindStruct<MetaCoreGameObjectData>();
if (!objDesc) return objJson;
for (const auto& objField : objDesc->Fields) {
if (objField.Name == "Id") {
objJson["Id"] = obj.Id;
} else if (objField.Name == "ParentId") {
objJson["ParentId"] = obj.ParentId;
} else if (objField.Name == "SceneGuid") {
objJson["SceneGuid"] = GuidToString(obj.SceneGuid);
} else if (objField.Name == "StableObjectGuid") {
objJson["StableObjectGuid"] = GuidToString(obj.StableObjectGuid);
} else if (objField.Name == "Name") {
objJson["Name"] = obj.Name;
} else if (objField.Name == "Transform") {
const MetaCoreStructDescriptor* transDesc = registry.FindStruct<MetaCoreTransformComponent>();
if (transDesc) {
json transJson = json::object();
for (const auto& transField : transDesc->Fields) {
if (transField.Name == "Position") transJson["Position"] = Vec3ToJson(obj.Transform.Position);
else if (transField.Name == "RotationEulerDegrees") transJson["RotationEulerDegrees"] = Vec3ToJson(obj.Transform.RotationEulerDegrees);
else if (transField.Name == "Scale") transJson["Scale"] = Vec3ToJson(obj.Transform.Scale);
}
objJson["Transform"] = transJson;
}
} else if (objField.Name == "MeshRenderer" && obj.MeshRenderer.has_value()) {
const MetaCoreStructDescriptor* meshDesc = registry.FindStruct<MetaCoreMeshRendererComponent>();
if (meshDesc) {
json meshJson = json::object();
const auto& mesh = obj.MeshRenderer.value();
for (const auto& meshField : meshDesc->Fields) {
if (meshField.Name == "MeshSource") meshJson["MeshSource"] = static_cast<int>(mesh.MeshSource);
else if (meshField.Name == "BuiltinMesh") meshJson["BuiltinMesh"] = static_cast<int>(mesh.BuiltinMesh);
else if (meshField.Name == "MeshAssetGuid") meshJson["MeshAssetGuid"] = GuidToString(mesh.MeshAssetGuid);
else if (meshField.Name == "MaterialAssetGuids") {
json matGuids = json::array();
for (const auto& guid : mesh.MaterialAssetGuids) {
matGuids.push_back(GuidToString(guid));
}
meshJson["MaterialAssetGuids"] = matGuids;
}
else if (meshField.Name == "SourceModelAssetGuid") meshJson["SourceModelAssetGuid"] = GuidToString(mesh.SourceModelAssetGuid);
else if (meshField.Name == "SourceModelPath") meshJson["SourceModelPath"] = mesh.SourceModelPath;
else if (meshField.Name == "SourceNodePath") meshJson["SourceNodePath"] = mesh.SourceNodePath;
else if (meshField.Name == "BaseColorTextureGuid") meshJson["BaseColorTextureGuid"] = GuidToString(mesh.BaseColorTextureGuid);
else if (meshField.Name == "MetallicRoughnessTextureGuid") meshJson["MetallicRoughnessTextureGuid"] = GuidToString(mesh.MetallicRoughnessTextureGuid);
else if (meshField.Name == "NormalTextureGuid") meshJson["NormalTextureGuid"] = GuidToString(mesh.NormalTextureGuid);
else if (meshField.Name == "EmissiveTextureGuid") meshJson["EmissiveTextureGuid"] = GuidToString(mesh.EmissiveTextureGuid);
else if (meshField.Name == "AoTextureGuid") meshJson["AoTextureGuid"] = GuidToString(mesh.AoTextureGuid);
else if (meshField.Name == "BaseColorTexturePath") meshJson["BaseColorTexturePath"] = mesh.BaseColorTexturePath;
else if (meshField.Name == "MetallicRoughnessTexturePath") meshJson["MetallicRoughnessTexturePath"] = mesh.MetallicRoughnessTexturePath;
else if (meshField.Name == "NormalTexturePath") meshJson["NormalTexturePath"] = mesh.NormalTexturePath;
else if (meshField.Name == "EmissiveTexturePath") meshJson["EmissiveTexturePath"] = mesh.EmissiveTexturePath;
else if (meshField.Name == "AoTexturePath") meshJson["AoTexturePath"] = mesh.AoTexturePath;
else if (meshField.Name == "DoubleSided") meshJson["DoubleSided"] = mesh.DoubleSided;
else if (meshField.Name == "Metallic") meshJson["Metallic"] = mesh.Metallic;
else if (meshField.Name == "Roughness") meshJson["Roughness"] = mesh.Roughness;
else if (meshField.Name == "AlphaMode") meshJson["AlphaMode"] = static_cast<int>(mesh.AlphaMode);
else if (meshField.Name == "AlphaCutoff") meshJson["AlphaCutoff"] = mesh.AlphaCutoff;
else if (meshField.Name == "EmissiveColor") meshJson["EmissiveColor"] = Vec3ToJson(mesh.EmissiveColor);
else if (meshField.Name == "BaseColor") meshJson["BaseColor"] = Vec3ToJson(mesh.BaseColor);
else if (meshField.Name == "ModelNodeIndex") meshJson["ModelNodeIndex"] = mesh.ModelNodeIndex;
else if (meshField.Name == "SubMeshIndex") meshJson["SubMeshIndex"] = mesh.SubMeshIndex;
else if (meshField.Name == "Visible") meshJson["Visible"] = mesh.Visible;
}
objJson["MeshRenderer"] = meshJson;
}
} else if (objField.Name == "Light" && obj.Light.has_value()) {
const MetaCoreStructDescriptor* lightDesc = registry.FindStruct<MetaCoreLightComponent>();
if (lightDesc) {
json lightJson = json::object();
for (const auto& lightField : lightDesc->Fields) {
if (lightField.Name == "Type") lightJson["Type"] = static_cast<int>(obj.Light->Type);
else if (lightField.Name == "Color") lightJson["Color"] = Vec3ToJson(obj.Light->Color);
else if (lightField.Name == "Intensity") lightJson["Intensity"] = obj.Light->Intensity;
else if (lightField.Name == "Enabled") lightJson["Enabled"] = obj.Light->Enabled;
else if (lightField.Name == "UseColorTemperature") lightJson["UseColorTemperature"] = obj.Light->UseColorTemperature;
else if (lightField.Name == "ColorTemperatureKelvin") lightJson["ColorTemperatureKelvin"] = obj.Light->ColorTemperatureKelvin;
else if (lightField.Name == "Range") lightJson["Range"] = obj.Light->Range;
else if (lightField.Name == "InnerConeDegrees") lightJson["InnerConeDegrees"] = obj.Light->InnerConeDegrees;
else if (lightField.Name == "OuterConeDegrees") lightJson["OuterConeDegrees"] = obj.Light->OuterConeDegrees;
else if (lightField.Name == "FalloffExponent") lightJson["FalloffExponent"] = obj.Light->FalloffExponent;
else if (lightField.Name == "CastShadows") lightJson["CastShadows"] = obj.Light->CastShadows;
else if (lightField.Name == "ShadowBias") lightJson["ShadowBias"] = obj.Light->ShadowBias;
else if (lightField.Name == "ShadowNormalBias") lightJson["ShadowNormalBias"] = obj.Light->ShadowNormalBias;
else if (lightField.Name == "ShadowPriority") lightJson["ShadowPriority"] = obj.Light->ShadowPriority;
else if (lightField.Name == "LightingMask") lightJson["LightingMask"] = obj.Light->LightingMask;
}
objJson["Light"] = lightJson;
}
} else if (objField.Name == "Rotator" && obj.Rotator.has_value()) {
const MetaCoreStructDescriptor* rotatorDesc = registry.FindStruct<MetaCoreRotatorComponent>();
if (rotatorDesc) {
json rotatorJson = json::object();
for (const auto& rotatorField : rotatorDesc->Fields) {
if (rotatorField.Name == "Enabled") rotatorJson["Enabled"] = obj.Rotator->Enabled;
else if (rotatorField.Name == "DegreesPerSecond") rotatorJson["DegreesPerSecond"] = obj.Rotator->DegreesPerSecond;
}
objJson["Rotator"] = rotatorJson;
}
} else if (objField.Name == "Animator" && obj.Animator.has_value()) {
const MetaCoreStructDescriptor* animatorDesc = registry.FindStruct<MetaCoreAnimatorComponent>();
if (animatorDesc) {
json animatorJson = json::object();
const auto& animator = obj.Animator.value();
for (const auto& animatorField : animatorDesc->Fields) {
if (animatorField.Name == "Enabled") animatorJson["Enabled"] = animator.Enabled;
else if (animatorField.Name == "ControllerAssetGuid") animatorJson["ControllerAssetGuid"] = GuidToString(animator.ControllerAssetGuid);
else if (animatorField.Name == "AvatarAssetGuid") animatorJson["AvatarAssetGuid"] = GuidToString(animator.AvatarAssetGuid);
else if (animatorField.Name == "UpdateMode") animatorJson["UpdateMode"] = static_cast<int>(animator.UpdateMode);
else if (animatorField.Name == "RootMotionMode") animatorJson["RootMotionMode"] = static_cast<int>(animator.RootMotionMode);
else if (animatorField.Name == "RootMotionTranslationMask") animatorJson["RootMotionTranslationMask"] = Vec3ToJson(animator.RootMotionTranslationMask);
else if (animatorField.Name == "RootMotionRotationMask") animatorJson["RootMotionRotationMask"] = Vec3ToJson(animator.RootMotionRotationMask);
else if (animatorField.Name == "SourceModelAssetGuid" && !animator.ControllerAssetGuid.IsValid()) animatorJson["SourceModelAssetGuid"] = GuidToString(animator.SourceModelAssetGuid);
else if (animatorField.Name == "SourceModelPath" && !animator.ControllerAssetGuid.IsValid()) animatorJson["SourceModelPath"] = animator.SourceModelPath;
else if (animatorField.Name == "ClipIndex" && !animator.ControllerAssetGuid.IsValid()) animatorJson["ClipIndex"] = animator.ClipIndex;
else if (animatorField.Name == "ClipName" && !animator.ControllerAssetGuid.IsValid()) animatorJson["ClipName"] = animator.ClipName;
else if (animatorField.Name == "PlayOnStart") animatorJson["PlayOnStart"] = animator.PlayOnStart;
else if (animatorField.Name == "Loop" && !animator.ControllerAssetGuid.IsValid()) animatorJson["Loop"] = animator.Loop;
else if (animatorField.Name == "Speed") animatorJson["Speed"] = animator.Speed;
}
objJson["Animator"] = std::move(animatorJson);
}
} else if (objField.Name == "Active" && obj.Active.has_value()) {
objJson["Active"] = {{"Active", obj.Active->Active}};
} else if (objField.Name == "TimelineDirector" && obj.TimelineDirector.has_value()) {
const auto& director = *obj.TimelineDirector;
json bindings = json::array();
for (const auto& binding : director.Bindings) bindings.push_back({
{"Slot", binding.Slot},
{"ObjectId", binding.ObjectId},
{"ObjectReference", ObjectReferenceToJson(binding.ObjectReference)}
});
objJson["TimelineDirector"] = {{"Enabled", director.Enabled}, {"TimelineAssetGuid", GuidToString(director.TimelineAssetGuid)},
{"PlayOnStart", director.PlayOnStart}, {"Loop", director.Loop}, {"Speed", director.Speed}, {"Bindings", std::move(bindings)}};
} else if (objField.Name == "Script" && obj.Script.has_value()) {
json scriptJson = json::object();
json instancesJson = json::array();
for (const MetaCoreScriptInstanceData& instance : obj.Script->Instances) {
json instanceJson = json::object();
instanceJson["InstanceId"] = instance.InstanceId;
instanceJson["ScriptTypeId"] = instance.ScriptTypeId;
instanceJson["Enabled"] = instance.Enabled;
instanceJson["FieldsJson"] = instance.FieldsJson;
instancesJson.push_back(std::move(instanceJson));
}
scriptJson["Instances"] = std::move(instancesJson);
objJson["Script"] = std::move(scriptJson);
} else if (objField.Name == "UiRenderer" && obj.UiRenderer.has_value()) {
const MetaCoreStructDescriptor* uiDesc = registry.FindStruct<MetaCoreUiRendererComponent>();
if (uiDesc) {
json uiJson = json::object();
const auto& ui = obj.UiRenderer.value();
for (const auto& uiField : uiDesc->Fields) {
if (uiField.Name == "UiDocumentAssetGuid") uiJson["UiDocumentAssetGuid"] = GuidToString(ui.UiDocumentAssetGuid);
else if (uiField.Name == "RenderMode") uiJson["RenderMode"] = static_cast<int>(ui.RenderMode);
else if (uiField.Name == "Visible") uiJson["Visible"] = ui.Visible;
else if (uiField.Name == "InputEnabled") uiJson["InputEnabled"] = ui.InputEnabled;
else if (uiField.Name == "FaceCamera") uiJson["FaceCamera"] = ui.FaceCamera;
else if (uiField.Name == "Layer") uiJson["Layer"] = ui.Layer;
else if (uiField.Name == "PixelsPerUnit") uiJson["PixelsPerUnit"] = ui.PixelsPerUnit;
else if (uiField.Name == "WorldSize") uiJson["WorldSize"] = Vec3ToJson(ui.WorldSize);
}
objJson["UiRenderer"] = std::move(uiJson);
}
} else if (objField.Name == "Interactable" && obj.Interactable.has_value()) {
objJson["Interactable"] = {
{"Enabled", obj.Interactable->Enabled},
{"EventMask", obj.Interactable->EventMask},
{"DragThresholdPixels", obj.Interactable->DragThresholdPixels}
};
} else if (objField.Name == "Collider" && obj.Collider.has_value()) {
const auto& value = *obj.Collider;
objJson["Collider"] = {{"Enabled", value.Enabled}, {"Shape", static_cast<int>(value.Shape)},
{"Center", Vec3ToJson(value.Center)}, {"Size", Vec3ToJson(value.Size)}, {"Radius", value.Radius},
{"Height", value.Height}, {"MeshAssetGuid", GuidToString(value.MeshAssetGuid)},
{"MaterialAssetGuid", GuidToString(value.MaterialAssetGuid)}, {"CollisionLayer", value.CollisionLayer},
{"IsTrigger", value.IsTrigger}};
} else if (objField.Name == "RigidBody" && obj.RigidBody.has_value()) {
const auto& value = *obj.RigidBody;
objJson["RigidBody"] = {{"BodyType", static_cast<int>(value.BodyType)}, {"Mass", value.Mass},
{"LinearDamping", value.LinearDamping}, {"AngularDamping", value.AngularDamping},
{"GravityScale", value.GravityScale}, {"InitialLinearVelocity", Vec3ToJson(value.InitialLinearVelocity)},
{"InitialAngularVelocity", Vec3ToJson(value.InitialAngularVelocity)}, {"AllowSleep", value.AllowSleep},
{"ContinuousCollisionDetection", value.ContinuousCollisionDetection}, {"LockedAxes", value.LockedAxes}};
} else if (objField.Name == "PhysicsConstraint" && obj.PhysicsConstraint.has_value()) {
const auto& value = *obj.PhysicsConstraint;
objJson["PhysicsConstraint"] = {{"Enabled", value.Enabled}, {"Type", static_cast<int>(value.Type)},
{"TargetObjectId", value.TargetObjectId}, {"TargetObjectReference", ObjectReferenceToJson(value.TargetObjectReference)},
{"Anchor", Vec3ToJson(value.Anchor)},
{"TargetAnchor", Vec3ToJson(value.TargetAnchor)}, {"Axis", Vec3ToJson(value.Axis)},
{"LinearLowerLimit", Vec3ToJson(value.LinearLowerLimit)}, {"LinearUpperLimit", Vec3ToJson(value.LinearUpperLimit)},
{"AngularLowerLimit", Vec3ToJson(value.AngularLowerLimit)}, {"AngularUpperLimit", Vec3ToJson(value.AngularUpperLimit)},
{"MotorEnabled", value.MotorEnabled}, {"MotorTargetVelocity", value.MotorTargetVelocity},
{"MotorMaxImpulse", value.MotorMaxImpulse}, {"BreakForce", value.BreakForce},
{"BreakTorque", value.BreakTorque}, {"EnableConnectedCollision", value.EnableConnectedCollision}};
} else if (objField.Name == "CharacterController" && obj.CharacterController.has_value()) {
const auto& value = *obj.CharacterController;
objJson["CharacterController"] = {{"Enabled", value.Enabled}, {"Radius", value.Radius},
{"Height", value.Height}, {"SkinWidth", value.SkinWidth}, {"MaxSlopeDegrees", value.MaxSlopeDegrees},
{"StepHeight", value.StepHeight}, {"GravityScale", value.GravityScale}, {"MaxFallSpeed", value.MaxFallSpeed},
{"PushForce", value.PushForce}, {"CollisionLayer", value.CollisionLayer}};
} else if (objField.Name == "ReflectionProbe" && obj.ReflectionProbe) objJson["ReflectionProbe"] = ReflectedValueToHex(*obj.ReflectionProbe, registry);
else if (objField.Name == "LodGroup" && obj.LodGroup) objJson["LodGroup"] = ReflectedValueToHex(*obj.LodGroup, registry);
else if (objField.Name == "ParticleEmitter" && obj.ParticleEmitter) objJson["ParticleEmitter"] = ReflectedValueToHex(*obj.ParticleEmitter, registry);
else if (objField.Name == "LineRenderer" && obj.LineRenderer) objJson["LineRenderer"] = ReflectedValueToHex(*obj.LineRenderer, registry);
else if (objField.Name == "TrailRenderer" && obj.TrailRenderer) objJson["TrailRenderer"] = ReflectedValueToHex(*obj.TrailRenderer, registry);
else if (objField.Name == "DecalProjector" && obj.DecalProjector) objJson["DecalProjector"] = ReflectedValueToHex(*obj.DecalProjector, registry);
else if (objField.Name == "Terrain" && obj.Terrain) objJson["Terrain"] = ReflectedValueToHex(*obj.Terrain, registry);
else if (objField.Name == "Camera" && obj.Camera.has_value()) {
const MetaCoreStructDescriptor* camDesc = registry.FindStruct<MetaCoreCameraComponent>();
if (camDesc) {
json camJson = json::object();
for (const auto& camField : camDesc->Fields) {
if (camField.Name == "FieldOfViewDegrees") camJson["FieldOfViewDegrees"] = obj.Camera->FieldOfViewDegrees;
else if (camField.Name == "NearClip") camJson["NearClip"] = obj.Camera->NearClip;
else if (camField.Name == "FarClip") camJson["FarClip"] = obj.Camera->FarClip;
else if (camField.Name == "IsPrimary") camJson["IsPrimary"] = obj.Camera->IsPrimary;
else if (camField.Name == "Enabled") camJson["Enabled"] = obj.Camera->Enabled;
else if (camField.Name == "ProjectionMode") camJson["ProjectionMode"] = static_cast<int>(obj.Camera->ProjectionMode);
else if (camField.Name == "OrthographicSize") camJson["OrthographicSize"] = obj.Camera->OrthographicSize;
else if (camField.Name == "Depth") camJson["Depth"] = obj.Camera->Depth;
else if (camField.Name == "CullingMask") camJson["CullingMask"] = obj.Camera->CullingMask;
else if (camField.Name == "ClearFlags") camJson["ClearFlags"] = static_cast<int>(obj.Camera->ClearFlags);
else if (camField.Name == "BackgroundColor") camJson["BackgroundColor"] = Vec3ToJson(obj.Camera->BackgroundColor);
else if (camField.Name == "BackgroundAlpha") camJson["BackgroundAlpha"] = obj.Camera->BackgroundAlpha;
else if (camField.Name == "PostProcessProfileGuid") camJson["PostProcessProfileGuid"] = GuidToString(obj.Camera->PostProcessProfileGuid);
}
objJson["Camera"] = camJson;
}
} else if (objField.Name == "PrefabInstance" && obj.PrefabInstance.has_value()) {
const MetaCoreStructDescriptor* prefabDesc = registry.FindStruct<MetaCorePrefabInstanceMetadata>();
if (prefabDesc) {
json prefabJson = json::object();
for (const auto& prefabField : prefabDesc->Fields) {
if (prefabField.Name == "PrefabAssetGuid") prefabJson["PrefabAssetGuid"] = GuidToString(obj.PrefabInstance->PrefabAssetGuid);
else if (prefabField.Name == "PrefabObjectId") prefabJson["PrefabObjectId"] = obj.PrefabInstance->PrefabObjectId;
else if (prefabField.Name == "PrefabInstanceRootId") prefabJson["PrefabInstanceRootId"] = obj.PrefabInstance->PrefabInstanceRootId;
else if (prefabField.Name == "InstanceGuid") prefabJson["InstanceGuid"] = GuidToString(obj.PrefabInstance->InstanceGuid);
else if (prefabField.Name == "PrefabNodeGuid") prefabJson["PrefabNodeGuid"] = GuidToString(obj.PrefabInstance->PrefabNodeGuid);
}
objJson["PrefabInstance"] = prefabJson;
}
}
}
return objJson;
}
// 辅助方法:从 json 格式中恢复单个 MetaCoreGameObjectData
static MetaCoreGameObjectData JsonToGameObjectData(const json& objJson, const MetaCoreTypeRegistry& registry) {
MetaCoreGameObjectData obj;
const MetaCoreStructDescriptor* objDesc = registry.FindStruct<MetaCoreGameObjectData>();
if (!objDesc) return obj;
for (const auto& objField : objDesc->Fields) {
if (objField.Name == "Id" && objJson.contains("Id")) {
obj.Id = objJson["Id"].get<MetaCoreId>();
} else if (objField.Name == "ParentId" && objJson.contains("ParentId")) {
obj.ParentId = objJson["ParentId"].get<MetaCoreId>();
} else if (objField.Name == "SceneGuid" && objJson.contains("SceneGuid")) {
obj.SceneGuid = StringToGuid(objJson["SceneGuid"].get<std::string>());
} else if (objField.Name == "StableObjectGuid" && objJson.contains("StableObjectGuid")) {
obj.StableObjectGuid = StringToGuid(objJson["StableObjectGuid"].get<std::string>());
} else if (objField.Name == "Name" && objJson.contains("Name")) {
obj.Name = objJson["Name"].get<std::string>();
} else if (objField.Name == "Transform" && objJson.contains("Transform")) {
const auto& transJson = objJson["Transform"];
const MetaCoreStructDescriptor* transDesc = registry.FindStruct<MetaCoreTransformComponent>();
if (transDesc) {
for (const auto& transField : transDesc->Fields) {
if (transField.Name == "Position" && transJson.contains("Position")) {
obj.Transform.Position = JsonToVec3(transJson["Position"]);
} else if (transField.Name == "RotationEulerDegrees" && transJson.contains("RotationEulerDegrees")) {
obj.Transform.RotationEulerDegrees = JsonToVec3(transJson["RotationEulerDegrees"]);
} else if (transField.Name == "Scale" && transJson.contains("Scale")) {
obj.Transform.Scale = JsonToVec3(transJson["Scale"]);
}
}
}
} else if (objField.Name == "MeshRenderer" && objJson.contains("MeshRenderer")) {
const auto& meshJson = objJson["MeshRenderer"];
const MetaCoreStructDescriptor* meshDesc = registry.FindStruct<MetaCoreMeshRendererComponent>();
if (meshDesc) {
MetaCoreMeshRendererComponent mesh;
for (const auto& meshField : meshDesc->Fields) {
if (meshField.Name == "MeshSource" && meshJson.contains("MeshSource")) mesh.MeshSource = static_cast<MetaCoreMeshSourceKind>(meshJson["MeshSource"].get<int>());
else if (meshField.Name == "BuiltinMesh" && meshJson.contains("BuiltinMesh")) mesh.BuiltinMesh = static_cast<MetaCoreBuiltinMeshType>(meshJson["BuiltinMesh"].get<int>());
else if (meshField.Name == "MeshAssetGuid" && meshJson.contains("MeshAssetGuid")) mesh.MeshAssetGuid = StringToGuid(meshJson["MeshAssetGuid"].get<std::string>());
else if (meshField.Name == "MaterialAssetGuids" && meshJson.contains("MaterialAssetGuids") && meshJson["MaterialAssetGuids"].is_array()) {
for (const auto& mGuidJson : meshJson["MaterialAssetGuids"]) {
mesh.MaterialAssetGuids.push_back(StringToGuid(mGuidJson.get<std::string>()));
}
}
else if (meshField.Name == "SourceModelAssetGuid" && meshJson.contains("SourceModelAssetGuid")) mesh.SourceModelAssetGuid = StringToGuid(meshJson["SourceModelAssetGuid"].get<std::string>());
else if (meshField.Name == "SourceModelPath" && meshJson.contains("SourceModelPath")) mesh.SourceModelPath = meshJson["SourceModelPath"].get<std::string>();
else if (meshField.Name == "SourceNodePath" && meshJson.contains("SourceNodePath")) mesh.SourceNodePath = meshJson["SourceNodePath"].get<std::string>();
else if (meshField.Name == "BaseColorTextureGuid" && meshJson.contains("BaseColorTextureGuid")) mesh.BaseColorTextureGuid = StringToGuid(meshJson["BaseColorTextureGuid"].get<std::string>());
else if (meshField.Name == "MetallicRoughnessTextureGuid" && meshJson.contains("MetallicRoughnessTextureGuid")) mesh.MetallicRoughnessTextureGuid = StringToGuid(meshJson["MetallicRoughnessTextureGuid"].get<std::string>());
else if (meshField.Name == "NormalTextureGuid" && meshJson.contains("NormalTextureGuid")) mesh.NormalTextureGuid = StringToGuid(meshJson["NormalTextureGuid"].get<std::string>());
else if (meshField.Name == "EmissiveTextureGuid" && meshJson.contains("EmissiveTextureGuid")) mesh.EmissiveTextureGuid = StringToGuid(meshJson["EmissiveTextureGuid"].get<std::string>());
else if (meshField.Name == "AoTextureGuid" && meshJson.contains("AoTextureGuid")) mesh.AoTextureGuid = StringToGuid(meshJson["AoTextureGuid"].get<std::string>());
else if (meshField.Name == "BaseColorTexturePath" && meshJson.contains("BaseColorTexturePath")) mesh.BaseColorTexturePath = meshJson["BaseColorTexturePath"].get<std::string>();
else if (meshField.Name == "MetallicRoughnessTexturePath" && meshJson.contains("MetallicRoughnessTexturePath")) mesh.MetallicRoughnessTexturePath = meshJson["MetallicRoughnessTexturePath"].get<std::string>();
else if (meshField.Name == "NormalTexturePath" && meshJson.contains("NormalTexturePath")) mesh.NormalTexturePath = meshJson["NormalTexturePath"].get<std::string>();
else if (meshField.Name == "EmissiveTexturePath" && meshJson.contains("EmissiveTexturePath")) mesh.EmissiveTexturePath = meshJson["EmissiveTexturePath"].get<std::string>();
else if (meshField.Name == "AoTexturePath" && meshJson.contains("AoTexturePath")) mesh.AoTexturePath = meshJson["AoTexturePath"].get<std::string>();
else if (meshField.Name == "DoubleSided" && meshJson.contains("DoubleSided")) mesh.DoubleSided = meshJson["DoubleSided"].get<bool>();
else if (meshField.Name == "Metallic" && meshJson.contains("Metallic")) mesh.Metallic = meshJson["Metallic"].get<float>();
else if (meshField.Name == "Roughness" && meshJson.contains("Roughness")) mesh.Roughness = meshJson["Roughness"].get<float>();
else if (meshField.Name == "AlphaMode" && meshJson.contains("AlphaMode")) mesh.AlphaMode = static_cast<MetaCoreMeshAlphaMode>(meshJson["AlphaMode"].get<int>());
else if (meshField.Name == "AlphaCutoff" && meshJson.contains("AlphaCutoff")) mesh.AlphaCutoff = meshJson["AlphaCutoff"].get<float>();
else if (meshField.Name == "EmissiveColor" && meshJson.contains("EmissiveColor")) mesh.EmissiveColor = JsonToVec3(meshJson["EmissiveColor"]);
else if (meshField.Name == "BaseColor" && meshJson.contains("BaseColor")) mesh.BaseColor = JsonToVec3(meshJson["BaseColor"]);
else if (meshField.Name == "ModelNodeIndex" && meshJson.contains("ModelNodeIndex")) mesh.ModelNodeIndex = meshJson["ModelNodeIndex"].get<std::int32_t>();
else if (meshField.Name == "SubMeshIndex" && meshJson.contains("SubMeshIndex")) mesh.SubMeshIndex = meshJson["SubMeshIndex"].get<std::int32_t>();
else if (meshField.Name == "Visible" && meshJson.contains("Visible")) mesh.Visible = meshJson["Visible"].get<bool>();
}
obj.MeshRenderer = mesh;
}
} else if (objField.Name == "Light" && objJson.contains("Light")) {
const auto& lightJson = objJson["Light"];
const MetaCoreStructDescriptor* lightDesc = registry.FindStruct<MetaCoreLightComponent>();
if (lightDesc) {
MetaCoreLightComponent light;
const bool legacyNormalizedIntensity = !lightJson.contains("Type");
for (const auto& lightField : lightDesc->Fields) {
if (lightField.Name == "Type" && lightJson.contains("Type")) light.Type = static_cast<MetaCoreLightType>(lightJson["Type"].get<int>());
else if (lightField.Name == "Color" && lightJson.contains("Color")) light.Color = JsonToVec3(lightJson["Color"]);
else if (lightField.Name == "Intensity" && lightJson.contains("Intensity")) light.Intensity = lightJson["Intensity"].get<float>();
else if (lightField.Name == "Enabled" && lightJson.contains("Enabled")) light.Enabled = lightJson["Enabled"].get<bool>();
else if (lightField.Name == "UseColorTemperature" && lightJson.contains("UseColorTemperature")) light.UseColorTemperature = lightJson["UseColorTemperature"].get<bool>();
else if (lightField.Name == "ColorTemperatureKelvin" && lightJson.contains("ColorTemperatureKelvin")) light.ColorTemperatureKelvin = lightJson["ColorTemperatureKelvin"].get<float>();
else if (lightField.Name == "Range" && lightJson.contains("Range")) light.Range = lightJson["Range"].get<float>();
else if (lightField.Name == "InnerConeDegrees" && lightJson.contains("InnerConeDegrees")) light.InnerConeDegrees = lightJson["InnerConeDegrees"].get<float>();
else if (lightField.Name == "OuterConeDegrees" && lightJson.contains("OuterConeDegrees")) light.OuterConeDegrees = lightJson["OuterConeDegrees"].get<float>();
else if (lightField.Name == "FalloffExponent" && lightJson.contains("FalloffExponent")) light.FalloffExponent = lightJson["FalloffExponent"].get<float>();
else if (lightField.Name == "CastShadows" && lightJson.contains("CastShadows")) light.CastShadows = lightJson["CastShadows"].get<bool>();
else if (lightField.Name == "ShadowBias" && lightJson.contains("ShadowBias")) light.ShadowBias = lightJson["ShadowBias"].get<float>();
else if (lightField.Name == "ShadowNormalBias" && lightJson.contains("ShadowNormalBias")) light.ShadowNormalBias = lightJson["ShadowNormalBias"].get<float>();
else if (lightField.Name == "ShadowPriority" && lightJson.contains("ShadowPriority")) light.ShadowPriority = lightJson["ShadowPriority"].get<std::int32_t>();
else if (lightField.Name == "LightingMask" && lightJson.contains("LightingMask")) light.LightingMask = lightJson["LightingMask"].get<std::uint32_t>();
}
if (legacyNormalizedIntensity) light.Intensity *= 100000.0F;
obj.Light = light;
}
} else if (objField.Name == "Rotator" && objJson.contains("Rotator")) {
const auto& rotatorJson = objJson["Rotator"];
const MetaCoreStructDescriptor* rotatorDesc = registry.FindStruct<MetaCoreRotatorComponent>();
if (rotatorDesc) {
MetaCoreRotatorComponent rotator;
for (const auto& rotatorField : rotatorDesc->Fields) {
if (rotatorField.Name == "Enabled" && rotatorJson.contains("Enabled")) rotator.Enabled = rotatorJson["Enabled"].get<bool>();
else if (rotatorField.Name == "DegreesPerSecond" && rotatorJson.contains("DegreesPerSecond")) rotator.DegreesPerSecond = rotatorJson["DegreesPerSecond"].get<float>();
}
obj.Rotator = rotator;
}
} else if (objField.Name == "Animator" && objJson.contains("Animator")) {
const auto& animatorJson = objJson["Animator"];
const MetaCoreStructDescriptor* animatorDesc = registry.FindStruct<MetaCoreAnimatorComponent>();
if (animatorDesc) {
MetaCoreAnimatorComponent animator;
for (const auto& animatorField : animatorDesc->Fields) {
if (animatorField.Name == "Enabled" && animatorJson.contains("Enabled")) animator.Enabled = animatorJson["Enabled"].get<bool>();
else if (animatorField.Name == "ControllerAssetGuid" && animatorJson.contains("ControllerAssetGuid")) animator.ControllerAssetGuid = StringToGuid(animatorJson["ControllerAssetGuid"].get<std::string>());
else if (animatorField.Name == "AvatarAssetGuid" && animatorJson.contains("AvatarAssetGuid")) animator.AvatarAssetGuid = StringToGuid(animatorJson["AvatarAssetGuid"].get<std::string>());
else if (animatorField.Name == "UpdateMode" && animatorJson.contains("UpdateMode")) animator.UpdateMode = static_cast<MetaCoreAnimatorUpdateMode>(animatorJson["UpdateMode"].get<int>());
else if (animatorField.Name == "RootMotionMode" && animatorJson.contains("RootMotionMode")) animator.RootMotionMode = static_cast<MetaCoreRootMotionMode>(animatorJson["RootMotionMode"].get<int>());
else if (animatorField.Name == "RootMotionTranslationMask" && animatorJson.contains("RootMotionTranslationMask")) animator.RootMotionTranslationMask = JsonToVec3(animatorJson["RootMotionTranslationMask"]);
else if (animatorField.Name == "RootMotionRotationMask" && animatorJson.contains("RootMotionRotationMask")) animator.RootMotionRotationMask = JsonToVec3(animatorJson["RootMotionRotationMask"]);
else if (animatorField.Name == "SourceModelAssetGuid" && animatorJson.contains("SourceModelAssetGuid")) animator.SourceModelAssetGuid = StringToGuid(animatorJson["SourceModelAssetGuid"].get<std::string>());
else if (animatorField.Name == "SourceModelPath" && animatorJson.contains("SourceModelPath")) animator.SourceModelPath = animatorJson["SourceModelPath"].get<std::string>();
else if (animatorField.Name == "ClipIndex" && animatorJson.contains("ClipIndex")) animator.ClipIndex = animatorJson["ClipIndex"].get<std::int32_t>();
else if (animatorField.Name == "ClipName" && animatorJson.contains("ClipName")) animator.ClipName = animatorJson["ClipName"].get<std::string>();
else if (animatorField.Name == "PlayOnStart" && animatorJson.contains("PlayOnStart")) animator.PlayOnStart = animatorJson["PlayOnStart"].get<bool>();
else if (animatorField.Name == "Loop" && animatorJson.contains("Loop")) animator.Loop = animatorJson["Loop"].get<bool>();
else if (animatorField.Name == "Speed" && animatorJson.contains("Speed")) animator.Speed = animatorJson["Speed"].get<float>();
}
obj.Animator = animator;
}
} else if (objField.Name == "Active" && objJson.contains("Active")) {
MetaCoreActiveComponent active;
active.Active = objJson["Active"].value("Active", true);
obj.Active = active;
} else if (objField.Name == "TimelineDirector" && objJson.contains("TimelineDirector")) {
const auto& value = objJson["TimelineDirector"];
MetaCoreTimelineDirectorComponent director;
director.Enabled = value.value("Enabled", true);
if (value.contains("TimelineAssetGuid")) director.TimelineAssetGuid = StringToGuid(value["TimelineAssetGuid"].get<std::string>());
director.PlayOnStart = value.value("PlayOnStart", true);
director.Loop = value.value("Loop", false);
director.Speed = value.value("Speed", 1.0F);
for (const auto& binding : value.value("Bindings", json::array())) {
MetaCoreTimelineBinding result;
result.Slot = binding.value("Slot", "");
result.ObjectId = binding.value("ObjectId", MetaCoreId{});
if (binding.contains("ObjectReference")) result.ObjectReference = JsonToObjectReference(binding["ObjectReference"]);
director.Bindings.push_back(std::move(result));
}
obj.TimelineDirector = std::move(director);
} else if (objField.Name == "Script" && objJson.contains("Script")) {
const auto& scriptJson = objJson["Script"];
MetaCoreScriptComponent script;
if (scriptJson.contains("Instances") && scriptJson["Instances"].is_array()) {
for (const auto& instanceJson : scriptJson["Instances"]) {
MetaCoreScriptInstanceData instance;
if (instanceJson.contains("InstanceId")) {
instance.InstanceId = instanceJson["InstanceId"].get<std::uint64_t>();
}
if (instanceJson.contains("ScriptTypeId")) {
instance.ScriptTypeId = instanceJson["ScriptTypeId"].get<std::string>();
}
if (instanceJson.contains("Enabled")) {
instance.Enabled = instanceJson["Enabled"].get<bool>();
}
if (instanceJson.contains("FieldsJson")) {
instance.FieldsJson = instanceJson["FieldsJson"].get<std::string>();
}
if (!instance.ScriptTypeId.empty()) {
script.Instances.push_back(std::move(instance));
}
}
}
if (!script.Instances.empty()) {
obj.Script = std::move(script);
}
} else if (objField.Name == "UiRenderer" && objJson.contains("UiRenderer")) {
const auto& uiJson = objJson["UiRenderer"];
const MetaCoreStructDescriptor* uiDesc = registry.FindStruct<MetaCoreUiRendererComponent>();
if (uiDesc) {
MetaCoreUiRendererComponent ui;
for (const auto& uiField : uiDesc->Fields) {
if (uiField.Name == "UiDocumentAssetGuid" && uiJson.contains("UiDocumentAssetGuid")) ui.UiDocumentAssetGuid = StringToGuid(uiJson["UiDocumentAssetGuid"].get<std::string>());
else if (uiField.Name == "RenderMode" && uiJson.contains("RenderMode")) ui.RenderMode = static_cast<MetaCoreUiRenderMode>(uiJson["RenderMode"].get<int>());
else if (uiField.Name == "Visible" && uiJson.contains("Visible")) ui.Visible = uiJson["Visible"].get<bool>();
else if (uiField.Name == "InputEnabled" && uiJson.contains("InputEnabled")) ui.InputEnabled = uiJson["InputEnabled"].get<bool>();
else if (uiField.Name == "FaceCamera" && uiJson.contains("FaceCamera")) ui.FaceCamera = uiJson["FaceCamera"].get<bool>();
else if (uiField.Name == "Layer" && uiJson.contains("Layer")) ui.Layer = uiJson["Layer"].get<std::int32_t>();
else if (uiField.Name == "PixelsPerUnit" && uiJson.contains("PixelsPerUnit")) ui.PixelsPerUnit = uiJson["PixelsPerUnit"].get<float>();
else if (uiField.Name == "WorldSize" && uiJson.contains("WorldSize")) ui.WorldSize = JsonToVec3(uiJson["WorldSize"]);
}
obj.UiRenderer = ui;
}
} else if (objField.Name == "Interactable" && objJson.contains("Interactable")) {
const auto& value = objJson["Interactable"];
MetaCoreInteractableComponent interactable;
if (value.contains("Enabled")) interactable.Enabled = value["Enabled"].get<bool>();
if (value.contains("EventMask")) interactable.EventMask = value["EventMask"].get<std::uint32_t>();
if (value.contains("DragThresholdPixels")) interactable.DragThresholdPixels = value["DragThresholdPixels"].get<float>();
obj.Interactable = interactable;
} else if (objField.Name == "Collider" && objJson.contains("Collider")) {
const auto& value = objJson["Collider"];
MetaCoreColliderComponent component;
component.Enabled = value.value("Enabled", true);
component.Shape = static_cast<MetaCoreColliderShape>(value.value("Shape", 0));
if (value.contains("Center")) component.Center = JsonToVec3(value["Center"]);
if (value.contains("Size")) component.Size = JsonToVec3(value["Size"]);
component.Radius = value.value("Radius", 0.5F); component.Height = value.value("Height", 2.0F);
if (value.contains("MeshAssetGuid")) component.MeshAssetGuid = StringToGuid(value["MeshAssetGuid"].get<std::string>());
if (value.contains("MaterialAssetGuid")) component.MaterialAssetGuid = StringToGuid(value["MaterialAssetGuid"].get<std::string>());
component.CollisionLayer = value.value("CollisionLayer", 0U); component.IsTrigger = value.value("IsTrigger", false);
obj.Collider = component;
} else if (objField.Name == "RigidBody" && objJson.contains("RigidBody")) {
const auto& value = objJson["RigidBody"];
MetaCoreRigidBodyComponent component;
component.BodyType = static_cast<MetaCoreRigidBodyType>(value.value("BodyType", 0));
component.Mass = value.value("Mass", 1.0F); component.LinearDamping = value.value("LinearDamping", 0.0F);
component.AngularDamping = value.value("AngularDamping", 0.05F); component.GravityScale = value.value("GravityScale", 1.0F);
if (value.contains("InitialLinearVelocity")) component.InitialLinearVelocity = JsonToVec3(value["InitialLinearVelocity"]);
if (value.contains("InitialAngularVelocity")) component.InitialAngularVelocity = JsonToVec3(value["InitialAngularVelocity"]);
component.AllowSleep = value.value("AllowSleep", true);
component.ContinuousCollisionDetection = value.value("ContinuousCollisionDetection", false);
component.LockedAxes = value.value("LockedAxes", 0U); obj.RigidBody = component;
} else if (objField.Name == "PhysicsConstraint" && objJson.contains("PhysicsConstraint")) {
const auto& value = objJson["PhysicsConstraint"];
MetaCorePhysicsConstraintComponent component;
component.Enabled = value.value("Enabled", true); component.Type = static_cast<MetaCorePhysicsConstraintType>(value.value("Type", 0));
component.TargetObjectId = value.value("TargetObjectId", MetaCoreId{0});
if (value.contains("TargetObjectReference")) component.TargetObjectReference = JsonToObjectReference(value["TargetObjectReference"]);
if (value.contains("Anchor")) component.Anchor = JsonToVec3(value["Anchor"]);
if (value.contains("TargetAnchor")) component.TargetAnchor = JsonToVec3(value["TargetAnchor"]);
if (value.contains("Axis")) component.Axis = JsonToVec3(value["Axis"]);
if (value.contains("LinearLowerLimit")) component.LinearLowerLimit = JsonToVec3(value["LinearLowerLimit"]);
if (value.contains("LinearUpperLimit")) component.LinearUpperLimit = JsonToVec3(value["LinearUpperLimit"]);
if (value.contains("AngularLowerLimit")) component.AngularLowerLimit = JsonToVec3(value["AngularLowerLimit"]);
if (value.contains("AngularUpperLimit")) component.AngularUpperLimit = JsonToVec3(value["AngularUpperLimit"]);
component.MotorEnabled = value.value("MotorEnabled", false); component.MotorTargetVelocity = value.value("MotorTargetVelocity", 0.0F);
component.MotorMaxImpulse = value.value("MotorMaxImpulse", 0.0F); component.BreakForce = value.value("BreakForce", 0.0F);
component.BreakTorque = value.value("BreakTorque", 0.0F); component.EnableConnectedCollision = value.value("EnableConnectedCollision", false);
obj.PhysicsConstraint = component;
} else if (objField.Name == "CharacterController" && objJson.contains("CharacterController")) {
const auto& value = objJson["CharacterController"];
MetaCoreCharacterControllerComponent component;
component.Enabled = value.value("Enabled", true); component.Radius = value.value("Radius", 0.4F);
component.Height = value.value("Height", 1.8F); component.SkinWidth = value.value("SkinWidth", 0.05F);
component.MaxSlopeDegrees = value.value("MaxSlopeDegrees", 45.0F); component.StepHeight = value.value("StepHeight", 0.3F);
component.GravityScale = value.value("GravityScale", 1.0F); component.MaxFallSpeed = value.value("MaxFallSpeed", 55.0F);
component.PushForce = value.value("PushForce", 1.0F); component.CollisionLayer = value.value("CollisionLayer", 0U);
obj.CharacterController = component;
} else if (objField.Name == "ReflectionProbe" && objJson.contains("ReflectionProbe")) obj.ReflectionProbe = ReflectedValueFromHex<MetaCoreReflectionProbeComponent>(objJson["ReflectionProbe"], registry);
else if (objField.Name == "LodGroup" && objJson.contains("LodGroup")) obj.LodGroup = ReflectedValueFromHex<MetaCoreLodGroupComponent>(objJson["LodGroup"], registry);
else if (objField.Name == "ParticleEmitter" && objJson.contains("ParticleEmitter")) obj.ParticleEmitter = ReflectedValueFromHex<MetaCoreParticleEmitterComponent>(objJson["ParticleEmitter"], registry);
else if (objField.Name == "LineRenderer" && objJson.contains("LineRenderer")) obj.LineRenderer = ReflectedValueFromHex<MetaCoreLineRendererComponent>(objJson["LineRenderer"], registry);
else if (objField.Name == "TrailRenderer" && objJson.contains("TrailRenderer")) obj.TrailRenderer = ReflectedValueFromHex<MetaCoreTrailRendererComponent>(objJson["TrailRenderer"], registry);
else if (objField.Name == "DecalProjector" && objJson.contains("DecalProjector")) obj.DecalProjector = ReflectedValueFromHex<MetaCoreDecalProjectorComponent>(objJson["DecalProjector"], registry);
else if (objField.Name == "Terrain" && objJson.contains("Terrain")) obj.Terrain = ReflectedValueFromHex<MetaCoreTerrainComponent>(objJson["Terrain"], registry);
else if (objField.Name == "Camera" && objJson.contains("Camera")) {
const auto& camJson = objJson["Camera"];
const MetaCoreStructDescriptor* camDesc = registry.FindStruct<MetaCoreCameraComponent>();
if (camDesc) {
MetaCoreCameraComponent cam;
for (const auto& camField : camDesc->Fields) {
if (camField.Name == "FieldOfViewDegrees" && camJson.contains("FieldOfViewDegrees")) cam.FieldOfViewDegrees = camJson["FieldOfViewDegrees"].get<float>();
else if (camField.Name == "NearClip" && camJson.contains("NearClip")) cam.NearClip = camJson["NearClip"].get<float>();
else if (camField.Name == "FarClip" && camJson.contains("FarClip")) cam.FarClip = camJson["FarClip"].get<float>();
else if (camField.Name == "IsPrimary" && camJson.contains("IsPrimary")) cam.IsPrimary = camJson["IsPrimary"].get<bool>();
else if (camField.Name == "Enabled" && camJson.contains("Enabled")) cam.Enabled = camJson["Enabled"].get<bool>();
else if (camField.Name == "ProjectionMode" && camJson.contains("ProjectionMode")) cam.ProjectionMode = static_cast<MetaCoreCameraProjectionMode>(camJson["ProjectionMode"].get<int>());
else if (camField.Name == "OrthographicSize" && camJson.contains("OrthographicSize")) cam.OrthographicSize = camJson["OrthographicSize"].get<float>();
else if (camField.Name == "Depth" && camJson.contains("Depth")) cam.Depth = camJson["Depth"].get<float>();
else if (camField.Name == "CullingMask" && camJson.contains("CullingMask")) cam.CullingMask = camJson["CullingMask"].get<std::uint32_t>();
else if (camField.Name == "ClearFlags" && camJson.contains("ClearFlags")) cam.ClearFlags = static_cast<MetaCoreCameraClearFlags>(camJson["ClearFlags"].get<int>());
else if (camField.Name == "BackgroundColor" && camJson.contains("BackgroundColor")) cam.BackgroundColor = JsonToVec3(camJson["BackgroundColor"]);
else if (camField.Name == "BackgroundAlpha" && camJson.contains("BackgroundAlpha")) cam.BackgroundAlpha = camJson["BackgroundAlpha"].get<float>();
else if (camField.Name == "PostProcessProfileGuid" && camJson.contains("PostProcessProfileGuid")) cam.PostProcessProfileGuid = StringToGuid(camJson["PostProcessProfileGuid"].get<std::string>());
}
obj.Camera = cam;
}
} else if (objField.Name == "PrefabInstance" && objJson.contains("PrefabInstance")) {
const auto& prefabJson = objJson["PrefabInstance"];
const MetaCoreStructDescriptor* prefabDesc = registry.FindStruct<MetaCorePrefabInstanceMetadata>();
if (prefabDesc) {
MetaCorePrefabInstanceMetadata prefab;
for (const auto& prefabField : prefabDesc->Fields) {
if (prefabField.Name == "PrefabAssetGuid" && prefabJson.contains("PrefabAssetGuid")) prefab.PrefabAssetGuid = StringToGuid(prefabJson["PrefabAssetGuid"].get<std::string>());
else if (prefabField.Name == "PrefabObjectId" && prefabJson.contains("PrefabObjectId")) prefab.PrefabObjectId = prefabJson["PrefabObjectId"].get<MetaCoreId>();
else if (prefabField.Name == "PrefabInstanceRootId" && prefabJson.contains("PrefabInstanceRootId")) prefab.PrefabInstanceRootId = prefabJson["PrefabInstanceRootId"].get<MetaCoreId>();
else if (prefabField.Name == "InstanceGuid" && prefabJson.contains("InstanceGuid")) prefab.InstanceGuid = StringToGuid(prefabJson["InstanceGuid"].get<std::string>());
else if (prefabField.Name == "PrefabNodeGuid" && prefabJson.contains("PrefabNodeGuid")) prefab.PrefabNodeGuid = StringToGuid(prefabJson["PrefabNodeGuid"].get<std::string>());
}
obj.PrefabInstance = prefab;
}
}
}
return obj;
}
static json PrefabOverrideToJson(const MetaCorePrefabOverrideDocument& value) {
return {
{"TargetNodeGuid", GuidToString(value.TargetNodeGuid)},
{"ComponentTypeId", value.ComponentTypeId},
{"PropertyPath", value.PropertyPath},
{"Operation", static_cast<int>(value.Operation)},
{"ValueJson", value.ValueJson}
};
}
static MetaCorePrefabOverrideDocument JsonToPrefabOverride(const json& value) {
MetaCorePrefabOverrideDocument result;
result.TargetNodeGuid = StringToGuid(value.value("TargetNodeGuid", std::string{}));
result.ComponentTypeId = value.value("ComponentTypeId", std::string{});
result.PropertyPath = value.value("PropertyPath", std::string{});
result.Operation = static_cast<MetaCorePrefabOverrideOperation>(value.value("Operation", 0));
result.ValueJson = value.value("ValueJson", std::string{});
return result;
}
static json SchemaVersionsToJson(const std::vector<MetaCoreSchemaVersionEntry>& versions) {
json result = json::array();
for (const auto& version : versions) {
result.push_back({{"TypeId", version.TypeId}, {"Version", version.Version}});
}
return result;
}
static std::vector<MetaCoreSchemaVersionEntry> JsonToSchemaVersions(const json& values) {
std::vector<MetaCoreSchemaVersionEntry> result;
if (!values.is_array()) return result;
for (const auto& value : values) {
result.push_back({value.value("TypeId", std::string{}), value.value("Version", 1U)});
}
return result;
}
static MetaCoreAssetGuid BuildLegacyDocumentGuid(std::string_view kind, std::string_view name) {
// Legacy documents do not carry an asset GUID. Derive one from persisted
// content so repeated migration/save runs are deterministic.
std::uint64_t first = 1469598103934665603ULL;
std::uint64_t second = 1099511628211ULL;
const auto mix = [&](std::string_view value) {
for (const unsigned char character : value) {
first = (first ^ character) * 1099511628211ULL;
second = (second + character + 0x9e3779b97f4a7c15ULL) * 14029467366897019727ULL;
}
};
mix(kind);
mix(name);
MetaCoreAssetGuid result;
for (std::size_t index = 0; index < 8; ++index) {
result.Bytes[index] = static_cast<std::uint8_t>(first >> (index * 8U));
result.Bytes[index + 8] = static_cast<std::uint8_t>(second >> (index * 8U));
}
result.Bytes[6] = static_cast<std::uint8_t>((result.Bytes[6] & 0x0FU) | 0x50U);
result.Bytes[8] = static_cast<std::uint8_t>((result.Bytes[8] & 0x3FU) | 0x80U);
return result;
}
static void NormalizeSceneDocumentIdentity(MetaCoreSceneDocument& document) {
if (!document.SceneGuid.IsValid()) document.SceneGuid = BuildLegacyDocumentGuid("scene:", document.Name);
std::unordered_map<MetaCoreId, MetaCoreAssetGuid> stableIds;
for (auto& object : document.GameObjects) {
object.SceneGuid = document.SceneGuid;
if (!object.StableObjectGuid.IsValid())
object.StableObjectGuid = MetaCoreBuildDeterministicGuid(document.SceneGuid, object.Id);
stableIds[object.Id] = object.StableObjectGuid;
}
for (auto& object : document.GameObjects) {
if (object.PhysicsConstraint) {
auto& reference = object.PhysicsConstraint->TargetObjectReference;
if (!reference.IsValid()) {
if (const auto target = stableIds.find(object.PhysicsConstraint->TargetObjectId); target != stableIds.end()) {
reference.SceneGuid = document.SceneGuid;
reference.StableObjectGuid = target->second;
reference.Strength = MetaCoreObjectReferenceStrength::Hard;
}
} else if (!reference.SceneGuid.IsValid()) reference.SceneGuid = document.SceneGuid;
}
if (object.TimelineDirector) {
for (auto& binding : object.TimelineDirector->Bindings) {
if (!binding.ObjectReference.IsValid()) {
if (const auto target = stableIds.find(binding.ObjectId); target != stableIds.end()) {
binding.ObjectReference.SceneGuid = document.SceneGuid;
binding.ObjectReference.StableObjectGuid = target->second;
}
} else if (!binding.ObjectReference.SceneGuid.IsValid()) binding.ObjectReference.SceneGuid = document.SceneGuid;
}
}
}
document.SchemaVersion = GMetaCoreSceneSchemaVersion;
}
static void NormalizePrefabDocumentIdentity(MetaCorePrefabDocument& document) {
if (!document.PrefabGuid.IsValid()) document.PrefabGuid = BuildLegacyDocumentGuid("prefab:", document.Name);
for (auto& object : document.GameObjects) {
if (!object.StableObjectGuid.IsValid())
object.StableObjectGuid = MetaCoreBuildDeterministicGuid(document.PrefabGuid, object.Id);
}
document.SchemaVersion = GMetaCorePrefabSchemaVersion;
}
bool MetaCoreSceneSerializer::SaveSceneToJson(
const std::filesystem::path& absolutePath,
const MetaCoreSceneDocument& sceneDocument,
const MetaCoreTypeRegistry& registry
) {
try {
MetaCoreSceneDocument normalized = sceneDocument;
NormalizeSceneDocumentIdentity(normalized);
json sceneJson;
sceneJson["SchemaVersion"] = normalized.SchemaVersion;
sceneJson["SceneGuid"] = GuidToString(normalized.SceneGuid);
// 验证 MetaCoreSceneDocument 反射注册
const MetaCoreStructDescriptor* sceneDesc = registry.FindStruct<MetaCoreSceneDocument>();
if (!sceneDesc) {
std::cerr << "SaveSceneToJson: 未能找到 MetaCoreSceneDocument 反射描述" << std::endl;
return false;
}
for (const auto& field : sceneDesc->Fields) {
if (field.Name == "Name") {
sceneJson["SceneName"] = normalized.Name;
} else if (field.Name == "Environment") {
sceneJson["Environment"] = SceneEnvironmentToJson(normalized.Environment);
} else if (field.Name == "Selection") {
const MetaCoreStructDescriptor* selDesc = registry.FindStruct<MetaCoreEditorSelectionSnapshot>();
if (selDesc) {
json selJson = json::object();
for (const auto& selField : selDesc->Fields) {
if (selField.Name == "SelectedObjectIds") selJson["SelectedObjectIds"] = normalized.Selection.SelectedObjectIds;
else if (selField.Name == "ActiveObjectId") selJson["ActiveObjectId"] = normalized.Selection.ActiveObjectId;
else if (selField.Name == "SelectionAnchorId") selJson["SelectionAnchorId"] = normalized.Selection.SelectionAnchorId;
}
sceneJson["Selection"] = selJson;
}
} else if (field.Name == "GameObjects") {
json gameObjectsArray = json::array();
for (const auto& obj : normalized.GameObjects) {
gameObjectsArray.push_back(GameObjectDataToJson(obj, registry));
}
sceneJson["GameObjects"] = gameObjectsArray;
} else if (field.Name == "SubScenes") {
sceneJson["SubScenes"] = json::array();
for (const auto& subScene : normalized.SubScenes) {
sceneJson["SubScenes"].push_back({
{"SceneGuid", GuidToString(subScene.SceneGuid)},
{"ScenePath", subScene.ScenePath.generic_string()},
{"LoadPolicy", static_cast<int>(subScene.LoadPolicy)},
{"BoundsCenter", Vec3ToJson(subScene.BoundsCenter)},
{"BoundsExtent", Vec3ToJson(subScene.BoundsExtent)},
{"CellCoordinate", Vec3ToJson(subScene.CellCoordinate)},
{"PreloadDistance", subScene.PreloadDistance},
{"UnloadDistance", subScene.UnloadDistance},
{"Priority", subScene.Priority}
});
}
} else if (field.Name == "PrefabInstances") {
sceneJson["PrefabInstances"] = json::array();
for (const auto& instance : normalized.PrefabInstances) {
json overrides = json::array();
for (const auto& entry : instance.Overrides) overrides.push_back(PrefabOverrideToJson(entry));
sceneJson["PrefabInstances"].push_back({
{"InstanceGuid", GuidToString(instance.InstanceGuid)},
{"PrefabAssetGuid", GuidToString(instance.PrefabAssetGuid)},
{"ParentObjectGuid", GuidToString(instance.ParentObjectGuid)},
{"Overrides", std::move(overrides)}
});
}
} else if (field.Name == "ComponentVersions") {
sceneJson["ComponentVersions"] = SchemaVersionsToJson(normalized.ComponentVersions);
}
}
std::ofstream file(absolutePath);
if (!file.is_open()) {
std::cerr << "SaveSceneToJson: 无法打开文件写入 " << absolutePath << std::endl;
return false;
}
file << sceneJson.dump(4);
return true;
} catch (const std::exception& e) {
std::cerr << "SaveSceneToJson: 发生异常 - " << e.what() << std::endl;
return false;
}
}
std::optional<MetaCoreSceneDocument> MetaCoreSceneSerializer::LoadSceneFromJson(
const std::filesystem::path& absolutePath,
const MetaCoreTypeRegistry& registry
) {
try {
std::ifstream file(absolutePath);
if (!file.is_open()) {
std::cerr << "LoadSceneFromJson: 无法打开文件进行读取 " << absolutePath << std::endl;
return std::nullopt;
}
json sceneJson;
file >> sceneJson;
MetaCoreSceneDocument sceneDocument;
const std::uint32_t sourceVersion = sceneJson.value("SchemaVersion", 1U);
if (sourceVersion > GMetaCoreSceneSchemaVersion) return std::nullopt;
sceneDocument.SchemaVersion = sourceVersion;
if (sceneJson.contains("SceneGuid")) sceneDocument.SceneGuid = StringToGuid(sceneJson["SceneGuid"].get<std::string>());
// 验证 MetaCoreSceneDocument 反射注册
const MetaCoreStructDescriptor* sceneDesc = registry.FindStruct<MetaCoreSceneDocument>();
if (!sceneDesc) {
std::cerr << "LoadSceneFromJson: 未能找到 MetaCoreSceneDocument 反射描述" << std::endl;
return std::nullopt;
}
for (const auto& field : sceneDesc->Fields) {
if (field.Name == "Name" && sceneJson.contains("SceneName")) {
sceneDocument.Name = sceneJson["SceneName"].get<std::string>();
} else if (field.Name == "Environment" && sceneJson.contains("Environment")) {
sceneDocument.Environment = JsonToSceneEnvironment(sceneJson["Environment"]);
} else if (field.Name == "Selection" && sceneJson.contains("Selection")) {
const auto& selectionJson = sceneJson["Selection"];
const MetaCoreStructDescriptor* selDesc = registry.FindStruct<MetaCoreEditorSelectionSnapshot>();
if (selDesc) {
for (const auto& selField : selDesc->Fields) {
if (selField.Name == "SelectedObjectIds" && selectionJson.contains("SelectedObjectIds")) {
sceneDocument.Selection.SelectedObjectIds = selectionJson["SelectedObjectIds"].get<std::vector<MetaCoreId>>();
} else if (selField.Name == "ActiveObjectId" && selectionJson.contains("ActiveObjectId")) {
sceneDocument.Selection.ActiveObjectId = selectionJson["ActiveObjectId"].get<MetaCoreId>();
} else if (selField.Name == "SelectionAnchorId" && selectionJson.contains("SelectionAnchorId")) {
sceneDocument.Selection.SelectionAnchorId = selectionJson["SelectionAnchorId"].get<MetaCoreId>();
}
}
}
} else if (field.Name == "GameObjects" && sceneJson.contains("GameObjects") && sceneJson["GameObjects"].is_array()) {
for (const auto& objJson : sceneJson["GameObjects"]) {
sceneDocument.GameObjects.push_back(JsonToGameObjectData(objJson, registry));
}
} else if (field.Name == "SubScenes" && sceneJson.contains("SubScenes") && sceneJson["SubScenes"].is_array()) {
for (const auto& value : sceneJson["SubScenes"]) {
MetaCoreSubSceneDocument subScene;
subScene.SceneGuid = StringToGuid(value.value("SceneGuid", std::string{}));
subScene.ScenePath = value.value("ScenePath", std::string{});
subScene.LoadPolicy = static_cast<MetaCoreSubSceneLoadPolicy>(value.value("LoadPolicy", 0));
if (value.contains("BoundsCenter")) subScene.BoundsCenter = JsonToVec3(value["BoundsCenter"]);
if (value.contains("BoundsExtent")) subScene.BoundsExtent = JsonToVec3(value["BoundsExtent"]);
if (value.contains("CellCoordinate")) subScene.CellCoordinate = JsonToVec3(value["CellCoordinate"]);
subScene.PreloadDistance = value.value("PreloadDistance", 100.0F);
subScene.UnloadDistance = value.value("UnloadDistance", 125.0F);
subScene.Priority = value.value("Priority", 0);
sceneDocument.SubScenes.push_back(std::move(subScene));
}
} else if (field.Name == "PrefabInstances" && sceneJson.contains("PrefabInstances") && sceneJson["PrefabInstances"].is_array()) {
for (const auto& value : sceneJson["PrefabInstances"]) {
MetaCorePrefabInstanceDocument instance;
instance.InstanceGuid = StringToGuid(value.value("InstanceGuid", std::string{}));
instance.PrefabAssetGuid = StringToGuid(value.value("PrefabAssetGuid", std::string{}));
instance.ParentObjectGuid = StringToGuid(value.value("ParentObjectGuid", std::string{}));
if (value.contains("Overrides")) for (const auto& entry : value["Overrides"]) instance.Overrides.push_back(JsonToPrefabOverride(entry));
sceneDocument.PrefabInstances.push_back(std::move(instance));
}
} else if (field.Name == "ComponentVersions" && sceneJson.contains("ComponentVersions")) {
sceneDocument.ComponentVersions = JsonToSchemaVersions(sceneJson["ComponentVersions"]);
}
}
NormalizeSceneDocumentIdentity(sceneDocument);
return sceneDocument;
} catch (const std::exception& e) {
std::cerr << "LoadSceneFromJson: 发生异常 - " << e.what() << std::endl;
return std::nullopt;
}
}
// Prefab
bool MetaCoreSceneSerializer::SavePrefabToJson(
const std::filesystem::path& absolutePath,
const MetaCorePrefabDocument& prefabDocument,
const MetaCoreTypeRegistry& registry
) {
try {
MetaCorePrefabDocument normalized = prefabDocument;
NormalizePrefabDocumentIdentity(normalized);
json prefabJson;
prefabJson["SchemaVersion"] = normalized.SchemaVersion;
prefabJson["PrefabGuid"] = GuidToString(normalized.PrefabGuid);
prefabJson["BasePrefabGuid"] = GuidToString(normalized.BasePrefabGuid);
prefabJson["PrefabName"] = normalized.Name;
json gameObjectsArray = json::array();
for (const auto& obj : normalized.GameObjects) {
gameObjectsArray.push_back(GameObjectDataToJson(obj, registry));
}
prefabJson["GameObjects"] = gameObjectsArray;
prefabJson["Overrides"] = json::array();
for (const auto& value : normalized.Overrides) prefabJson["Overrides"].push_back(PrefabOverrideToJson(value));
prefabJson["NestedPrefabs"] = json::array();
for (const auto& nested : normalized.NestedPrefabs) {
json overrides = json::array();
for (const auto& value : nested.Overrides) overrides.push_back(PrefabOverrideToJson(value));
prefabJson["NestedPrefabs"].push_back({
{"MountNodeGuid", GuidToString(nested.MountNodeGuid)},
{"PrefabAssetGuid", GuidToString(nested.PrefabAssetGuid)},
{"InstanceGuid", GuidToString(nested.InstanceGuid)},
{"Overrides", std::move(overrides)}
});
}
prefabJson["ComponentVersions"] = SchemaVersionsToJson(normalized.ComponentVersions);
std::ofstream file(absolutePath);
if (!file.is_open()) {
return false;
}
file << prefabJson.dump(4);
return true;
} catch (...) {
return false;
}
}
std::optional<MetaCorePrefabDocument> MetaCoreSceneSerializer::LoadPrefabFromJson(
const std::filesystem::path& absolutePath,
const MetaCoreTypeRegistry& registry
) {
try {
std::ifstream file(absolutePath);
if (!file.is_open()) {
return std::nullopt;
}
json prefabJson;
file >> prefabJson;
MetaCorePrefabDocument doc;
const std::uint32_t sourceVersion = prefabJson.value("SchemaVersion", 1U);
if (sourceVersion > GMetaCorePrefabSchemaVersion) return std::nullopt;
doc.SchemaVersion = sourceVersion;
doc.PrefabGuid = StringToGuid(prefabJson.value("PrefabGuid", std::string{}));
doc.BasePrefabGuid = StringToGuid(prefabJson.value("BasePrefabGuid", std::string{}));
if (prefabJson.contains("PrefabName")) {
doc.Name = prefabJson["PrefabName"].get<std::string>();
}
if (prefabJson.contains("GameObjects") && prefabJson["GameObjects"].is_array()) {
for (const auto& objJson : prefabJson["GameObjects"]) {
doc.GameObjects.push_back(JsonToGameObjectData(objJson, registry));
}
}
if (prefabJson.contains("Overrides")) for (const auto& value : prefabJson["Overrides"]) doc.Overrides.push_back(JsonToPrefabOverride(value));
if (prefabJson.contains("NestedPrefabs")) for (const auto& value : prefabJson["NestedPrefabs"]) {
MetaCoreNestedPrefabDocument nested;
nested.MountNodeGuid = StringToGuid(value.value("MountNodeGuid", std::string{}));
nested.PrefabAssetGuid = StringToGuid(value.value("PrefabAssetGuid", std::string{}));
nested.InstanceGuid = StringToGuid(value.value("InstanceGuid", std::string{}));
if (value.contains("Overrides")) for (const auto& entry : value["Overrides"]) nested.Overrides.push_back(JsonToPrefabOverride(entry));
doc.NestedPrefabs.push_back(std::move(nested));
}
if (prefabJson.contains("ComponentVersions")) doc.ComponentVersions = JsonToSchemaVersions(prefabJson["ComponentVersions"]);
NormalizePrefabDocumentIdentity(doc);
return doc;
} catch (...) {
return std::nullopt;
}
}
// Material
bool MetaCoreSceneSerializer::SaveMaterialToJson(
const std::filesystem::path& absolutePath,
const MetaCoreMaterialAssetDocument& doc,
const MetaCoreTypeRegistry& registry
) {
(void)registry;
try {
MetaCoreMaterialAssetDocument validated = doc;
MetaCoreMaterialTemplateRegistry templates;
if (!templates.ValidateAndMigrate(validated, nullptr)) return false;
json matJson;
matJson["AssetGuid"] = GuidToString(doc.AssetGuid);
matJson["Name"] = doc.Name;
matJson["StableImportKey"] = doc.StableImportKey;
matJson["TemplateId"] = validated.TemplateId;
matJson["TemplateVersion"] = validated.TemplateVersion;
matJson["Parameters"] = json::array();
for(const auto& parameter:validated.Parameters)matJson["Parameters"].push_back({{"Name",parameter.Name},{"Type",static_cast<int>(parameter.Type)},{"FloatValue",parameter.FloatValue},{"BoolValue",parameter.BoolValue},{"VectorValue",Vec3ToJson(parameter.VectorValue)},{"TextureValue",GuidToString(parameter.TextureValue)}});
matJson["ShaderModel"] = static_cast<int>(doc.ShaderModel);
matJson["BaseColor"] = Vec3ToJson(doc.BaseColor);
matJson["BaseColorTexture"] = GuidToString(doc.BaseColorTexture);
matJson["NormalTexture"] = GuidToString(doc.NormalTexture);
matJson["Metallic"] = doc.Metallic;
matJson["Roughness"] = doc.Roughness;
matJson["MetallicRoughnessTexture"] = GuidToString(doc.MetallicRoughnessTexture);
matJson["AoTexture"] = GuidToString(doc.AoTexture);
matJson["EmissiveColor"] = Vec3ToJson(doc.EmissiveColor);
matJson["EmissiveTexture"] = GuidToString(doc.EmissiveTexture);
matJson["AlphaMode"] = static_cast<int>(doc.AlphaMode);
matJson["AlphaCutoff"] = doc.AlphaCutoff;
matJson["DoubleSided"] = doc.DoubleSided;
std::ofstream file(absolutePath);
if (!file.is_open()) {
return false;
}
file << matJson.dump(4);
return true;
} catch (...) {
return false;
}
}
std::optional<MetaCoreMaterialAssetDocument> MetaCoreSceneSerializer::LoadMaterialFromJson(
const std::filesystem::path& absolutePath,
const MetaCoreTypeRegistry& registry
) {
(void)registry;
try {
std::ifstream file(absolutePath);
if (!file.is_open()) {
return std::nullopt;
}
json matJson;
file >> matJson;
MetaCoreMaterialAssetDocument doc;
if (matJson.contains("AssetGuid")) doc.AssetGuid = StringToGuid(matJson["AssetGuid"].get<std::string>());
if (matJson.contains("Name")) doc.Name = matJson["Name"].get<std::string>();
if (matJson.contains("StableImportKey")) doc.StableImportKey = matJson["StableImportKey"].get<std::string>();
if (matJson.contains("ShaderModel")) doc.ShaderModel = static_cast<MetaCoreMaterialShaderModel>(matJson["ShaderModel"].get<int>());
if (matJson.contains("BaseColor")) doc.BaseColor = JsonToVec3(matJson["BaseColor"]);
if (matJson.contains("BaseColorTexture")) doc.BaseColorTexture = StringToGuid(matJson["BaseColorTexture"].get<std::string>());
if (matJson.contains("NormalTexture")) doc.NormalTexture = StringToGuid(matJson["NormalTexture"].get<std::string>());
if (matJson.contains("Metallic")) doc.Metallic = matJson["Metallic"].get<float>();
if (matJson.contains("Roughness")) doc.Roughness = matJson["Roughness"].get<float>();
if (matJson.contains("MetallicRoughnessTexture")) doc.MetallicRoughnessTexture = StringToGuid(matJson["MetallicRoughnessTexture"].get<std::string>());
if (matJson.contains("AoTexture")) doc.AoTexture = StringToGuid(matJson["AoTexture"].get<std::string>());
if (matJson.contains("EmissiveColor")) doc.EmissiveColor = JsonToVec3(matJson["EmissiveColor"]);
if (matJson.contains("EmissiveTexture")) doc.EmissiveTexture = StringToGuid(matJson["EmissiveTexture"].get<std::string>());
if (matJson.contains("AlphaMode")) doc.AlphaMode = static_cast<MetaCoreMaterialAlphaMode>(matJson["AlphaMode"].get<int>());
if (matJson.contains("AlphaCutoff")) doc.AlphaCutoff = matJson["AlphaCutoff"].get<float>();
if (matJson.contains("DoubleSided")) doc.DoubleSided = matJson["DoubleSided"].get<bool>();
if(matJson.contains("TemplateId"))doc.TemplateId=matJson["TemplateId"].get<std::string>();else doc.TemplateId=doc.ShaderModel==MetaCoreMaterialShaderModel::PbrMetalRough?"metacore.pbr":"metacore.unlit";
if(matJson.contains("TemplateVersion"))doc.TemplateVersion=matJson["TemplateVersion"].get<std::uint32_t>();
if(matJson.contains("Parameters")&&matJson["Parameters"].is_array())for(const auto& value:matJson["Parameters"]){MetaCoreMaterialParameterDocument parameter;parameter.Name=value.value("Name",std::string{});parameter.Type=static_cast<MetaCoreMaterialParameterType>(value.value("Type",0));parameter.FloatValue=value.value("FloatValue",0.0F);parameter.BoolValue=value.value("BoolValue",false);if(value.contains("VectorValue"))parameter.VectorValue=JsonToVec3(value["VectorValue"]);if(value.contains("TextureValue"))parameter.TextureValue=StringToGuid(value["TextureValue"].get<std::string>());doc.Parameters.push_back(std::move(parameter));}
MetaCoreMaterialTemplateRegistry templates;if(!templates.ValidateAndMigrate(doc,nullptr))return std::nullopt;
return doc;
} catch (...) {
return std::nullopt;
}
}
// UI 序列化辅助方法
static json RectTransformToJson(const MetaCoreUiRectTransformDocument& trans) {
json j = json::object();
j["AnchorMin"] = Vec3ToJson(trans.AnchorMin);
j["AnchorMax"] = Vec3ToJson(trans.AnchorMax);
j["Pivot"] = Vec3ToJson(trans.Pivot);
j["Position"] = Vec3ToJson(trans.Position);
j["Size"] = Vec3ToJson(trans.Size);
j["MinSize"] = Vec3ToJson(trans.MinSize);
j["MaxSize"] = Vec3ToJson(trans.MaxSize);
j["ClipChildren"] = trans.ClipChildren;
return j;
}
static MetaCoreUiRectTransformDocument JsonToRectTransform(const json& j) {
MetaCoreUiRectTransformDocument trans;
if (j.contains("AnchorMin")) trans.AnchorMin = JsonToVec3(j["AnchorMin"]);
if (j.contains("AnchorMax")) trans.AnchorMax = JsonToVec3(j["AnchorMax"]);
if (j.contains("Pivot")) trans.Pivot = JsonToVec3(j["Pivot"]);
if (j.contains("Position")) trans.Position = JsonToVec3(j["Position"]);
if (j.contains("Size")) trans.Size = JsonToVec3(j["Size"]);
if (j.contains("MinSize")) trans.MinSize = JsonToVec3(j["MinSize"]);
if (j.contains("MaxSize")) trans.MaxSize = JsonToVec3(j["MaxSize"]);
if (j.contains("ClipChildren")) trans.ClipChildren = j["ClipChildren"].get<bool>();
return trans;
}
static json UiStyleToJson(const MetaCoreUiStyleDocument& style) {
json j = json::object();
j["BackgroundColor"] = Vec3ToJson(style.BackgroundColor);
j["TextColor"] = Vec3ToJson(style.TextColor);
j["TintColor"] = Vec3ToJson(style.TintColor);
j["FontSize"] = style.FontSize;
j["Padding"] = Vec3ToJson(style.Padding);
j["HorizontalAlignment"] = static_cast<int>(style.HorizontalAlignment);
j["VerticalAlignment"] = static_cast<int>(style.VerticalAlignment);
j["ImageAssetGuid"] = GuidToString(style.ImageAssetGuid);
j["PreserveAspect"] = style.PreserveAspect;
j["Opacity"] = style.Opacity;
j["BorderRadius"] = style.BorderRadius;
j["ThemeToken"] = style.ThemeToken;
return j;
}
static MetaCoreUiStyleDocument JsonToUiStyle(const json& j) {
MetaCoreUiStyleDocument style;
if (j.contains("BackgroundColor")) style.BackgroundColor = JsonToVec3(j["BackgroundColor"]);
if (j.contains("TextColor")) style.TextColor = JsonToVec3(j["TextColor"]);
if (j.contains("TintColor")) style.TintColor = JsonToVec3(j["TintColor"]);
if (j.contains("FontSize")) style.FontSize = j["FontSize"].get<float>();
if (j.contains("Padding")) style.Padding = JsonToVec3(j["Padding"]);
if (j.contains("HorizontalAlignment")) style.HorizontalAlignment = static_cast<MetaCoreUiHorizontalAlignment>(j["HorizontalAlignment"].get<int>());
if (j.contains("VerticalAlignment")) style.VerticalAlignment = static_cast<MetaCoreUiVerticalAlignment>(j["VerticalAlignment"].get<int>());
if (j.contains("ImageAssetGuid")) style.ImageAssetGuid = StringToGuid(j["ImageAssetGuid"].get<std::string>());
if (j.contains("PreserveAspect")) style.PreserveAspect = j["PreserveAspect"].get<bool>();
if (j.contains("Opacity")) style.Opacity = j["Opacity"].get<float>();
if (j.contains("BorderRadius")) style.BorderRadius = j["BorderRadius"].get<float>();
if (j.contains("ThemeToken")) style.ThemeToken = j["ThemeToken"].get<std::string>();
return style;
}
static json UiBindingToJson(const MetaCoreUiBindingDocument& binding) {
return json{
{"SourcePath", binding.SourcePath},
{"Target", static_cast<int>(binding.Target)},
{"Mode", static_cast<int>(binding.Mode)},
{"Format", binding.Format},
{"Converter", binding.Converter},
{"Fallback", binding.Fallback}
};
}
static MetaCoreUiBindingDocument JsonToUiBinding(const json& j) {
MetaCoreUiBindingDocument binding;
if (j.contains("SourcePath")) binding.SourcePath = j["SourcePath"].get<std::string>();
if (j.contains("Target")) binding.Target = static_cast<MetaCoreUiBindingTarget>(j["Target"].get<int>());
if (j.contains("Mode")) binding.Mode = static_cast<MetaCoreUiBindingMode>(j["Mode"].get<int>());
if (j.contains("Format")) binding.Format = j["Format"].get<std::string>();
if (j.contains("Converter")) binding.Converter = j["Converter"].get<std::string>();
if (j.contains("Fallback")) binding.Fallback = j["Fallback"].get<std::string>();
return binding;
}
static json UiAnimationToJson(const MetaCoreUiAnimationDocument& animation) {
return json{
{"Id", animation.Id},
{"Trigger", static_cast<int>(animation.Trigger)},
{"Property", static_cast<int>(animation.Property)},
{"From", Vec3ToJson(animation.From)},
{"To", Vec3ToJson(animation.To)},
{"DurationSeconds", animation.DurationSeconds},
{"DelaySeconds", animation.DelaySeconds},
{"Easing", static_cast<int>(animation.Easing)}
};
}
static MetaCoreUiAnimationDocument JsonToUiAnimation(const json& j) {
MetaCoreUiAnimationDocument animation;
if (j.contains("Id")) animation.Id = j["Id"].get<std::string>();
if (j.contains("Trigger")) animation.Trigger = static_cast<MetaCoreUiAnimationTrigger>(j["Trigger"].get<int>());
if (j.contains("Property")) animation.Property = static_cast<MetaCoreUiAnimationProperty>(j["Property"].get<int>());
if (j.contains("From")) animation.From = JsonToVec3(j["From"]);
if (j.contains("To")) animation.To = JsonToVec3(j["To"]);
if (j.contains("DurationSeconds")) animation.DurationSeconds = j["DurationSeconds"].get<float>();
if (j.contains("DelaySeconds")) animation.DelaySeconds = j["DelaySeconds"].get<float>();
if (j.contains("Easing")) animation.Easing = static_cast<MetaCoreUiEasing>(j["Easing"].get<int>());
return animation;
}
static json UiListItemToJson(const MetaCoreUiListItemDocument& item) {
return json{{"Id", item.Id}, {"Text", item.Text}, {"Value", item.Value},
{"LocalizationKey", item.LocalizationKey}};
}
static MetaCoreUiListItemDocument JsonToUiListItem(const json& j) {
MetaCoreUiListItemDocument item;
if (j.contains("Id")) item.Id = j["Id"].get<std::string>();
if (j.contains("Text")) item.Text = j["Text"].get<std::string>();
if (j.contains("Value")) item.Value = j["Value"].get<std::string>();
if (j.contains("LocalizationKey")) item.LocalizationKey = j["LocalizationKey"].get<std::string>();
return item;
}
static json UiNodeToJson(const MetaCoreUiNodeDocument& node) {
json j = json::object();
j["Id"] = node.Id;
j["Name"] = node.Name;
j["Type"] = static_cast<int>(node.Type);
j["ParentId"] = node.ParentId;
j["Children"] = node.Children;
j["Visible"] = node.Visible;
j["RectTransform"] = RectTransformToJson(node.RectTransform);
j["Style"] = UiStyleToJson(node.Style);
j["Text"] = node.Text;
j["Interactable"] = node.Interactable;
j["Action"] = node.Action;
j["DataBinding"] = node.DataBinding;
j["DataFormat"] = node.DataFormat;
j["Bindings"] = json::array();
for (const auto& binding : node.Bindings) j["Bindings"].push_back(UiBindingToJson(binding));
j["NavigationOrder"] = node.NavigationOrder;
j["Placeholder"] = node.Placeholder;
j["InputValueType"] = static_cast<int>(node.InputValueType);
j["ReadOnly"] = node.ReadOnly;
j["MaxLength"] = node.MaxLength;
j["SelectedIndex"] = node.SelectedIndex;
j["StaticItems"] = json::array();
for (const auto& item : node.StaticItems) j["StaticItems"].push_back(UiListItemToJson(item));
j["ItemTemplateNodeId"] = node.ItemTemplateNodeId;
j["LocalizationKey"] = node.LocalizationKey;
j["ShowAnimation"] = node.ShowAnimation;
j["HideAnimation"] = node.HideAnimation;
j["Animations"] = json::array();
for (const auto& animation : node.Animations) j["Animations"].push_back(UiAnimationToJson(animation));
return j;
}
static MetaCoreUiNodeDocument JsonToUiNode(const json& j) {
MetaCoreUiNodeDocument node;
if (j.contains("Id")) node.Id = j["Id"].get<std::string>();
if (j.contains("Name")) node.Name = j["Name"].get<std::string>();
if (j.contains("Type")) node.Type = static_cast<MetaCoreUiNodeType>(j["Type"].get<int>());
if (j.contains("ParentId")) node.ParentId = j["ParentId"].get<std::string>();
if (j.contains("Children")) node.Children = j["Children"].get<std::vector<std::string>>();
if (j.contains("Visible")) node.Visible = j["Visible"].get<bool>();
if (j.contains("RectTransform")) node.RectTransform = JsonToRectTransform(j["RectTransform"]);
if (j.contains("Style")) node.Style = JsonToUiStyle(j["Style"]);
if (j.contains("Text")) node.Text = j["Text"].get<std::string>();
if (j.contains("Interactable")) node.Interactable = j["Interactable"].get<bool>();
if (j.contains("Action")) node.Action = j["Action"].get<std::string>();
if (j.contains("DataBinding")) node.DataBinding = j["DataBinding"].get<std::string>();
if (j.contains("DataFormat")) node.DataFormat = j["DataFormat"].get<std::string>();
if (j.contains("Bindings") && j["Bindings"].is_array())
for (const auto& binding : j["Bindings"]) node.Bindings.push_back(JsonToUiBinding(binding));
if (j.contains("NavigationOrder")) node.NavigationOrder = j["NavigationOrder"].get<std::int32_t>();
if (j.contains("Placeholder")) node.Placeholder = j["Placeholder"].get<std::string>();
if (j.contains("InputValueType")) node.InputValueType = static_cast<MetaCoreUiInputValueType>(j["InputValueType"].get<int>());
if (j.contains("ReadOnly")) node.ReadOnly = j["ReadOnly"].get<bool>();
if (j.contains("MaxLength")) node.MaxLength = j["MaxLength"].get<std::int32_t>();
if (j.contains("SelectedIndex")) node.SelectedIndex = j["SelectedIndex"].get<std::int32_t>();
if (j.contains("StaticItems") && j["StaticItems"].is_array())
for (const auto& item : j["StaticItems"]) node.StaticItems.push_back(JsonToUiListItem(item));
if (j.contains("ItemTemplateNodeId")) node.ItemTemplateNodeId = j["ItemTemplateNodeId"].get<std::string>();
if (j.contains("LocalizationKey")) node.LocalizationKey = j["LocalizationKey"].get<std::string>();
if (j.contains("ShowAnimation")) node.ShowAnimation = j["ShowAnimation"].get<std::string>();
if (j.contains("HideAnimation")) node.HideAnimation = j["HideAnimation"].get<std::string>();
if (j.contains("Animations") && j["Animations"].is_array())
for (const auto& animation : j["Animations"]) node.Animations.push_back(JsonToUiAnimation(animation));
return node;
}
bool MetaCoreSceneSerializer::SaveUiToJson(
const std::filesystem::path& absolutePath,
const MetaCoreUiDocument& uiDocument,
const MetaCoreTypeRegistry& registry
) {
(void)registry;
try {
json uiJson;
uiJson["SchemaVersion"] = uiDocument.SchemaVersion;
uiJson["GeneratorVersion"] = uiDocument.GeneratorVersion;
uiJson["Name"] = uiDocument.Name;
uiJson["ReferenceWidth"] = uiDocument.ReferenceWidth;
uiJson["ReferenceHeight"] = uiDocument.ReferenceHeight;
uiJson["ScaleMode"] = static_cast<int>(uiDocument.ScaleMode);
uiJson["MatchWidthOrHeight"] = uiDocument.MatchWidthOrHeight;
uiJson["ReferenceDpi"] = uiDocument.ReferenceDpi;
uiJson["MinScale"] = uiDocument.MinScale;
uiJson["MaxScale"] = uiDocument.MaxScale;
uiJson["UseSafeArea"] = uiDocument.UseSafeArea;
uiJson["MinAspectRatio"] = uiDocument.MinAspectRatio;
uiJson["MaxAspectRatio"] = uiDocument.MaxAspectRatio;
uiJson["ThemeAssetGuid"] = GuidToString(uiDocument.ThemeAssetGuid);
uiJson["FontFamilyAssetGuid"] = GuidToString(uiDocument.FontFamilyAssetGuid);
uiJson["LocalizationAssetGuid"] = GuidToString(uiDocument.LocalizationAssetGuid);
uiJson["DefaultLocale"] = uiDocument.DefaultLocale;
uiJson["FallbackLocale"] = uiDocument.FallbackLocale;
uiJson["BackgroundColor"] = Vec3ToJson(uiDocument.BackgroundColor);
uiJson["BackgroundAlpha"] = uiDocument.BackgroundAlpha;
uiJson["RootNodeIds"] = uiDocument.RootNodeIds;
json nodesArray = json::array();
for (const auto& node : uiDocument.Nodes) {
nodesArray.push_back(UiNodeToJson(node));
}
uiJson["Nodes"] = nodesArray;
std::ofstream file(absolutePath);
if (!file.is_open()) {
return false;
}
file << uiJson.dump(4);
return true;
} catch (...) {
return false;
}
}
std::optional<MetaCoreUiDocument> MetaCoreSceneSerializer::LoadUiFromJson(
const std::filesystem::path& absolutePath,
const MetaCoreTypeRegistry& registry
) {
(void)registry;
try {
std::ifstream file(absolutePath);
if (!file.is_open()) {
return std::nullopt;
}
json uiJson;
file >> uiJson;
MetaCoreUiDocument doc;
if (uiJson.contains("SchemaVersion")) doc.SchemaVersion = uiJson["SchemaVersion"].get<std::uint32_t>(); else doc.SchemaVersion = 1;
if (uiJson.contains("GeneratorVersion")) doc.GeneratorVersion = uiJson["GeneratorVersion"].get<std::uint32_t>();
if (uiJson.contains("Name")) doc.Name = uiJson["Name"].get<std::string>();
if (uiJson.contains("ReferenceWidth")) doc.ReferenceWidth = uiJson["ReferenceWidth"].get<std::int32_t>();
if (uiJson.contains("ReferenceHeight")) doc.ReferenceHeight = uiJson["ReferenceHeight"].get<std::int32_t>();
if (uiJson.contains("ScaleMode")) doc.ScaleMode = static_cast<MetaCoreUiCanvasScaleMode>(uiJson["ScaleMode"].get<int>());
if (uiJson.contains("MatchWidthOrHeight")) doc.MatchWidthOrHeight = uiJson["MatchWidthOrHeight"].get<float>();
if (uiJson.contains("ReferenceDpi")) doc.ReferenceDpi = uiJson["ReferenceDpi"].get<float>();
if (uiJson.contains("MinScale")) doc.MinScale = uiJson["MinScale"].get<float>();
if (uiJson.contains("MaxScale")) doc.MaxScale = uiJson["MaxScale"].get<float>();
if (uiJson.contains("UseSafeArea")) doc.UseSafeArea = uiJson["UseSafeArea"].get<bool>();
if (uiJson.contains("MinAspectRatio")) doc.MinAspectRatio = uiJson["MinAspectRatio"].get<float>();
if (uiJson.contains("MaxAspectRatio")) doc.MaxAspectRatio = uiJson["MaxAspectRatio"].get<float>();
if (uiJson.contains("ThemeAssetGuid")) doc.ThemeAssetGuid = StringToGuid(uiJson["ThemeAssetGuid"].get<std::string>());
if (uiJson.contains("FontFamilyAssetGuid")) doc.FontFamilyAssetGuid = StringToGuid(uiJson["FontFamilyAssetGuid"].get<std::string>());
if (uiJson.contains("LocalizationAssetGuid")) doc.LocalizationAssetGuid = StringToGuid(uiJson["LocalizationAssetGuid"].get<std::string>());
if (uiJson.contains("DefaultLocale")) doc.DefaultLocale = uiJson["DefaultLocale"].get<std::string>();
if (uiJson.contains("FallbackLocale")) doc.FallbackLocale = uiJson["FallbackLocale"].get<std::string>();
if (uiJson.contains("BackgroundColor")) doc.BackgroundColor = JsonToVec3(uiJson["BackgroundColor"]);
if (uiJson.contains("BackgroundAlpha")) doc.BackgroundAlpha = uiJson["BackgroundAlpha"].get<float>();
if (uiJson.contains("RootNodeIds")) doc.RootNodeIds = uiJson["RootNodeIds"].get<std::vector<std::string>>();
if (uiJson.contains("Nodes") && uiJson["Nodes"].is_array()) {
for (const auto& nodeJson : uiJson["Nodes"]) {
doc.Nodes.push_back(JsonToUiNode(nodeJson));
}
}
return doc;
} catch (...) {
return std::nullopt;
}
}
bool MetaCoreSceneSerializer::SaveUiThemeToJson(
const std::filesystem::path& absolutePath,
const MetaCoreUiThemeDocument& document
) {
try {
json root{{"SchemaVersion", document.SchemaVersion}, {"Name", document.Name}};
root["Styles"] = json::array();
for (const auto& style : document.Styles) {
root["Styles"].push_back({
{"Token", style.Token},
{"State", static_cast<int>(style.State)},
{"BackgroundColor", Vec3ToJson(style.BackgroundColor)},
{"TextColor", Vec3ToJson(style.TextColor)},
{"Opacity", style.Opacity},
{"BorderRadius", style.BorderRadius},
{"FontSize", style.FontSize}
});
}
std::ofstream output(absolutePath);
return output.is_open() && static_cast<bool>(output << root.dump(4));
} catch (...) {
return false;
}
}
std::optional<MetaCoreUiThemeDocument> MetaCoreSceneSerializer::LoadUiThemeFromJson(
const std::filesystem::path& absolutePath
) {
try {
std::ifstream input(absolutePath);
if (!input.is_open()) return std::nullopt;
json root;
input >> root;
MetaCoreUiThemeDocument result;
if (root.contains("SchemaVersion")) result.SchemaVersion = root["SchemaVersion"].get<std::uint32_t>();
if (root.contains("Name")) result.Name = root["Name"].get<std::string>();
if (root.contains("Styles") && root["Styles"].is_array()) {
for (const auto& value : root["Styles"]) {
MetaCoreUiThemeStyleDocument style;
if (value.contains("Token")) style.Token = value["Token"].get<std::string>();
if (value.contains("State")) style.State = static_cast<MetaCoreUiVisualState>(value["State"].get<int>());
if (value.contains("BackgroundColor")) style.BackgroundColor = JsonToVec3(value["BackgroundColor"]);
if (value.contains("TextColor")) style.TextColor = JsonToVec3(value["TextColor"]);
if (value.contains("Opacity")) style.Opacity = value["Opacity"].get<float>();
if (value.contains("BorderRadius")) style.BorderRadius = value["BorderRadius"].get<float>();
if (value.contains("FontSize")) style.FontSize = value["FontSize"].get<float>();
result.Styles.push_back(std::move(style));
}
}
return result;
} catch (...) {
return std::nullopt;
}
}
bool MetaCoreSceneSerializer::SaveUiFontFamilyToJson(
const std::filesystem::path& absolutePath,
const MetaCoreUiFontFamilyDocument& document
) {
try {
json root{
{"SchemaVersion", document.SchemaVersion},
{"FamilyName", document.FamilyName},
{"FallbackFamilies", document.FallbackFamilies}
};
root["Faces"] = json::array();
for (const auto& face : document.Faces) {
root["Faces"].push_back({
{"FontAssetGuid", GuidToString(face.FontAssetGuid)},
{"Weight", face.Weight},
{"Italic", face.Italic},
{"RequiredCharacters", face.RequiredCharacters}
});
}
std::ofstream output(absolutePath);
return output.is_open() && static_cast<bool>(output << root.dump(4));
} catch (...) {
return false;
}
}
std::optional<MetaCoreUiFontFamilyDocument> MetaCoreSceneSerializer::LoadUiFontFamilyFromJson(
const std::filesystem::path& absolutePath
) {
try {
std::ifstream input(absolutePath);
if (!input.is_open()) return std::nullopt;
json root;
input >> root;
MetaCoreUiFontFamilyDocument result;
if (root.contains("SchemaVersion")) result.SchemaVersion = root["SchemaVersion"].get<std::uint32_t>();
if (root.contains("FamilyName")) result.FamilyName = root["FamilyName"].get<std::string>();
if (root.contains("FallbackFamilies")) result.FallbackFamilies = root["FallbackFamilies"].get<std::vector<std::string>>();
if (root.contains("Faces") && root["Faces"].is_array()) {
for (const auto& value : root["Faces"]) {
MetaCoreUiFontFaceDocument face;
if (value.contains("FontAssetGuid")) face.FontAssetGuid = StringToGuid(value["FontAssetGuid"].get<std::string>());
if (value.contains("Weight")) face.Weight = value["Weight"].get<std::int32_t>();
if (value.contains("Italic")) face.Italic = value["Italic"].get<bool>();
if (value.contains("RequiredCharacters")) face.RequiredCharacters = value["RequiredCharacters"].get<std::string>();
result.Faces.push_back(std::move(face));
}
}
return result;
} catch (...) {
return std::nullopt;
}
}
bool MetaCoreSceneSerializer::SaveUiLocalizationTableToJson(
const std::filesystem::path& absolutePath,
const MetaCoreUiLocalizationTableDocument& document
) {
try {
json root{
{"SchemaVersion", document.SchemaVersion},
{"DefaultLocale", document.DefaultLocale},
{"FallbackLocale", document.FallbackLocale}
};
root["Locales"] = json::array();
for (const auto& locale : document.Locales) {
json localeJson{{"Locale", locale.Locale}};
localeJson["Entries"] = json::array();
for (const auto& entry : locale.Entries)
localeJson["Entries"].push_back({{"Key", entry.Key}, {"Value", entry.Value}});
root["Locales"].push_back(std::move(localeJson));
}
std::ofstream output(absolutePath);
return output.is_open() && static_cast<bool>(output << root.dump(4));
} catch (...) {
return false;
}
}
std::optional<MetaCoreUiLocalizationTableDocument> MetaCoreSceneSerializer::LoadUiLocalizationTableFromJson(
const std::filesystem::path& absolutePath
) {
try {
std::ifstream input(absolutePath);
if (!input.is_open()) return std::nullopt;
json root;
input >> root;
MetaCoreUiLocalizationTableDocument result;
if (root.contains("SchemaVersion")) result.SchemaVersion = root["SchemaVersion"].get<std::uint32_t>();
if (root.contains("DefaultLocale")) result.DefaultLocale = root["DefaultLocale"].get<std::string>();
if (root.contains("FallbackLocale")) result.FallbackLocale = root["FallbackLocale"].get<std::string>();
if (root.contains("Locales") && root["Locales"].is_array()) {
for (const auto& value : root["Locales"]) {
MetaCoreUiLocalizationLocaleDocument locale;
if (value.contains("Locale")) locale.Locale = value["Locale"].get<std::string>();
if (value.contains("Entries") && value["Entries"].is_array()) {
for (const auto& entryValue : value["Entries"]) {
MetaCoreUiLocalizationEntryDocument entry;
if (entryValue.contains("Key")) entry.Key = entryValue["Key"].get<std::string>();
if (entryValue.contains("Value")) entry.Value = entryValue["Value"].get<std::string>();
locale.Entries.push_back(std::move(entry));
}
}
result.Locales.push_back(std::move(locale));
}
}
return result;
} catch (...) {
return std::nullopt;
}
}
} // namespace MetaCore