feat: add ObjectRef for scene loading to not rely on runtime id's

This commit is contained in:
2026-10-18 17:15:51 +02:00
parent 94fa2bb4b2
commit d21c2ccfd7
10 changed files with 262 additions and 35 deletions
+1
View File
@@ -96,6 +96,7 @@ set(SRC_FILES
"src/ObjectModel/Component.cpp"
"src/ObjectModel/GameObject.cpp"
"src/ObjectModel/Object.cpp"
"src/ObjectModel/ObjectRef.cpp"
"src/ObjectModel/Transform.cpp"
"src/Scene/Scene.cpp"
@@ -44,9 +44,6 @@ public:
[[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() {
@@ -0,0 +1,37 @@
#ifndef DESTRUM_OBJECTREF_H
#define DESTRUM_OBJECTREF_H
#include <unordered_map>
#include <nlohmann/json_fwd.hpp>
#include <destrum/ObjectModel/ObjectId.h>
class GameObject;
class Scene;
class ObjectRef final {
public:
using ObjectMap = std::unordered_map<ObjectId, GameObject*>;
ObjectRef() = default;
explicit ObjectRef(ObjectId id) : m_Id(id) {}
explicit ObjectRef(const GameObject& object);
[[nodiscard]] ObjectId GetId() const { return m_Id; }
[[nodiscard]] bool IsSet() const { return m_Id != InvalidObjectId; }
[[nodiscard]] GameObject* Get(const Scene& scene) const;
bool Resolve(const ObjectMap& objects);
bool operator==(const ObjectRef&) const = default;
private:
ObjectId m_Id{InvalidObjectId};
};
void to_json(nlohmann::json& json, const ObjectRef& ref);
void from_json(const nlohmann::json& json, ObjectRef& ref);
#endif //DESTRUM_OBJECTREF_H
-13
View File
@@ -39,19 +39,6 @@ GameObject::GameObject(const std::string& name)
m_Id(AllocateId()) {
}
void GameObject::SetIdForDeserialization(ObjectId id) {
if (id == InvalidObjectId || id >= std::numeric_limits<ObjectId>::max() - 1) {
throw std::out_of_range("Object ID is reserved or cannot be incremented");
}
m_Id = id;
// Prevent newly created objects from reusing loaded IDs.
if (id != std::numeric_limits<ObjectId>::max() && id >= s_NextId) {
s_NextId = id + 1;
}
}
ObjectId GameObject::AllocateId() {
if (s_NextId == InvalidObjectId ||
s_NextId >= std::numeric_limits<ObjectId>::max() - 1) {
+38
View File
@@ -0,0 +1,38 @@
#include <destrum/ObjectModel/ObjectRef.h>
#include <nlohmann/json.hpp>
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/Scene/Scene.h>
ObjectRef::ObjectRef(const GameObject& object)
: m_Id(object.GetId()) {
}
GameObject* ObjectRef::Get(const Scene& scene) const {
return scene.FindGameObject(m_Id);
}
bool ObjectRef::Resolve(const ObjectMap& objects) {
if (m_Id == InvalidObjectId) {
return true;
}
const auto it = objects.find(m_Id);
if (it == objects.end() || it->second == nullptr) {
// Keeping an unmatched file id would alias an unrelated runtime object.
m_Id = InvalidObjectId;
return false;
}
m_Id = it->second->GetId();
return true;
}
void to_json(nlohmann::json& json, const ObjectRef& ref) {
json = ref.GetId();
}
void from_json(const nlohmann::json& json, ObjectRef& ref) {
ref = json.is_number_unsigned() ? ObjectRef(json.get<ObjectId>()) : ObjectRef{};
}
@@ -563,8 +563,8 @@ bool SceneSerializer::ConstructScene(Scene& scene, LoadResult& result,
const std::string name = objectJson.value("name", "GameObject");
const bool active = objectJson.value("active", true);
// File ids are local to the file, every loaded object gets a fresh runtime id
GameObject* object = stagingScene.CreateGameObject(name);
object->SetIdForDeserialization(id);
object->SetActive(active);
idMap.emplace(id, object);
@@ -687,11 +687,14 @@ bool SceneSerializer::ConstructScene(Scene& scene, LoadResult& result,
scene.m_name = root.at("name").get<std::string>();
}
scene.m_SelectedObjectId = InvalidObjectId;
scene.m_CurrentCameraId = root.value("currentCamera", InvalidObjectId);
if (scene.m_CurrentCameraId != InvalidObjectId && !idMap.contains(scene.m_CurrentCameraId)) {
spdlog::warn("Scene \"{}\" current camera id {} does not exist, ignoring it",
scene.m_name, scene.m_CurrentCameraId);
scene.m_CurrentCameraId = InvalidObjectId;
scene.m_CurrentCameraId = InvalidObjectId;
if (const ObjectId cameraFileId = root.value("currentCamera", InvalidObjectId); cameraFileId != InvalidObjectId) {
if (const auto it = idMap.find(cameraFileId); it != idMap.end()) {
scene.m_CurrentCameraId = it->second->GetId();
} else {
spdlog::warn("Scene \"{}\" current camera id {} does not exist, ignoring it",
scene.m_name, cameraFileId);
}
}
scene.m_Environment = SceneEnvironment{};
if (root.contains("environment") && root.at("environment").is_object()) {