feat: add ObjectRef for scene loading to not rely on runtime id's
This commit is contained in:
@@ -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
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
Reference in New Issue
Block a user