feat? add basich ah scene loading

This commit is contained in:
2026-07-25 21:46:01 +02:00
parent aa5c497f29
commit 4b6f773c0c
18 changed files with 523 additions and 73 deletions
+3
View File
@@ -53,6 +53,9 @@ set(SRC_FILES
"src/FS/AssetFS.cpp" "src/FS/AssetFS.cpp"
"src/FS/Manifest.cpp" "src/FS/Manifest.cpp"
"src/Serialization/SceneSerializer.cpp"
"src/Serialization/ComponentRegistry.cpp"
"src/Util/DeltaTime.cpp" "src/Util/DeltaTime.cpp"
"src/Physics/PhysicsWorld.cpp" "src/Physics/PhysicsWorld.cpp"
@@ -19,6 +19,10 @@ public:
explicit Animator(GameObject& parent); explicit Animator(GameObject& parent);
void Update() override; void Update() override;
std::string GetTypeName() const override
{
return "Animator";
}
void ImGuiInspector() override; void ImGuiInspector() override;
void addClip(std::shared_ptr<SkeletalAnimation> clip); void addClip(std::shared_ptr<SkeletalAnimation> clip);
@@ -18,6 +18,11 @@ public:
void SetMaterialID(MaterialID id) { materialID = id; } void SetMaterialID(MaterialID id) { materialID = id; }
MaterialID GetMaterialID() const { return materialID; } MaterialID GetMaterialID() const { return materialID; }
std::string GetTypeName() const override
{
return "MeshRendererComponent";
}
private: private:
MeshID meshID{NULL_MESH_ID}; MeshID meshID{NULL_MESH_ID};
MaterialID materialID{NULL_MATERIAL_ID}; MaterialID materialID{NULL_MATERIAL_ID};
@@ -11,6 +11,11 @@ public:
void Update() override {} void Update() override {}
std::string GetTypeName() const override
{
return "BoxCollider";
}
[[nodiscard]] PhysicsShapeDesc BuildPhysicsShape() const override { [[nodiscard]] PhysicsShapeDesc BuildPhysicsShape() const override {
return PhysicsShapeDesc::Box(m_HalfExtents, m_CenterOffset, m_IsTrigger); return PhysicsShapeDesc::Box(m_HalfExtents, m_CenterOffset, m_IsTrigger);
@@ -16,6 +16,11 @@ public:
void Update() override {} void Update() override {}
std::string GetTypeName() const override
{
return "RigidBody";
}
void AttachPhysicsBody(PhysicsWorld* world, PhysicsBodyHandle body); void AttachPhysicsBody(PhysicsWorld* world, PhysicsBodyHandle body);
void DetachPhysicsBody(); void DetachPhysicsBody();
@@ -11,6 +11,10 @@ public:
, m_Radius(radius) {} , m_Radius(radius) {}
void Update() override {} void Update() override {}
std::string GetTypeName() const override
{
return "SphereCollider";
}
[[nodiscard]] PhysicsShapeDesc BuildPhysicsShape() const override { [[nodiscard]] PhysicsShapeDesc BuildPhysicsShape() const override {
return PhysicsShapeDesc::Sphere(m_Radius, m_CenterOffset, m_IsTrigger); return PhysicsShapeDesc::Sphere(m_Radius, m_CenterOffset, m_IsTrigger);
@@ -1,14 +1,21 @@
#ifndef COMPONENT_H #ifndef COMPONENT_H
#define COMPONENT_H #define COMPONENT_H
#include <string>
#include <unordered_map>
#include <nlohmann/json.hpp>
#include <destrum/Graphics/RenderContext.h> #include <destrum/Graphics/RenderContext.h>
#include <destrum/ObjectModel/Object.h> #include <destrum/ObjectModel/Object.h>
#include <destrum/ObjectModel/ObjectId.h>
class GameObject; class GameObject;
class Transform; class Transform;
class Component: public Object { class Component : public Object {
public: public:
using ObjectMap = std::unordered_map<ObjectId, GameObject*>;
~Component() override = default; ~Component() override = default;
@@ -18,30 +25,44 @@ public:
Component& operator=(Component&& other) noexcept = delete; Component& operator=(Component&& other) noexcept = delete;
[[nodiscard]] bool isEnabled() const { return m_IsEnabled; } [[nodiscard]] bool isEnabled() const { return m_IsEnabled; }
[[nodiscard]] GameObject *GetGameObject() const { return m_ParentGameObjectPtr; } [[nodiscard]] GameObject* GetGameObject() const { return m_ParentGameObjectPtr; }
[[nodiscard]] Transform& GetTransform() const; [[nodiscard]] Transform& GetTransform() const;
void Destroy() override; void Destroy() override;
virtual void Start(); virtual void Start();
virtual void SetEnabled(bool enabled); virtual void SetEnabled(bool enabled);
virtual void Update() = 0; virtual void Update() = 0;
virtual void LateUpdate(); virtual void LateUpdate();
virtual void FixedUpdate(); virtual void FixedUpdate();
virtual void ImGuiInspector(); //Specifically for the inspector
virtual void ImGuiRender();
virtual void ImGuiInspector();
virtual void ImGuiRender();
virtual void Render(const RenderContext& ctx); virtual void Render(const RenderContext& ctx);
// Serialization.
virtual std::string GetTypeName() const = 0;
virtual nlohmann::json Serialize() const {
return {};
}
virtual void Deserialize(const nlohmann::json& data) {
}
virtual void ResolveReferences(const ObjectMap& objects) {
}
bool HasStarted{false}; bool HasStarted{false};
protected: protected:
explicit Component(GameObject& pParent, const std::string& name = "Component"); explicit Component(GameObject& pParent, const std::string& name = "Component");
private: private:
GameObject* m_ParentGameObjectPtr{}; GameObject* m_ParentGameObjectPtr{};
bool m_IsEnabled{true}; bool m_IsEnabled{true};
}; };
#endif //COMPONENT_H #endif // COMPONENT_H
@@ -1,14 +1,17 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <algorithm>
#include <destrum/ObjectModel/Component.h> #include <destrum/ObjectModel/Component.h>
#include <destrum/ObjectModel/Object.h> #include <destrum/ObjectModel/Object.h>
#include <destrum/ObjectModel/ObjectId.h>
#include <destrum/ObjectModel/Transform.h> #include <destrum/ObjectModel/Transform.h>
class Scene; class Scene;
class GameObject final: public Object { class GameObject final : public Object {
public: public:
friend class Scene; friend class Scene;
@@ -31,14 +34,28 @@ public:
SetActiveDirty(); SetActiveDirty();
} }
[[nodiscard]] std::vector<std::unique_ptr<Component>>& GetComponents() { return m_Components; } [[nodiscard]] bool IsActive() const { return m_Active; }
[[nodiscard]] ObjectId GetId() const { return m_Id; }
// Use this only while loading a scene.
void SetIdForDeserialization(ObjectId id);
[[nodiscard]] Scene* GetScene() const { return m_Scene; }
[[nodiscard]] std::vector<std::unique_ptr<Component>>& GetComponents() {
return m_Components;
}
[[nodiscard]] const std::vector<std::unique_ptr<Component>>& GetComponents() const {
return m_Components;
}
[[nodiscard]] Transform& GetTransform() { return m_TransformPtr; } [[nodiscard]] Transform& GetTransform() { return m_TransformPtr; }
[[nodiscard]] const Transform& GetTransform() const { return m_TransformPtr; }
[[nodiscard]] bool IsActiveInHierarchy(); [[nodiscard]] bool IsActiveInHierarchy();
[[nodiscard]] bool IsActive() const { return m_Active; }
explicit GameObject(const std::string& name = "GameObject"); explicit GameObject(const std::string& name = "GameObject");
~GameObject() override; ~GameObject() override;
@@ -57,20 +74,30 @@ public:
return static_cast<TComponent*>(addedComponent.get()); return static_cast<TComponent*>(addedComponent.get());
} }
template <typename Component> template <typename TComponent>
[[nodiscard]] Component *GetComponent() { [[nodiscard]] TComponent* GetComponent() {
for (const auto& component: m_Components) { for (const auto& component : m_Components) {
if (auto casted = dynamic_cast<Component*>(component.get())) { if (auto casted = dynamic_cast<TComponent*>(component.get())) {
return casted; return casted;
} }
} }
return nullptr; return nullptr;
} }
template <typename Component> template <typename TComponent>
Component *DestroyComponent() { [[nodiscard]] const TComponent* GetComponent() const {
for (const auto& component: m_Components) { for (const auto& component : m_Components) {
if (auto casted = dynamic_cast<Component*>(component.get())) { if (auto casted = dynamic_cast<const TComponent*>(component.get())) {
return casted;
}
}
return nullptr;
}
template <typename TComponent>
TComponent* DestroyComponent() {
for (const auto& component : m_Components) {
if (auto casted = dynamic_cast<TComponent*>(component.get())) {
casted->Destroy(); casted->Destroy();
return casted; return casted;
} }
@@ -78,40 +105,50 @@ public:
return nullptr; return nullptr;
} }
template <typename Component> template <typename TComponent>
[[nodiscard]] bool HasComponent() { [[nodiscard]] bool HasComponent() const {
for (const auto& component: m_Components) { for (const auto& component : m_Components) {
if (auto casted = dynamic_cast<Component*>(component.get())) { if (dynamic_cast<TComponent*>(component.get())) {
return true; return true;
} }
} }
return false; return false;
} }
template <typename Component> template <typename TComponent>
Component *GetComponentInChildren() { TComponent* GetComponentInChildren() {
return GetComponentInChildrenRecursive<Component>(&GetTransform()); return GetComponentInChildrenRecursive<TComponent>(&GetTransform());
} }
private: private:
template <typename Component> template <typename TComponent>
static Component *GetComponentInChildrenRecursive(Transform* transform) { static TComponent* GetComponentInChildrenRecursive(Transform* transform) {
if (!transform) return nullptr; if (!transform) return nullptr;
GameObject* owner = transform->GetOwner(); GameObject* owner = transform->GetOwner();
if (owner) { if (owner) {
if (Component* comp = owner->GetComponent<Component>()) { if (TComponent* comp = owner->GetComponent<TComponent>()) {
return comp; return comp;
} }
} }
for (Transform* child: transform->GetChildren()) {
if (Component* found = GetComponentInChildrenRecursive<Component>(child)) { for (Transform* child : transform->GetChildren()) {
if (TComponent* found = GetComponentInChildrenRecursive<TComponent>(child)) {
return found; return found;
} }
} }
return nullptr; return nullptr;
} }
void SetScene(Scene* scene) { m_Scene = scene; }
void SetActiveDirty(); void SetActiveDirty();
void UpdateActiveState();
inline static ObjectId s_NextId{1};
ObjectId m_Id{InvalidObjectId};
bool m_Active{true}; bool m_Active{true};
Scene* m_Scene{}; Scene* m_Scene{};
@@ -119,6 +156,5 @@ private:
std::vector<std::unique_ptr<Component>> m_Components{}; std::vector<std::unique_ptr<Component>> m_Components{};
bool m_ActiveDirty{true}; bool m_ActiveDirty{true};
bool m_ActiveInHierarchy{true}; //Derived bool m_ActiveInHierarchy{true};
void UpdateActiveState();
}; };
@@ -0,0 +1,10 @@
#ifndef DESTRUM_OBJECTID_H
#define DESTRUM_OBJECTID_H
#include <cstdint>
using ObjectId = std::uint64_t;
inline constexpr ObjectId InvalidObjectId = 0;
#endif //DESTRUM_OBJECTID_H
+6
View File
@@ -35,6 +35,12 @@ public:
[[nodiscard]] bool IsBeingUnloaded() const { return m_BeingUnloaded; } [[nodiscard]] bool IsBeingUnloaded() const { return m_BeingUnloaded; }
void CommitPendingAdditions();
[[nodiscard]] const std::vector<std::shared_ptr<GameObject>>& GetObjects() const {
return m_objects;
}
void SetRegisterBindings(std::function<void()> registerBindings) { void SetRegisterBindings(std::function<void()> registerBindings) {
m_registerBindings = std::move(registerBindings); m_registerBindings = std::move(registerBindings);
} }
@@ -0,0 +1,37 @@
#ifndef DESTRUM_COMPONENTFACTORY_H
#define DESTRUM_COMPONENTFACTORY_H
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
#include <destrum/ObjectModel/Component.h>
#include <destrum/ObjectModel/GameObject.h>
class ComponentFactory final {
public:
using CreateFn = std::function<Component*(GameObject&)>;
static void Register(const std::string& typeName, CreateFn createFn) {
Registry()[typeName] = std::move(createFn);
}
static Component* Create(const std::string& typeName, GameObject& owner) {
const auto it = Registry().find(typeName);
if (it == Registry().end()) {
return nullptr;
}
return it->second(owner);
}
private:
static std::unordered_map<std::string, CreateFn>& Registry() {
static std::unordered_map<std::string, CreateFn> registry;
return registry;
}
};
#endif //DESTRUM_COMPONENTFACTORY_H
@@ -0,0 +1,6 @@
#ifndef DESTRUM_COMPONENTREGISTRY_H
#define DESTRUM_COMPONENTREGISTRY_H
void RegisterEngineComponents();
#endif //DESTRUM_COMPONENTREGISTRY_H
@@ -0,0 +1,14 @@
#ifndef DESTRUM_SCENESERIALIZER_H
#define DESTRUM_SCENESERIALIZER_H
#include <filesystem>
class Scene;
class SceneSerializer final {
public:
static bool Save(Scene& scene, const std::filesystem::path& path);
static bool Load(Scene& scene, const std::filesystem::path& path);
};
#endif //DESTRUM_SCENESERIALIZER_H
+3 -8
View File
@@ -2,13 +2,10 @@
#include <stdexcept> #include <stdexcept>
#include <destrum/ObjectModel/GameObject.h> #include <destrum/ObjectModel/GameObject.h>
// #include "imgui.h"
Component::Component(GameObject& pParent, const std::string& name): Object(name), m_ParentGameObjectPtr(&pParent) { Component::Component(GameObject& pParent, const std::string& name)
// if (m_ParentGameObjectPtr == nullptr) { : Object(name),
// //TODO: Change pParent ot be a reference m_ParentGameObjectPtr(&pParent) {
// throw std::runtime_error("Component made with no GameObject??");
// }
} }
Transform& Component::GetTransform() const { Transform& Component::GetTransform() const {
@@ -16,7 +13,6 @@ Transform& Component::GetTransform() const {
} }
void Component::Destroy() { void Component::Destroy() {
// const bool isBeingDestroyed = GetIsBeingDestroyed();
Object::Destroy(); Object::Destroy();
} }
@@ -40,5 +36,4 @@ void Component::ImGuiRender() {
} }
void Component::Render(const RenderContext& ctx) { void Component::Render(const RenderContext& ctx) {
} }
+35 -21
View File
@@ -1,18 +1,30 @@
#include <destrum/ObjectModel/GameObject.h> #include <destrum/ObjectModel/GameObject.h>
#include <string> #include <string>
#include <iostream> #include <iostream>
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
// #include "Managers/ResourceManager.h"
GameObject::~GameObject() { GameObject::~GameObject() {
// spdlog::debug("GameObject destroyed: {}", GetName()); // spdlog::debug("GameObject destroyed: {}", GetName());
} }
GameObject::GameObject(const std::string& name)
: Object(name),
m_Id(s_NextId++) {
}
void GameObject::SetIdForDeserialization(ObjectId id) {
m_Id = id;
// Prevent newly created objects from reusing loaded IDs.
if (id >= s_NextId) {
s_NextId = id + 1;
}
}
void GameObject::SetActiveDirty() { void GameObject::SetActiveDirty() {
m_ActiveDirty = true; m_ActiveDirty = true;
for (const Transform* child : m_TransformPtr.GetChildren()) { for (const Transform* child : m_TransformPtr.GetChildren()) {
child->GetOwner()->SetActiveDirty(); child->GetOwner()->SetActiveDirty();
} }
@@ -21,7 +33,7 @@ void GameObject::SetActiveDirty() {
void GameObject::UpdateActiveState() { void GameObject::UpdateActiveState() {
const auto* parentPtr = m_TransformPtr.GetParent(); const auto* parentPtr = m_TransformPtr.GetParent();
if(parentPtr == nullptr) { if (parentPtr == nullptr) {
m_ActiveInHierarchy = m_Active; m_ActiveInHierarchy = m_Active;
} else { } else {
m_ActiveInHierarchy = m_Active && parentPtr->GetOwner()->IsActiveInHierarchy(); m_ActiveInHierarchy = m_Active && parentPtr->GetOwner()->IsActiveInHierarchy();
@@ -38,49 +50,51 @@ bool GameObject::IsActiveInHierarchy() {
return m_ActiveInHierarchy; return m_ActiveInHierarchy;
} }
GameObject::GameObject(const std::string& name): Object(name) {
}
void GameObject::Update() { void GameObject::Update() {
for (const auto& component: m_Components) { for (const auto& component : m_Components) {
if (!component->isEnabled()) {
continue;
}
if (!component->HasStarted) { if (!component->HasStarted) {
component->Start(); component->Start();
component->HasStarted = true; component->HasStarted = true;
} }
component->Update(); component->Update();
} }
} }
void GameObject::LateUpdate() { void GameObject::LateUpdate() {
for (const auto& component: m_Components) { for (const auto& component : m_Components) {
component->LateUpdate(); if (component->isEnabled()) {
component->LateUpdate();
}
} }
} }
void GameObject::FixedUpdate() { void GameObject::FixedUpdate() {
for (const auto& component: m_Components) { for (const auto& component : m_Components) {
component->FixedUpdate(); if (component->isEnabled()) {
component->FixedUpdate();
}
} }
} }
void GameObject::Render(const RenderContext& ctx) const { void GameObject::Render(const RenderContext& ctx) const {
for (const auto& component: m_Components) { for (const auto& component : m_Components) {
component->Render(ctx); if (component->isEnabled()) {
component->Render(ctx);
}
} }
} }
// void GameObject::ImGuiRender() {
// for (const auto& component: m_Components) {
// component->ImGuiRender();
// }
// }
void GameObject::Destroy() { void GameObject::Destroy() {
Object::Destroy(); Object::Destroy();
m_TransformPtr.SetParent(nullptr); m_TransformPtr.SetParent(nullptr);
for (const auto& component: m_Components) { for (const auto& component : m_Components) {
component->Destroy(); component->Destroy();
} }
+15 -9
View File
@@ -32,7 +32,7 @@ GameObject* Scene::CreateGameObject(std::string name)
GameObject* rawPtr = obj.get(); GameObject* rawPtr = obj.get();
// obj->SetScene(this); obj->SetScene(this);
m_pendingAdditions.emplace_back(std::move(obj)); m_pendingAdditions.emplace_back(std::move(obj));
return rawPtr; return rawPtr;
@@ -61,15 +61,9 @@ void Scene::Load() {
} }
void Scene::Update() { void Scene::Update() {
if (!m_pendingAdditions.empty()) { CommitPendingAdditions();
for (auto& obj : m_pendingAdditions) {
m_objects.emplace_back(std::move(obj));
}
m_pendingAdditions.clear();
}
for (const auto& object : m_objects) {
for (const auto& object: m_objects) {
if (object->IsActiveInHierarchy()) { if (object->IsActiveInHierarchy()) {
object->Update(); object->Update();
} }
@@ -155,3 +149,15 @@ void Scene::DestroyGameObjects() {
assert(m_BeingUnloaded && "Scene is being cleared but not unloaded? Weird"); assert(m_BeingUnloaded && "Scene is being cleared but not unloaded? Weird");
} }
} }
void Scene::CommitPendingAdditions() {
if (m_pendingAdditions.empty()) {
return;
}
for (auto& obj : m_pendingAdditions) {
m_objects.emplace_back(std::move(obj));
}
m_pendingAdditions.clear();
}
@@ -0,0 +1,56 @@
// destrum/Serialization/ComponentRegistry.cpp
#include <destrum/Serialization/ComponentRegistry.h>
#include <destrum/Serialization/ComponentFactory.h>
#include <destrum/Components/MeshRendererComponent.h>
#include <destrum/Components/Rotator.h>
#include <destrum/Components/Spinner.h>
#include <destrum/Components/OrbitAndSpin.h>
#include <destrum/Components/Animator.h>
#include <destrum/Components/Physics/Rigidbody.h>
#include <destrum/Components/Physics/BoxCollider.h>
#include <destrum/Components/Physics/SphereCollider.h>
void RegisterEngineComponents()
{
static bool registered = false;
if (registered) {
return;
}
registered = true;
ComponentFactory::Register("MeshRenderer", [](GameObject& owner) {
return owner.AddComponent<MeshRendererComponent>();
});
// ComponentFactory::Register("Rotator", [](GameObject& owner) {
// return owner.AddComponent<Rotator>();
// });
// ComponentFactory::Register("Spinner", [](GameObject& owner) {
// return owner.AddComponent<Spinner>();
// });
// ComponentFactory::Register("OrbitAndSpin", [](GameObject& owner) {
// return owner.AddComponent<OrbitAndSpin>();
// });
ComponentFactory::Register("Animator", [](GameObject& owner) {
return owner.AddComponent<Animator>();
});
ComponentFactory::Register("Rigidbody", [](GameObject& owner) {
return owner.AddComponent<Rigidbody>();
});
ComponentFactory::Register("BoxCollider", [](GameObject& owner) {
return owner.AddComponent<BoxCollider>();
});
ComponentFactory::Register("SphereCollider", [](GameObject& owner) {
return owner.AddComponent<SphereCollider>();
});
}
@@ -0,0 +1,223 @@
#include <destrum/Serialization/SceneSerializer.h>
#include <fstream>
#include <iostream>
#include <unordered_map>
#include <vector>
#include <nlohmann/json.hpp>
#include <destrum/Scene/Scene.h>
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/ObjectModel/Component.h>
#include <destrum/ObjectModel/ObjectId.h>
#include <destrum/Serialization/ComponentFactory.h>
using json = nlohmann::json;
bool SceneSerializer::Save(Scene& scene, const std::filesystem::path& path) {
scene.CommitPendingAdditions();
json root;
root["version"] = 1;
root["name"] = scene.GetName();
root["objects"] = json::array();
for (const auto& objectPtr : scene.GetObjects()) {
if (!objectPtr) {
continue;
}
const GameObject& object = *objectPtr;
json objectJson;
objectJson["id"] = object.GetId();
objectJson["name"] = object.GetName();
objectJson["active"] = object.IsActive();
// Transform should be saved separately because in your engine it is not a component.
// Replace this with your actual Transform getters.
objectJson["transform"] = {
// Example:
// { "position", { object.GetTransform().GetLocalPosition().x,
// object.GetTransform().GetLocalPosition().y,
// object.GetTransform().GetLocalPosition().z } },
// { "rotation", { ... } },
// { "scale", { ... } }
};
const Transform* parent = object.GetTransform().GetParent();
objectJson["parent"] = parent
? parent->GetOwner()->GetId()
: InvalidObjectId;
objectJson["components"] = json::array();
for (const auto& componentPtr : object.GetComponents()) {
if (!componentPtr) {
continue;
}
const Component& component = *componentPtr;
json componentJson;
componentJson["type"] = component.GetTypeName();
componentJson["enabled"] = component.isEnabled();
componentJson["data"] = component.Serialize();
objectJson["components"].push_back(componentJson);
}
root["objects"].push_back(objectJson);
}
std::ofstream file(path);
if (!file.is_open()) {
std::cerr << "Failed to open scene file for writing: " << path << '\n';
return false;
}
file << root.dump(4);
return true;
}
bool SceneSerializer::Load(Scene& scene, const std::filesystem::path& path) {
std::ifstream file(path);
if (!file.is_open()) {
std::cerr << "Failed to open scene file for reading: " << path << '\n';
return false;
}
json root;
try {
file >> root;
} catch (const std::exception& e) {
std::cerr << "Failed to parse scene file: " << e.what() << '\n';
return false;
}
scene.RemoveAll();
std::unordered_map<ObjectId, GameObject*> idMap;
std::vector<const json*> objectJsonList;
try {
const auto& objectsJson = root.at("objects");
// Pass 1:
// Create all GameObjects first.
for (const auto& objectJson : objectsJson) {
const auto id = objectJson.at("id").get<ObjectId>();
const std::string name = objectJson.value("name", "GameObject");
const bool active = objectJson.value("active", true);
GameObject* object = scene.CreateGameObject(name);
object->SetIdForDeserialization(id);
object->SetActive(active);
idMap[id] = object;
objectJsonList.push_back(&objectJson);
}
scene.CommitPendingAdditions();
// Pass 2:
// Restore transforms and parent-child hierarchy.
for (const json* objectJson : objectJsonList) {
const auto id = objectJson->at("id").get<ObjectId>();
GameObject* object = idMap.at(id);
// Replace this with your actual Transform deserialization.
if (objectJson->contains("transform")) {
const json& transformJson = objectJson->at("transform");
auto position = transformJson.at("position");
object->GetTransform().SetLocalPosition({
position[0].get<float>(),
position[1].get<float>(),
position[2].get<float>()
});
auto rotation = transformJson.at("rotation");
object->GetTransform().SetLocalRotation({
rotation[0].get<float>(),
rotation[1].get<float>(),
rotation[2].get<float>()
});
auto scale = transformJson.at("scale");
object->GetTransform().SetLocalScale({
scale[0].get<float>(),
scale[1].get<float>(),
scale[2].get<float>()
});
}
const ObjectId parentId = objectJson->value("parent", InvalidObjectId);
if (parentId != InvalidObjectId) {
auto parentIt = idMap.find(parentId);
if (parentIt != idMap.end()) {
object->GetTransform().SetParent(&parentIt->second->GetTransform());
} else {
std::cerr << "Parent not found while loading object " << id << '\n';
}
}
}
// Pass 3:
// Create and deserialize components.
for (const json* objectJson : objectJsonList) {
const auto id = objectJson->at("id").get<ObjectId>();
GameObject* object = idMap.at(id);
if (!objectJson->contains("components")) {
continue;
}
for (const auto& componentJson : objectJson->at("components")) {
const std::string type = componentJson.at("type").get<std::string>();
Component* component = ComponentFactory::Create(type, *object);
if (!component) {
std::cerr << "Unknown component type while loading scene: " << type << '\n';
continue;
}
component->SetEnabled(componentJson.value("enabled", true));
if (componentJson.contains("data")) {
component->Deserialize(componentJson.at("data"));
}
}
}
// Pass 4:
// Resolve object references now that all objects and components exist.
for (const auto& objectPtr : scene.GetObjects()) {
if (!objectPtr) {
continue;
}
for (const auto& componentPtr : objectPtr->GetComponents()) {
if (!componentPtr) {
continue;
}
componentPtr->ResolveReferences(idMap);
}
}
} catch (const std::exception& e) {
std::cerr << "Failed to load scene: " << e.what() << '\n';
scene.RemoveAll();
return false;
}
return true;
}