diff --git a/Hazel/src/Hazel/Scene/SceneSerializer.cpp b/Hazel/src/Hazel/Scene/SceneSerializer.cpp index 6aa2a6619..e357d9cfb 100644 --- a/Hazel/src/Hazel/Scene/SceneSerializer.cpp +++ b/Hazel/src/Hazel/Scene/SceneSerializer.cpp @@ -88,6 +88,10 @@ namespace YAML { } namespace Hazel { + static void SerializeAllEntityComponents(YAML::Emitter& out, Entity entity); + + static void DeserializeAllEntryComponents(YAML::detail::iterator_value& entity, Entity& deserializedEntity); + YAML::Emitter& operator<<(YAML::Emitter& out, const glm::vec2& v) { out << YAML::Flow; @@ -113,9 +117,9 @@ namespace Hazel { { switch (bodyType) { - case Rigidbody2DComponent::BodyType::Static: return "Static"; - case Rigidbody2DComponent::BodyType::Dynamic: return "Dynamic"; - case Rigidbody2DComponent::BodyType::Kinematic: return "Kinematic"; + case Rigidbody2DComponent::BodyType::Static: return "Static"; + case Rigidbody2DComponent::BodyType::Dynamic: return "Dynamic"; + case Rigidbody2DComponent::BodyType::Kinematic: return "Kinematic"; } HZ_CORE_ASSERT(false, "Unknown body type"); @@ -127,7 +131,7 @@ namespace Hazel { if (bodyTypeString == "Static") return Rigidbody2DComponent::BodyType::Static; if (bodyTypeString == "Dynamic") return Rigidbody2DComponent::BodyType::Dynamic; if (bodyTypeString == "Kinematic") return Rigidbody2DComponent::BodyType::Kinematic; - + HZ_CORE_ASSERT(false, "Unknown body type"); return Rigidbody2DComponent::BodyType::Static; } @@ -144,6 +148,98 @@ namespace Hazel { out << YAML::BeginMap; // Entity out << YAML::Key << "Entity" << YAML::Value << entity.GetUUID(); + // Serialize components (except IDComponent) + SerializeAllEntityComponents(out,entity); + + out << YAML::EndMap; // Entity + } + + void SceneSerializer::Serialize(const std::string& filepath) + { + YAML::Emitter out; + out << YAML::BeginMap; + out << YAML::Key << "Scene" << YAML::Value << "Untitled"; + out << YAML::Key << "Entities" << YAML::Value << YAML::BeginSeq; + m_Scene->m_Registry.each([&](auto entityID) + { + Entity entity = { entityID, m_Scene.get() }; + if (!entity) + return; + + SerializeEntity(out, entity); + }); + out << YAML::EndSeq; + out << YAML::EndMap; + + std::ofstream fout(filepath); + fout << out.c_str(); + } + + void SceneSerializer::SerializeRuntime(const std::string& filepath) + { + // Not implemented + HZ_CORE_ASSERT(false); + } + + bool SceneSerializer::Deserialize(const std::string& filepath) + { + YAML::Node data; + try + { + data = YAML::LoadFile(filepath); + } + catch (YAML::ParserException e) + { + HZ_CORE_ERROR("Failed to load .hazel file '{0}'\n {1}", filepath, e.what()); + return false; + } + + if (!data["Scene"]) + return false; + + std::string sceneName = data["Scene"].as(); + HZ_CORE_TRACE("Deserializing scene '{0}'", sceneName); + + auto entities = data["Entities"]; + if (entities) + { + for (auto entity : entities) + { + uint64_t uuid = entity["Entity"].as(); + + std::string name; + auto tagComponent = entity["TagComponent"]; + if (tagComponent) + name = tagComponent["Tag"].as(); + + HZ_CORE_TRACE("Deserialized entity with ID = {0}, name = {1}", uuid, name); + + Entity deserializedEntity = m_Scene->CreateEntityWithUUID(uuid, name); + + // Deserialize components (except IDComponent and TagComponent) + DeserializeAllEntryComponents(entity, deserializedEntity); + } + } + + return true; + } + + bool SceneSerializer::DeserializeRuntime(const std::string& filepath) + { + // Not implemented + HZ_CORE_ASSERT(false); + return false; + } + + template + static void SerializeEntityComponent(YAML::Emitter&, Entity) = delete; + + template + void DeserializeEntryComponent(YAML::detail::iterator_value& entity, Entity&) = delete; + + template<> + static void SerializeEntityComponent(YAML::Emitter& out, Entity entity) + { if (entity.HasComponent()) { out << YAML::Key << "TagComponent"; @@ -154,7 +250,11 @@ namespace Hazel { out << YAML::EndMap; // TagComponent } + } + template<> + static void SerializeEntityComponent(YAML::Emitter& out, Entity entity) + { if (entity.HasComponent()) { out << YAML::Key << "TransformComponent"; @@ -168,6 +268,25 @@ namespace Hazel { out << YAML::EndMap; // TransformComponent } + } + + template<> + static void DeserializeEntryComponent(YAML::detail::iterator_value& entity, Entity& deserializedEntity) + { + auto transformComponent = entity["TransformComponent"]; + if (transformComponent) + { + // Entities always have transforms + auto& tc = deserializedEntity.GetComponent(); + tc.Translation = transformComponent["Translation"].as(); + tc.Rotation = transformComponent["Rotation"].as(); + tc.Scale = transformComponent["Scale"].as(); + } + } + + template<> + static void SerializeEntityComponent(YAML::Emitter& out, Entity entity) + { if (entity.HasComponent()) { out << YAML::Key << "CameraComponent"; @@ -192,22 +311,71 @@ namespace Hazel { out << YAML::EndMap; // CameraComponent } + } + + template<> + static void DeserializeEntryComponent(YAML::detail::iterator_value& entity, Entity& deserializedEntity) + { + auto cameraComponent = entity["CameraComponent"]; + if (cameraComponent) + { + auto& cc = deserializedEntity.AddComponent(); + + auto& cameraProps = cameraComponent["Camera"]; + cc.Camera.SetProjectionType((SceneCamera::ProjectionType)cameraProps["ProjectionType"].as()); + + cc.Camera.SetPerspectiveVerticalFOV(cameraProps["PerspectiveFOV"].as()); + cc.Camera.SetPerspectiveNearClip(cameraProps["PerspectiveNear"].as()); + cc.Camera.SetPerspectiveFarClip(cameraProps["PerspectiveFar"].as()); + cc.Camera.SetOrthographicSize(cameraProps["OrthographicSize"].as()); + cc.Camera.SetOrthographicNearClip(cameraProps["OrthographicNear"].as()); + cc.Camera.SetOrthographicFarClip(cameraProps["OrthographicFar"].as()); + + cc.Primary = cameraComponent["Primary"].as(); + cc.FixedAspectRatio = cameraComponent["FixedAspectRatio"].as(); + } + } + + template<> + static void SerializeEntityComponent(YAML::Emitter& out, Entity entity) + { if (entity.HasComponent()) { out << YAML::Key << "SpriteRendererComponent"; out << YAML::BeginMap; // SpriteRendererComponent auto& spriteRendererComponent = entity.GetComponent(); - out << YAML::Key << "Color" << YAML::Value << spriteRendererComponent.Color; - if (spriteRendererComponent.Texture) + if (spriteRendererComponent.Texture && std::filesystem::exists(spriteRendererComponent.Texture->GetPath())) + { out << YAML::Key << "TexturePath" << YAML::Value << spriteRendererComponent.Texture->GetPath(); + } + out << YAML::Key << "Color" << YAML::Value << spriteRendererComponent.Color; out << YAML::Key << "TilingFactor" << YAML::Value << spriteRendererComponent.TilingFactor; out << YAML::EndMap; // SpriteRendererComponent } + } + template<> + static void DeserializeEntryComponent(YAML::detail::iterator_value& entity, Entity& deserializedEntity) + { + auto spriteRendererComponent = entity["SpriteRendererComponent"]; + if (spriteRendererComponent) + { + auto& src = deserializedEntity.AddComponent(); + if(spriteRendererComponent["TexturePath"]) + src.Texture = Texture2D::Create(spriteRendererComponent["TexturePath"].as()); + src.Color = spriteRendererComponent["Color"].as(); + if (spriteRendererComponent["TilingFactor"]) + src.TilingFactor = spriteRendererComponent["TilingFactor"].as(); + } + } + + template<> + static void SerializeEntityComponent(YAML::Emitter& out, Entity entity) + { if (entity.HasComponent()) { out << YAML::Key << "CircleRendererComponent"; @@ -216,11 +384,26 @@ namespace Hazel { auto& circleRendererComponent = entity.GetComponent(); out << YAML::Key << "Color" << YAML::Value << circleRendererComponent.Color; out << YAML::Key << "Thickness" << YAML::Value << circleRendererComponent.Thickness; - out << YAML::Key << "Fade" << YAML::Value << circleRendererComponent.Fade; out << YAML::EndMap; // CircleRendererComponent } + } + template<> + static void DeserializeEntryComponent(YAML::detail::iterator_value& entity, Entity& deserializedEntity) + { + auto circleRendererComponent = entity["CircleRendererComponent"]; + if (circleRendererComponent) + { + auto& src = deserializedEntity.AddComponent(); + src.Color = circleRendererComponent["Color"].as(); + src.Thickness = circleRendererComponent["Thickness"].as(); + } + } + + template<> + static void SerializeEntityComponent(YAML::Emitter& out, Entity entity) + { if (entity.HasComponent()) { out << YAML::Key << "Rigidbody2DComponent"; @@ -232,7 +415,23 @@ namespace Hazel { out << YAML::EndMap; // Rigidbody2DComponent } + } + + template<> + static void DeserializeEntryComponent(YAML::detail::iterator_value& entity, Entity& deserializedEntity) + { + auto rigidbody2DComponent = entity["Rigidbody2DComponent"]; + if (rigidbody2DComponent) + { + auto& rb2d = deserializedEntity.AddComponent(); + rb2d.Type = RigidBody2DBodyTypeFromString(rigidbody2DComponent["BodyType"].as()); + rb2d.FixedRotation = rigidbody2DComponent["FixedRotation"].as(); + } + } + template<> + static void SerializeEntityComponent(YAML::Emitter& out, Entity entity) + { if (entity.HasComponent()) { out << YAML::Key << "BoxCollider2DComponent"; @@ -248,6 +447,26 @@ namespace Hazel { out << YAML::EndMap; // BoxCollider2DComponent } + } + + template<> + static void DeserializeEntryComponent(YAML::detail::iterator_value& entity, Entity& deserializedEntity) + { + auto boxCollider2DComponent = entity["BoxCollider2DComponent"]; + if (boxCollider2DComponent) + { + auto& bc2d = deserializedEntity.AddComponent(); + bc2d.Offset = boxCollider2DComponent["Offset"].as(); + bc2d.Size = boxCollider2DComponent["Size"].as(); + bc2d.Density = boxCollider2DComponent["Density"].as(); + bc2d.Friction = boxCollider2DComponent["Friction"].as(); + bc2d.Restitution = boxCollider2DComponent["Restitution"].as(); + bc2d.RestitutionThreshold = boxCollider2DComponent["RestitutionThreshold"].as(); + } + } + template<> + static void SerializeEntityComponent(YAML::Emitter& out, Entity entity) + { if (entity.HasComponent()) { @@ -264,165 +483,67 @@ namespace Hazel { out << YAML::EndMap; // CircleCollider2DComponent } - - out << YAML::EndMap; // Entity } - void SceneSerializer::Serialize(const std::string& filepath) + template<> + static void DeserializeEntryComponent(YAML::detail::iterator_value& entity, Entity& deserializedEntity) { - YAML::Emitter out; - out << YAML::BeginMap; - out << YAML::Key << "Scene" << YAML::Value << "Untitled"; - out << YAML::Key << "Entities" << YAML::Value << YAML::BeginSeq; - m_Scene->m_Registry.each([&](auto entityID) + auto circleCollider2DComponent = entity["CircleCollider2DComponent"]; + if (circleCollider2DComponent) { - Entity entity = { entityID, m_Scene.get() }; - if (!entity) - return; - - SerializeEntity(out, entity); - }); - out << YAML::EndSeq; - out << YAML::EndMap; - - std::ofstream fout(filepath); - fout << out.c_str(); + auto& bc2d = deserializedEntity.AddComponent(); + bc2d.Offset = circleCollider2DComponent["Offset"].as(); + bc2d.Radius = circleCollider2DComponent["Radius"].as(); + bc2d.Density = circleCollider2DComponent["Density"].as(); + bc2d.Friction = circleCollider2DComponent["Friction"].as(); + bc2d.Restitution = circleCollider2DComponent["Restitution"].as(); + bc2d.RestitutionThreshold = circleCollider2DComponent["RestitutionThreshold"].as(); + } } - void SceneSerializer::SerializeRuntime(const std::string& filepath) + template<> + static void SerializeEntityComponent(YAML::Emitter& out, Entity entity) { - // Not implemented - HZ_CORE_ASSERT(false); + //TODO: serialize NativeScriptComponent? } - bool SceneSerializer::Deserialize(const std::string& filepath) + template<> + static void DeserializeEntryComponent(YAML::detail::iterator_value&, Entity&) { - YAML::Node data; - try - { - data = YAML::LoadFile(filepath); - } - catch (YAML::ParserException e) - { - HZ_CORE_ERROR("Failed to load .hazel file '{0}'\n {1}", filepath, e.what()); - return false; - } - - if (!data["Scene"]) - return false; - - std::string sceneName = data["Scene"].as(); - HZ_CORE_TRACE("Deserializing scene '{0}'", sceneName); - - auto entities = data["Entities"]; - if (entities) - { - for (auto entity : entities) - { - uint64_t uuid = entity["Entity"].as(); - - std::string name; - auto tagComponent = entity["TagComponent"]; - if (tagComponent) - name = tagComponent["Tag"].as(); + //TODO: deserialize NativeScriptComponent? + } - HZ_CORE_TRACE("Deserialized entity with ID = {0}, name = {1}", uuid, name); + template + static void SerializeEntityComponents(YAML::Emitter& out, Entity entity) + { + (SerializeEntityComponent(out, entity), ...); + } - Entity deserializedEntity = m_Scene->CreateEntityWithUUID(uuid, name); + template + static void DeserializeEntryComponents(YAML::detail::iterator_value& entity, Entity& deserializedEntity) + { + (DeserializeEntryComponent(entity, deserializedEntity), ...); + } - auto transformComponent = entity["TransformComponent"]; - if (transformComponent) - { - // Entities always have transforms - auto& tc = deserializedEntity.GetComponent(); - tc.Translation = transformComponent["Translation"].as(); - tc.Rotation = transformComponent["Rotation"].as(); - tc.Scale = transformComponent["Scale"].as(); - } - - auto cameraComponent = entity["CameraComponent"]; - if (cameraComponent) - { - auto& cc = deserializedEntity.AddComponent(); - - auto& cameraProps = cameraComponent["Camera"]; - cc.Camera.SetProjectionType((SceneCamera::ProjectionType)cameraProps["ProjectionType"].as()); - - cc.Camera.SetPerspectiveVerticalFOV(cameraProps["PerspectiveFOV"].as()); - cc.Camera.SetPerspectiveNearClip(cameraProps["PerspectiveNear"].as()); - cc.Camera.SetPerspectiveFarClip(cameraProps["PerspectiveFar"].as()); - - cc.Camera.SetOrthographicSize(cameraProps["OrthographicSize"].as()); - cc.Camera.SetOrthographicNearClip(cameraProps["OrthographicNear"].as()); - cc.Camera.SetOrthographicFarClip(cameraProps["OrthographicFar"].as()); - - cc.Primary = cameraComponent["Primary"].as(); - cc.FixedAspectRatio = cameraComponent["FixedAspectRatio"].as(); - } - - auto spriteRendererComponent = entity["SpriteRendererComponent"]; - if (spriteRendererComponent) - { - auto& src = deserializedEntity.AddComponent(); - src.Color = spriteRendererComponent["Color"].as(); - if (spriteRendererComponent["TexturePath"]) - src.Texture = Texture2D::Create(spriteRendererComponent["TexturePath"].as()); - - if (spriteRendererComponent["TilingFactor"]) - src.TilingFactor = spriteRendererComponent["TilingFactor"].as(); - } - - auto circleRendererComponent = entity["CircleRendererComponent"]; - if (circleRendererComponent) - { - auto& crc = deserializedEntity.AddComponent(); - crc.Color = circleRendererComponent["Color"].as(); - crc.Thickness = circleRendererComponent["Thickness"].as(); - crc.Fade = circleRendererComponent["Fade"].as(); - } - - auto rigidbody2DComponent = entity["Rigidbody2DComponent"]; - if (rigidbody2DComponent) - { - auto& rb2d = deserializedEntity.AddComponent(); - rb2d.Type = RigidBody2DBodyTypeFromString(rigidbody2DComponent["BodyType"].as()); - rb2d.FixedRotation = rigidbody2DComponent["FixedRotation"].as(); - } - - auto boxCollider2DComponent = entity["BoxCollider2DComponent"]; - if (boxCollider2DComponent) - { - auto& bc2d = deserializedEntity.AddComponent(); - bc2d.Offset = boxCollider2DComponent["Offset"].as(); - bc2d.Size = boxCollider2DComponent["Size"].as(); - bc2d.Density = boxCollider2DComponent["Density"].as(); - bc2d.Friction = boxCollider2DComponent["Friction"].as(); - bc2d.Restitution = boxCollider2DComponent["Restitution"].as(); - bc2d.RestitutionThreshold = boxCollider2DComponent["RestitutionThreshold"].as(); - } - - auto circleCollider2DComponent = entity["CircleCollider2DComponent"]; - if (circleCollider2DComponent) - { - auto& cc2d = deserializedEntity.AddComponent(); - cc2d.Offset = circleCollider2DComponent["Offset"].as(); - cc2d.Radius = circleCollider2DComponent["Radius"].as(); - cc2d.Density = circleCollider2DComponent["Density"].as(); - cc2d.Friction = circleCollider2DComponent["Friction"].as(); - cc2d.Restitution = circleCollider2DComponent["Restitution"].as(); - cc2d.RestitutionThreshold = circleCollider2DComponent["RestitutionThreshold"].as(); - } - } - } + template + static void SerializeEntityComponents(ComponentGroup, YAML::Emitter& out, Entity entity) + { + SerializeEntityComponents(out, entity); + } - return true; + template + static void DeserializeEntryComponents(ComponentGroup, YAML::detail::iterator_value& entity, Entity& deserializedEntity) + { + DeserializeEntryComponents(entity, deserializedEntity); } - bool SceneSerializer::DeserializeRuntime(const std::string& filepath) + static void SerializeAllEntityComponents(YAML::Emitter& out, Entity entity) { - // Not implemented - HZ_CORE_ASSERT(false); - return false; + SerializeEntityComponents(AllComponents{}, out, entity); } + static void DeserializeAllEntryComponents(YAML::detail::iterator_value& entity, Entity& deserializedEntity) + { + DeserializeEntryComponents(AllComponents{}, entity, deserializedEntity); + } }