MetaCore/MetaCore/engine/scene/SceneSerializer.cpp

205 lines
7.9 KiB
C++

#include "MetaCore/engine/scene/SceneSerializer.h"
#include "MetaCore/foundation/FileSystem.h"
#include "MetaCore/foundation/Json.h"
namespace metacore::engine::scene {
using foundation::JsonValue;
namespace {
JsonValue array3(const std::array<float, 3>& values) {
JsonValue::array_type array;
array.emplace_back(values[0]);
array.emplace_back(values[1]);
array.emplace_back(values[2]);
return JsonValue(std::move(array));
}
std::array<float, 3> read_array3(const JsonValue& value, const std::array<float, 3>& fallback) {
if (!value.is_array() || value.as_array().size() != 3) {
return fallback;
}
return {
static_cast<float>(value.as_array()[0].as_number(fallback[0])),
static_cast<float>(value.as_array()[1].as_number(fallback[1])),
static_cast<float>(value.as_array()[2].as_number(fallback[2]))
};
}
std::string light_type_to_string(const LightComponent::Type type) {
switch (type) {
case LightComponent::Type::Directional:
return "directional";
case LightComponent::Type::Point:
return "point";
case LightComponent::Type::Spot:
return "spot";
}
return "directional";
}
LightComponent::Type light_type_from_string(const std::string& type) {
if (type == "point") {
return LightComponent::Type::Point;
}
if (type == "spot") {
return LightComponent::Type::Spot;
}
return LightComponent::Type::Directional;
}
JsonValue scene_object_to_json(const SceneObject& object) {
JsonValue::object_type root;
root["id"] = object.id;
root["parent_id"] = object.parent_id;
root["name"] = object.name;
JsonValue::object_type transform;
transform["position"] = array3(object.transform.position);
transform["rotation"] = array3(object.transform.rotation);
transform["scale"] = array3(object.transform.scale);
root["transform"] = JsonValue(std::move(transform));
if (object.has_mesh_renderer) {
JsonValue::object_type mesh;
mesh["mesh_asset_id"] = object.mesh_renderer.mesh_asset_id;
mesh["material_asset_id"] = object.mesh_renderer.material_asset_id;
mesh["visible"] = object.mesh_renderer.visible;
root["mesh_renderer"] = JsonValue(std::move(mesh));
}
if (object.has_light) {
JsonValue::object_type light;
light["type"] = light_type_to_string(object.light.type);
light["color"] = array3(object.light.color);
light["intensity"] = object.light.intensity;
light["range"] = object.light.range;
light["spot_angle_degrees"] = object.light.spot_angle_degrees;
root["light"] = JsonValue(std::move(light));
}
if (object.has_camera) {
JsonValue::object_type camera;
camera["fov_degrees"] = object.camera.fov_degrees;
camera["near_clip"] = object.camera.near_clip;
camera["far_clip"] = object.camera.far_clip;
camera["primary"] = object.camera.primary;
root["camera"] = JsonValue(std::move(camera));
}
if (object.has_ui_document) {
JsonValue::object_type ui;
ui["document_path"] = object.ui_document.document_path;
ui["stylesheet_path"] = object.ui_document.stylesheet_path;
ui["visible"] = object.ui_document.visible;
root["ui_document"] = JsonValue(std::move(ui));
}
if (object.has_collision) {
JsonValue::object_type collision;
collision["enabled"] = object.collision.enabled;
collision["shape"] = object.collision.shape;
root["collision"] = JsonValue(std::move(collision));
}
JsonValue::object_type editor;
editor["editor_only"] = object.editor_metadata.editor_only;
editor["selected"] = object.editor_metadata.selected;
editor["locked"] = object.editor_metadata.locked;
root["editor_metadata"] = JsonValue(std::move(editor));
return JsonValue(std::move(root));
}
SceneObject scene_object_from_json(const JsonValue& json) {
SceneObject object;
object.id = json["id"].as_string();
object.parent_id = json["parent_id"].as_string();
object.name = json["name"].as_string();
const auto& transform = json["transform"];
object.transform.position = read_array3(transform["position"], object.transform.position);
object.transform.rotation = read_array3(transform["rotation"], object.transform.rotation);
object.transform.scale = read_array3(transform["scale"], object.transform.scale);
if (json.contains("mesh_renderer")) {
const auto& mesh = json["mesh_renderer"];
object.has_mesh_renderer = true;
object.mesh_renderer.mesh_asset_id = mesh["mesh_asset_id"].as_string();
object.mesh_renderer.material_asset_id = mesh["material_asset_id"].as_string();
object.mesh_renderer.visible = mesh["visible"].as_bool(true);
}
if (json.contains("light")) {
const auto& light = json["light"];
object.has_light = true;
object.light.type = light_type_from_string(light["type"].as_string());
object.light.color = read_array3(light["color"], object.light.color);
object.light.intensity = static_cast<float>(light["intensity"].as_number(object.light.intensity));
object.light.range = static_cast<float>(light["range"].as_number(object.light.range));
object.light.spot_angle_degrees = static_cast<float>(light["spot_angle_degrees"].as_number(object.light.spot_angle_degrees));
}
if (json.contains("camera")) {
const auto& camera = json["camera"];
object.has_camera = true;
object.camera.fov_degrees = static_cast<float>(camera["fov_degrees"].as_number(object.camera.fov_degrees));
object.camera.near_clip = static_cast<float>(camera["near_clip"].as_number(object.camera.near_clip));
object.camera.far_clip = static_cast<float>(camera["far_clip"].as_number(object.camera.far_clip));
object.camera.primary = camera["primary"].as_bool(false);
}
if (json.contains("ui_document")) {
const auto& ui = json["ui_document"];
object.has_ui_document = true;
object.ui_document.document_path = ui["document_path"].as_string();
object.ui_document.stylesheet_path = ui["stylesheet_path"].as_string();
object.ui_document.visible = ui["visible"].as_bool(true);
}
if (json.contains("collision")) {
const auto& collision = json["collision"];
object.has_collision = true;
object.collision.enabled = collision["enabled"].as_bool(false);
object.collision.shape = collision["shape"].as_string();
}
const auto& editor = json["editor_metadata"];
object.editor_metadata.editor_only = editor["editor_only"].as_bool(false);
object.editor_metadata.selected = editor["selected"].as_bool(false);
object.editor_metadata.locked = editor["locked"].as_bool(false);
return object;
}
JsonValue scene_to_json(const Scene& scene) {
JsonValue::object_type root;
root["name"] = scene.name;
JsonValue::array_type objects;
for (const auto& object : scene.objects) {
objects.push_back(scene_object_to_json(object));
}
root["objects"] = JsonValue(std::move(objects));
return JsonValue(std::move(root));
}
Scene scene_from_json(const JsonValue& json) {
Scene scene;
scene.name = json["name"].as_string();
for (const auto& object : json["objects"].as_array()) {
scene.objects.push_back(scene_object_from_json(object));
}
return scene;
}
} // namespace
bool SceneSerializer::save_scene(const std::filesystem::path& scene_file, const Scene& scene) const {
return foundation::write_text_file(scene_file, scene_to_json(scene).stringify(2));
}
bool SceneSerializer::load_scene(const std::filesystem::path& scene_file, Scene& out_scene) const {
const auto text = foundation::read_text_file(scene_file);
if (!text.has_value()) {
return false;
}
out_scene = scene_from_json(JsonValue::parse(*text));
return true;
}
} // namespace metacore::engine::scene