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
@@ -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;
}