Refactored Scene serialization

This commit is contained in:
Simon Lübeß
2024-04-12 19:36:01 +02:00
parent d81471419d
commit df13aa8596
4 changed files with 325 additions and 303 deletions
+3 -3
View File
@@ -1151,7 +1151,7 @@ static class ScriptGlue
char8* rawTypeName = Mono.mono_string_to_utf8(typeName); char8* rawTypeName = Mono.mono_string_to_utf8(typeName);
SerializedObject newObject = new SerializedObject(context.AllObjects, StringView(rawTypeName)); SerializedObject newObject = new SerializedObject(context.Serializer, StringView(rawTypeName));
Mono.mono_free(rawTypeName); Mono.mono_free(rawTypeName);
@@ -1182,9 +1182,9 @@ static class ScriptGlue
objectContext = null; objectContext = null;
Log.EngineLogger.AssertDebug(context.AllObjects.ContainsKey(context.Id)); Log.EngineLogger.AssertDebug(context.Serializer.ContainsId(context.Id));
if (!context.AllObjects.TryGetValue(id, let foundObject)) if (!context.Serializer.TryGetSerializedObject(id, let foundObject))
return; return;
objectContext = Internal.UnsafeCastToPtr(foundObject); objectContext = Internal.UnsafeCastToPtr(foundObject);
@@ -40,7 +40,7 @@ public class ScriptInstanceSerializer
/// Serializes the given script instance. /// Serializes the given script instance.
public void SerializeScriptInstance(ScriptInstance script) public void SerializeScriptInstance(ScriptInstance script)
{ {
SerializedObject object = new SerializedObject(_serializedData, script.ScriptClass.FullName, script.EntityId); SerializedObject object = new SerializedObject(this, script.ScriptClass.FullName, script.EntityId);
object.Serialize(script); object.Serialize(script);
} }
@@ -94,4 +94,14 @@ public class ScriptInstanceSerializer
{ {
return _serializedData.TryGetValue(id, out object); return _serializedData.TryGetValue(id, out object);
} }
public bool ContainsId(UUID id)
{
return _serializedData.ContainsKey(id);
}
internal void AddObject(SerializedObject object)
{
_serializedData.Add(object.Id, object);
}
} }
@@ -9,6 +9,8 @@ using System.Reflection;
namespace GlitchyEngine.Serialization; namespace GlitchyEngine.Serialization;
using internal GlitchyEngine.Scripting;
[BonTarget] [BonTarget]
class SerializedObject class SerializedObject
{ {
@@ -33,18 +35,18 @@ class SerializedObject
public String TypeName ~ delete:append _; public String TypeName ~ delete:append _;
public Dictionary<UUID, SerializedObject> AllObjects; public ScriptInstanceSerializer Serializer;
private List<String> _ownedString = new List<String>() ~ DeleteContainerAndItems!(_); private List<String> _ownedString = new List<String>() ~ DeleteContainerAndItems!(_);
public append Dictionary<StringView, (SerializationType PrimitiveType, FieldData Data)> Fields = .(); public append Dictionary<StringView, (SerializationType PrimitiveType, FieldData Data)> Fields = .();
[AllowAppend] [AllowAppend]
public this(Dictionary<UUID, SerializedObject> allObjects, StringView? typeName, UUID? id = null) public this(ScriptInstanceSerializer serializer, StringView? typeName, UUID? id = null)
{ {
String typeNameCopy = append String(typeName.Value); String typeNameCopy = append String(typeName.Value);
AllObjects = allObjects; Serializer = serializer;
if (id == null) if (id == null)
{ {
@@ -53,14 +55,14 @@ class SerializedObject
{ {
Id = UUID.Create(); Id = UUID.Create();
} }
while (AllObjects.ContainsKey(Id)); while (Serializer.ContainsId(Id));
} }
else else
{ {
Id = id.Value; Id = id.Value;
} }
AllObjects.Add(Id, this); Serializer.AddObject(this);
TypeName = typeNameCopy; TypeName = typeNameCopy;
} }
@@ -364,7 +366,7 @@ class SerializedObject
return name; return name;
} }
public static Result<SerializedObject> BonDeserialize(BonReader reader, Dictionary<UUID, SerializedObject> allObjects, BonEnvironment environment = gBonEnv) public static Result<SerializedObject> BonDeserialize(BonReader reader, ScriptInstanceSerializer scriptSerializer, int sceneFileVersion, BonEnvironment environment = gBonEnv)
{ {
StringView type = Try!(reader.Type()); StringView type = Try!(reader.Type());
@@ -372,7 +374,7 @@ class SerializedObject
Try!(Deserialize.Value<UUID>(reader, "ID", let objectId, environment)); Try!(Deserialize.Value<UUID>(reader, "ID", let objectId, environment));
SerializedObject object = new SerializedObject(allObjects, type, objectId); SerializedObject object = new SerializedObject(scriptSerializer, type, objectId);
while (reader.ObjectHasMore()) while (reader.ObjectHasMore())
{ {
+295 -285
View File
@@ -18,6 +18,8 @@ using internal GlitchyEngine.World;
class SceneSerializer class SceneSerializer
{ {
private const int FileVersion = 1;
private Scene _scene; private Scene _scene;
// Maps from ParentID to ChildEntity // Maps from ParentID to ChildEntity
@@ -32,6 +34,8 @@ class SceneSerializer
private HashSet<UUID> _objectsNotWritten; private HashSet<UUID> _objectsNotWritten;
private int _fileVersion;
public ScriptInstanceSerializer ScriptSerializer => _scriptSerializer; public ScriptInstanceSerializer ScriptSerializer => _scriptSerializer;
public this(Scene scene) public this(Scene scene)
@@ -63,6 +67,8 @@ class SceneSerializer
// TODO: Scene name goes here! // TODO: Scene name goes here!
Serialize.Value(writer, "Name", "Scene name here pls!!!"); Serialize.Value(writer, "Name", "Scene name here pls!!!");
Serialize.Value(writer, "Version", FileVersion);
writer.Identifier("Entities"); writer.Identifier("Entities");
using (writer.ArrayBlock()) using (writer.ArrayBlock())
@@ -280,6 +286,31 @@ class SceneSerializer
Runtime.NotImplemented(); Runtime.NotImplemented();
} }
private Result<void> DeserializeValue<T>(BonReader reader, StringView identifier, out T value, bool tryEndEntry = true, BonEnvironment env = gBonEnv)
{
Try!(Deserialize.Value(reader, identifier, out value, env));
TryEndEntry(reader);
return .Ok;
}
private static bool Check(BonReader reader, char8 token, bool consume = true)
{
return reader.[Friend]Check(token, consume);
}
private static bool TryEndEntry(BonReader reader)
{
if (Check(reader, ',', false))
{
reader.EntryEnd();
return true;
}
return false;
}
public Result<void> Deserialize(StringView filePath, bool loadAsPrefab = false) public Result<void> Deserialize(StringView filePath, bool loadAsPrefab = false)
{ {
Debug.Profiler.ProfileResourceFunction!(); Debug.Profiler.ProfileResourceFunction!();
@@ -299,55 +330,30 @@ class SceneSerializer
_scene.Name = Path.GetFileName(filePath, .. scope .()); _scene.Name = Path.GetFileName(filePath, .. scope .());
// TODO: Scene name goes here! Try!(DeserializeValue(reader, "Version", out _fileVersion));
String testName;
Deserialize.Value(reader, "Name", out testName);
delete testName;
Try!(reader.EntryEnd()); if (_fileVersion == 0 || _fileVersion > FileVersion)
if (Try!(reader.Identifier()) != "Entities")
return .Err;
Try!(reader.ArrayBlock());
bool first = true;
while (reader.ArrayHasMore())
{ {
if (!first) Log.EngineLogger.Warning("The scene file either has no version or it's newer than the current editor. Deserialization might fail.");
{
Try!(reader.EntryEnd());
}
Try!(DeserializeEntity(reader, loadAsPrefab));
first = false;
} }
Try!(reader.ArrayBlockEnd()); while (reader.ObjectHasMore())
Try!(reader.EntryEnd());
if (Try!(reader.Identifier()) != "ReferencedObjects")
return .Err;
Try!(reader.ArrayBlock());
first = true;
while (reader.ArrayHasMore())
{ {
if (!first) StringView identifier = Try!(reader.Identifier());
switch (identifier)
{ {
Try!(reader.EntryEnd()); case "Entities":
Try!(DeserializeEntities(reader, loadAsPrefab));
case "ReferencedObjects":
Try!(DeserializeReferencedObjects(reader));
default:
Log.EngineLogger.Warning($"Encountered unexpected Identifier \"{identifier}\".");
} }
Try!(SerializedObject.BonDeserialize(reader, _scriptSerializer.SerializedObjects, gBonEnv)); TryEndEntry(reader);
first = false;
} }
Try!(reader.ArrayBlockEnd());
Try!(reader.ObjectBlockEnd()); Try!(reader.ObjectBlockEnd());
Try!(Deserialize.End(reader)); Try!(Deserialize.End(reader));
@@ -368,6 +374,38 @@ class SceneSerializer
return .Ok; return .Ok;
} }
private Result<void> DeserializeEntities(BonReader reader, bool loadAsPrefab)
{
Try!(reader.ArrayBlock());
while (reader.ArrayHasMore())
{
Try!(DeserializeEntity(reader, loadAsPrefab));
TryEndEntry(reader);
}
Try!(reader.ArrayBlockEnd());
return .Ok;
}
private Result<void> DeserializeReferencedObjects(BonReader reader)
{
Try!(reader.ArrayBlock());
while (reader.ArrayHasMore())
{
Try!(SerializedObject.BonDeserialize(reader, _scriptSerializer, _fileVersion, gBonEnv));
TryEndEntry(reader);
}
Try!(reader.ArrayBlockEnd());
return .Ok;
}
/// Deserializes the next entity in the file /// Deserializes the next entity in the file
/// @param reader the reader /// @param reader the reader
/// @param replaceId If false, the ID that is stored in the file will be used as ID in the scene. /// @param replaceId If false, the ID that is stored in the file will be used as ID in the scene.
@@ -407,7 +445,8 @@ class SceneSerializer
Try!(reader.ObjectBlock()); Try!(reader.ObjectBlock());
Deserialize.Value<uint64>(reader, "Id", let rawUuid); // We must start with the Id because we need to construct the entity!
DeserializeValue<uint64>(reader, "Id", let rawUuid);
UUID uuid = RemapId(UUID(rawUuid)); UUID uuid = RemapId(UUID(rawUuid));
@@ -415,58 +454,65 @@ class SceneSerializer
while(reader.ObjectHasMore()) while(reader.ObjectHasMore())
{ {
Try!(reader.EntryEnd()); TryEndEntry(reader);
StringView identifier = Try!(reader.Identifier()); StringView componentIdentifier = Try!(reader.Identifier());
switch(identifier) switch(componentIdentifier)
{ {
case "NameComponent": case "NameComponent":
Try!(DeserializeComponent<NameComponent>(reader, entity, scope (component) => Try!(DeserializeComponent<NameComponent>(reader, entity, scope (component) =>
{ {
String name; Try!(Deserialize.Value<String>(reader, "Name", let name));
Deserialize.Value(reader, "Name", out name);
component.Name = name; component.Name = name;
delete name; delete name;
return .Ok; return .Ok;
})); }));
case "SpriteRendererComponent": case "SpriteRendererComponent":
Try!(DeserializeComponent<SpriteRendererComponent>(reader, entity, scope (component) => Try!(DeserializeComponentFields<SpriteRendererComponent>(reader, componentIdentifier, entity, scope (component, fieldIdentifier) =>
{ {
Try!(Deserialize.Value(reader, "Color", out component.Color)); switch(fieldIdentifier)
reader.EntryEnd(); {
Try!(Deserialize.Value(reader, "Sprite", out component.Sprite)); case "Color":
reader.EntryEnd(); Try!(Deserialize.Value(reader, out component.Color));
Try!(Deserialize.Value(reader, "UvTransform", out component.UvTransform)); case "Sprite":
Try!(Deserialize.Value(reader, out component.Sprite));
case "UvTransform":
Try!(Deserialize.Value(reader, out component.UvTransform));
default:
return false;
}
return .Ok; return true;
})); }));
case "CircleRendererComponent": case "CircleRendererComponent":
Try!(DeserializeComponent<CircleRendererComponent>(reader, entity, scope (component) => Try!(DeserializeComponentFields<CircleRendererComponent>(reader, componentIdentifier, entity, scope (component, fieldIdentifier) =>
{ {
Try!(Deserialize.Value(reader, "Color", out component.Color)); switch(fieldIdentifier)
reader.EntryEnd(); {
Try!(Deserialize.Value(reader, "InnerRadius", out component.InnerRadius)); case "Color":
reader.EntryEnd(); Try!(Deserialize.Value(reader, out component.Color));
Try!(Deserialize.Value(reader, "Sprite", out component.Sprite)); case "InnerRadius":
reader.EntryEnd(); Try!(Deserialize.Value(reader, out component.InnerRadius));
Try!(Deserialize.Value(reader, "UvTransform", out component.UvTransform)); case "Sprite":
Try!(Deserialize.Value(reader, out component.Sprite));
case "UvTransform":
Try!(Deserialize.Value(reader, out component.UvTransform));
default:
return false;
}
return .Ok; return true;
})); }));
case "TransformComponent": case "TransformComponent":
Try!(DeserializeComponent<TransformComponent>(reader, entity, scope (component) => Try!(DeserializeComponentFields<TransformComponent>(reader, componentIdentifier, entity, scope (component, fieldIdentifier) =>
{ {
let nextId = Try!(reader.Identifier()); switch(fieldIdentifier)
if (nextId == "ParentId")
{ {
case "ParentId":
UUID rawParentId; UUID rawParentId;
Deserialize.Value(reader, out rawParentId); Try!(Deserialize.Value(reader, out rawParentId));
reader.EntryEnd();
UUID parentId = RemapId(rawParentId); UUID parentId = RemapId(rawParentId);
@@ -480,155 +526,170 @@ class SceneSerializer
{ {
_entitiesMissingParent.Add((entity, parentId)); _entitiesMissingParent.Add((entity, parentId));
} }
case "Position":
Deserialize.Value(reader, "Position", out component.[Friend]_position); Try!(Deserialize.Value(reader, out component.[Friend]_position));
reader.EntryEnd(); case "Rotation":
Try!(Deserialize.Value(reader, out component.[Friend]_rotation));
case "Scale":
Try!(Deserialize.Value(reader, out component.[Friend]_scale));
case "EditorEulerRotation":
Try!(Deserialize.Value(reader, out component.[Friend]_editorRotationEuler));
default:
return false;
} }
else if (nextId == "Position")
{
Deserialize.Value(reader, out component.[Friend]_position);
reader.EntryEnd();
}
else
{
return .Err;
}
Deserialize.Value(reader, "Rotation", out component.[Friend]_rotation);
reader.EntryEnd();
Deserialize.Value(reader, "Scale", out component.[Friend]_scale);
reader.EntryEnd();
Deserialize.Value(reader, "EditorEulerRotation", out component.[Friend]_editorRotationEuler);
component.IsDirty = true; component.IsDirty = true;
return .Ok; return true;
})); }));
case "CameraComponent": case "CameraComponent":
Try!(DeserializeComponent<CameraComponent>(reader, entity, scope (component) => Try!(DeserializeComponentFields<CameraComponent>(reader, componentIdentifier, entity, scope (component, fieldIdentifier) =>
{ {
SceneCamera camera = component.Camera; ref SceneCamera camera = ref component.Camera;
Deserialize.Value(reader, "Primary", out component.Primary); switch(fieldIdentifier)
reader.EntryEnd(); {
case "Primary":
Try!(Deserialize.Value(reader, out component.Primary));
case "ProjectionType":
Try!(Deserialize.Value(reader, out camera.[Friend]_projectionType));
// TODO: Render target case "PerspectiveFovY":
Try!(Deserialize.Value(reader, out camera.[Friend]_perspectiveFovY));
case "PerspectiveNearPlane":
Try!(Deserialize.Value(reader, out camera.[Friend]_perspectiveNearPlane));
case "PerspectiveFarPlane":
Try!(Deserialize.Value(reader, out camera.[Friend]_perspectiveFarPlane));
Deserialize.Value(reader, "ProjectionType", out camera.[Friend]_projectionType); case "OrthographicHeight":
reader.EntryEnd(); Try!(Deserialize.Value(reader, out camera.[Friend]_orthographicHeight));
case "OrthographicNearPlane":
Try!(Deserialize.Value(reader, out camera.[Friend]_orthographicNearPlane));
case "OrthographicFarPlane":
Try!(Deserialize.Value(reader, out camera.[Friend]_orthographicFarPlane));
case "AspectRatio":
Try!(Deserialize.Value(reader, out camera.[Friend]_aspectRatio));
case "FixedAspectRatio":
Try!(Deserialize.Value(reader, out camera.[Friend]_fixedAspectRatio));
default:
return false;
}
Deserialize.Value(reader, "PerspectiveFovY", out camera.[Friend]_perspectiveFovY); return true;
reader.EntryEnd(); }));
Deserialize.Value(reader, "PerspectiveNearPlane", out camera.[Friend]_perspectiveNearPlane);
reader.EntryEnd();
Deserialize.Value(reader, "PerspectiveFarPlane", out camera.[Friend]_perspectiveFarPlane);
reader.EntryEnd();
Deserialize.Value(reader, "OrthographicHeight", out camera.[Friend]_orthographicHeight);
reader.EntryEnd();
Deserialize.Value(reader, "OrthographicNearPlane", out camera.[Friend]_orthographicNearPlane);
reader.EntryEnd();
Deserialize.Value(reader, "OrthographicFarPlane", out camera.[Friend]_orthographicFarPlane);
reader.EntryEnd();
Deserialize.Value(reader, "AspectRatio", out camera.[Friend]_aspectRatio);
reader.EntryEnd();
Deserialize.Value(reader, "FixedAspectRatio", out camera.[Friend]_fixedAspectRatio);
camera.[Friend]CalculateProjection();
return .Ok;
}));
case "LightComponent": case "LightComponent":
Try!(DeserializeComponent<LightComponent>(reader, entity, scope (component) => Try!(DeserializeComponentFields<LightComponent>(reader, componentIdentifier, entity, scope (component, fieldIdentifier) =>
{ {
ref SceneLight light = ref component.SceneLight; ref SceneLight light = ref component.SceneLight;
Deserialize.Value(reader, "LightType", out light.[Friend]_type); switch(fieldIdentifier)
reader.EntryEnd(); {
case "LightType":
Try!(Deserialize.Value(reader, out light.[Friend]_type));
case "Illuminance":
Try!(Deserialize.Value(reader, out light.[Friend]_illuminance));
case "Color":
Try!(Deserialize.Value(reader, out light.[Friend]_color));
default:
return false;
}
Deserialize.Value(reader, "Illuminance", out light.[Friend]_illuminance); return true;
reader.EntryEnd();
Deserialize.Value(reader, "Color", out light.[Friend]_color);
return .Ok;
})); }));
case "Rigidbody2D": case "Rigidbody2D":
Try!(DeserializeComponent<Rigidbody2DComponent>(reader, entity, scope (component) => Try!(DeserializeComponentFields<Rigidbody2DComponent>(reader, componentIdentifier, entity, scope (component, fieldIdentifier) =>
{ {
Rigidbody2DComponent.BodyType bodyType = .Static; switch(fieldIdentifier)
Deserialize.Value(reader, "BodyType", out bodyType); {
component.BodyType = bodyType; case "BodyType":
Rigidbody2DComponent.BodyType bodyType = .Static;
Try!(Deserialize.Value(reader, out bodyType));
component.BodyType = bodyType;
case "FixedRotation":
Try!(Deserialize.Value<bool>(reader, let fixedRotation));
component.FixedRotation = fixedRotation;
case "GravityScale":
Try!(Deserialize.Value<float>(reader, let gravityScale));
component.GravityScale = gravityScale;
default:
return false;
}
reader.EntryEnd(); return true;
bool fixedRotation = false;
Deserialize.Value(reader, "FixedRotation", out fixedRotation);
component.FixedRotation = fixedRotation;
reader.EntryEnd();
Deserialize.Value<float>(reader, "GravityScale", let gravityScale);
component.GravityScale = gravityScale;
return .Ok;
})); }));
case "BoxCollider2D": case "BoxCollider2D":
Try!(DeserializeComponent<BoxCollider2DComponent>(reader, entity, scope (component) => Try!(DeserializeComponentFields<BoxCollider2DComponent>(reader, componentIdentifier, entity, scope (component, fieldIdentifier) =>
{ {
Deserialize.Value(reader, "Offset", out component.Offset); switch(fieldIdentifier)
reader.EntryEnd(); {
Deserialize.Value(reader, "Size", out component.Size); case "Offset":
reader.EntryEnd(); Try!(Deserialize.Value(reader, out component.Offset));
case "Size":
Try!(Deserialize.Value(reader, out component.Size));
Deserialize.Value(reader, "Density", out component.Density); case "Density":
reader.EntryEnd(); Try!(Deserialize.Value(reader, out component.Density));
Deserialize.Value(reader, "Friction", out component.Friction); case "Friction":
reader.EntryEnd(); Try!(Deserialize.Value(reader, out component.Friction));
Deserialize.Value(reader, "Restitution", out component.Restitution); case "Restitution":
reader.EntryEnd(); Try!(Deserialize.Value(reader, out component.Restitution));
Deserialize.Value(reader, "RestitutionThreshold", out component.RestitutionThreshold); case "RestitutionThreshold":
Try!(Deserialize.Value(reader, out component.RestitutionThreshold));
default:
return false;
}
return .Ok; return true;
})); }));
case "CircleCollider2D": case "CircleCollider2D":
Try!(DeserializeComponent<CircleCollider2DComponent>(reader, entity, scope (component) => Try!(DeserializeComponentFields<CircleCollider2DComponent>(reader, componentIdentifier, entity, scope (component, fieldIdentifier) =>
{ {
Deserialize.Value(reader, "Offset", out component.Offset); switch(fieldIdentifier)
reader.EntryEnd(); {
Deserialize.Value(reader, "Radius", out component.Radius); case "Offset":
reader.EntryEnd(); Try!(Deserialize.Value(reader, out component.Offset));
case "Radius":
Try!(Deserialize.Value(reader, out component.Radius));
Deserialize.Value(reader, "Density", out component.Density); case "Density":
reader.EntryEnd(); Try!(Deserialize.Value(reader, out component.Density));
Deserialize.Value(reader, "Friction", out component.Friction); case "Friction":
reader.EntryEnd(); Try!(Deserialize.Value(reader, out component.Friction));
Deserialize.Value(reader, "Restitution", out component.Restitution); case "Restitution":
reader.EntryEnd(); Try!(Deserialize.Value(reader, out component.Restitution));
Deserialize.Value(reader, "RestitutionThreshold", out component.RestitutionThreshold); case "RestitutionThreshold":
Try!(Deserialize.Value(reader, out component.RestitutionThreshold));
default:
return false;
}
return .Ok; return true;
})); }));
case "PolygonCollider2D": case "PolygonCollider2D":
Try!(DeserializeComponent<PolygonCollider2DComponent>(reader, entity, scope (component) => Try!(DeserializeComponentFields<PolygonCollider2DComponent>(reader, componentIdentifier, entity, scope (component, fieldIdentifier) =>
{ {
Deserialize.Value(reader, "Offset", out component.Offset); switch(fieldIdentifier)
reader.EntryEnd(); {
case "Offset":
Try!(Deserialize.Value(reader, out component.Offset));
case "Vertices":
Try!(Deserialize.Value(reader, out component.Vertices));
case "VertexCount":
Try!(Deserialize.Value(reader, out component.VertexCount));
Deserialize.Value(reader, "Vertices", out component.Vertices); case "Density":
reader.EntryEnd(); Try!(Deserialize.Value(reader, out component.Density));
Deserialize.Value(reader, "VertexCount", out component.VertexCount); case "Friction":
reader.EntryEnd(); Try!(Deserialize.Value(reader, out component.Friction));
case "Restitution":
Try!(Deserialize.Value(reader, out component.Restitution));
case "RestitutionThreshold":
Try!(Deserialize.Value(reader, out component.RestitutionThreshold));
default:
return false;
}
return true;
Deserialize.Value(reader, "Density", out component.Density);
reader.EntryEnd();
Deserialize.Value(reader, "Friction", out component.Friction);
reader.EntryEnd();
Deserialize.Value(reader, "Restitution", out component.Restitution);
reader.EntryEnd();
Deserialize.Value(reader, "RestitutionThreshold", out component.RestitutionThreshold);
return .Ok;
})); }));
case "MeshComponent": case "MeshComponent":
Try!(DeserializeComponent<MeshComponent>(reader, entity, scope (component) => Try!(DeserializeComponent<MeshComponent>(reader, entity, scope (component) =>
@@ -645,29 +706,21 @@ class SceneSerializer
return .Ok; return .Ok;
})); }));
case "ScriptComponent": case "ScriptComponent":
Try!(DeserializeComponent<ScriptComponent>(reader, entity, scope (component) => Try!(DeserializeComponentFields<ScriptComponent>(reader, componentIdentifier, entity, scope (component, fieldIdentifier) =>
{ {
String scriptClassName = null; switch(fieldIdentifier)
Try!(Deserialize.Value(reader, "ScriptClass", out scriptClassName));
if (scriptClassName != null)
{ {
case "ScriptClass":
Try!(Deserialize.Value<String>(reader, let scriptClassName));
component.ScriptClassName = scriptClassName; component.ScriptClassName = scriptClassName;
delete scriptClassName; delete scriptClassName;
case "Fields":
Try!(SerializedObject.BonDeserialize(reader, _scriptSerializer, _fileVersion, gBonEnv));
default:
return false;
} }
return true;
if (reader.ObjectHasMore())
{
Try!(reader.EntryEnd());
if (Try!(reader.Identifier()) == "Fields")
SerializedObject.BonDeserialize(reader, _scriptSerializer.SerializedObjects, gBonEnv);
}
return .Ok;
})); }));
case "Editor": case "Editor":
Try!(DeserializeComponent<EditorFlagsComponent>(reader, entity, scope (component) => Try!(DeserializeComponent<EditorFlagsComponent>(reader, entity, scope (component) =>
@@ -677,104 +730,32 @@ class SceneSerializer
return .Ok; return .Ok;
})); }));
case "TextRenderer": case "TextRenderer":
Try!(DeserializeComponent<TextRendererComponent>(reader, entity, scope (component) => Try!(DeserializeComponentFields<TextRendererComponent>(reader, componentIdentifier, entity, scope (component, fieldIdentifier) =>
{ {
Try!(Deserialize.Value<bool>(reader, "IsRichText", let isRichText)); switch(fieldIdentifier)
component.IsRichText = isRichText;
Try!(reader.EntryEnd());
String text = null;
Try!(Deserialize.Value<String>(reader, "Text", out text));
if (text != null)
{ {
case "IsRichText":
Try!(Deserialize.Value<bool>(reader, let isRichText));
component.IsRichText = isRichText;
case "Text":
Try!(Deserialize.Value<String>(reader, let text));
component.Text = text; component.Text = text;
delete text; delete text;
default:
return false;
} }
return .Ok; return true;
})); }));
default: default:
Log.EngineLogger.AssertDebug(false, "Unknown component type"); Log.EngineLogger.Error($"Unknown component type \"{componentIdentifier}\" in entity {entity.UUID}");
//return .Err; return .Err;
} }
} }
Try!(reader.ObjectBlockEnd()); Try!(reader.ObjectBlockEnd());
return .Ok; return .Ok;
/*writer.EntryStart();
using (writer.ObjectBlock())
{
// TODO: Entity GUID goes here!
Serialize.Value(writer, "Id", entity.Handle.Index);
SerializeComponent<EditorComponent>(writer, entity, "EditorComponent", scope (component) => {});
SerializeComponent<DebugNameComponent>(writer, entity, "NameComponent", scope (component) =>
{
Serialize.Value(writer, "Name", component.DebugName);
});
SerializeComponent<SpriterRendererComponent>(writer, entity, "SpriterRendererComponent", scope (component) =>
{
// TODO: Texture
Serialize.Value(writer, "Color", component.Color);
});
SerializeComponent<TransformComponent>(writer, entity, "TransformComponent", scope (component) =>
{
// TODO: Use GUIDs
if (component.Parent != .InvalidEntity)
Serialize.Value(writer, "ParentId", component.Parent.Index);
Serialize.Value(writer, "Position", component.Position);
Serialize.Value(writer, "Rotation", component.Rotation);
Serialize.Value(writer, "Scale", component.Scale);
Serialize.Value(writer, "EditorEulerRotation", component.EditorRotationEuler);
});
SerializeComponent<CameraComponent>(writer, entity, "CameraComponent", scope (component) =>
{
SceneCamera camera = component.Camera;
Serialize.Value(writer, "Primary", component.Primary);
// TODO: Render target
Serialize.Value(writer, "ProjectionType", camera.ProjectionType);
Serialize.Value(writer, "PerspectiveFovY", camera.PerspectiveFovY);
Serialize.Value(writer, "PerspectiveNearPlane", camera.PerspectiveNearPlane);
Serialize.Value(writer, "PerspectiveFarPlane", camera.PerspectiveFarPlane);
Serialize.Value(writer, "OrthographicHeight", camera.OrthographicHeight);
Serialize.Value(writer, "OrthographicNearPlane", camera.OrthographicNearPlane);
Serialize.Value(writer, "OrthographicFarPlane", camera.OrthographicFarPlane);
Serialize.Value(writer, "AspectRatio", camera.AspectRatio);
Serialize.Value(writer, "FixedAspectRatio", camera.FixedAspectRatio);
});
SerializeComponent<LightComponent>(writer, entity, "LightComponent", scope (component) =>
{
SceneLight light = component.SceneLight;
Serialize.Value(writer, "LightType", light.LightType);
Serialize.Value(writer, "Illuminance", light.Illuminance);
Serialize.Value(writer, "Color", light.Color);
});
}
writer.EntryEnd();*/
} }
static Result<void> DeserializeComponent<T>(BonReader reader, Entity entity, delegate Result<void>(T* component) deserialize) where T : struct, new static Result<void> DeserializeComponent<T>(BonReader reader, Entity entity, delegate Result<void>(T* component) deserialize) where T : struct, new
@@ -795,6 +776,35 @@ class SceneSerializer
return .Ok; return .Ok;
} }
static Result<void> DeserializeComponentFields<T>(BonReader reader, StringView prettyComponentName, Entity entity, delegate Result<bool>(T* component, StringView identifier) deserializeField) where T : struct, new
{
Try!(reader.ObjectBlock());
T* component;
if (entity.HasComponent<T>())
component = entity.GetComponent<T>();
else
component = entity.AddComponent<T>();
while(reader.ObjectHasMore())
{
StringView fieldIdentifier = Try!(reader.Identifier());
if (!Try!(deserializeField(component, fieldIdentifier)))
{
Log.EngineLogger.Error($"Unknown identifier \"{fieldIdentifier}\" in component \"{prettyComponentName}\" of entity {entity.UUID}");
return .Err;
}
TryEndEntry(reader);
}
Try!(reader.ObjectBlockEnd());
return .Ok;
}
public bool DeserializeRuntime(StringView filePath) public bool DeserializeRuntime(StringView filePath)
{ {
Runtime.NotImplemented(); Runtime.NotImplemented();