205 lines
7.9 KiB
C++
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
|