/* * Copyright (C) 2021 Christopher J. Howard * * This file is part of Antkeeper source code. * * Antkeeper source code is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Antkeeper source code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Antkeeper source code. If not, see . */ #include "resource-loader.hpp" #include "resource-manager.hpp" #include "render/model.hpp" #include "game/component/atmosphere.hpp" #include "game/component/behavior.hpp" #include "game/component/collision.hpp" #include "game/component/diffuse-reflector.hpp" #include "game/component/terrain.hpp" #include "game/component/transform.hpp" #include "game/component/model.hpp" #include "game/component/orbit.hpp" #include "game/component/blackbody.hpp" #include "game/component/celestial-body.hpp" #include "entity/archetype.hpp" #include "entity/ebt.hpp" #include "physics/orbit/elements.hpp" #include "resources/json.hpp" #include static bool load_component_atmosphere(entity::archetype& archetype, const json& element) { game::component::atmosphere component; if (element.contains("upper_limit")) component.upper_limit = element["upper_limit"].get(); if (element.contains("index_of_refraction")) component.index_of_refraction = element["index_of_refraction"].get(); if (element.contains("rayleigh_concentration")) component.rayleigh_concentration = element["rayleigh_concentration"].get(); if (element.contains("rayleigh_scale_height")) component.rayleigh_scale_height = element["rayleigh_scale_height"].get(); if (element.contains("mie_concentration")) component.mie_concentration = element["mie_concentration"].get(); if (element.contains("mie_scale_height")) component.mie_scale_height = element["mie_scale_height"].get(); if (element.contains("mie_anisotropy")) component.mie_anisotropy = element["mie_anisotropy"].get(); if (element.contains("mie_albedo")) component.mie_albedo = element["mie_albedo"].get(); if (element.contains("ozone_concentration")) component.ozone_concentration = element["ozone_concentration"].get(); if (element.contains("ozone_lower_limit")) component.ozone_lower_limit = element["ozone_lower_limit"].get(); if (element.contains("ozone_upper_limit")) component.ozone_upper_limit = element["ozone_upper_limit"].get(); if (element.contains("ozone_mode")) component.ozone_mode = element["ozone_mode"].get(); if (element.contains("airglow_illuminance")) { const auto& airglow_illuminance = element["airglow_illuminance"]; component.airglow_illuminance.x() = airglow_illuminance[0].get(); component.airglow_illuminance.y() = airglow_illuminance[1].get(); component.airglow_illuminance.z() = airglow_illuminance[2].get(); } archetype.stamps.push_back ( [component](entt::handle& handle) { handle.emplace_or_replace(component); } ); return true; } static bool load_component_behavior(entity::archetype& archetype, resource_manager& resource_manager, const json& element) { game::component::behavior component; component.behavior_tree = nullptr; if (element.contains("file")) { component.behavior_tree = resource_manager.load(element["file"].get()); } archetype.stamps.push_back ( [component](entt::handle& handle) { handle.emplace_or_replace(component); } ); return (component.behavior_tree != nullptr); } static bool load_component_blackbody(entity::archetype& archetype, const json& element) { game::component::blackbody component; component.temperature = 0.0; if (element.contains("temperature")) component.temperature = element["temperature"].get(); archetype.stamps.push_back ( [component](entt::handle& handle) { handle.emplace_or_replace(component); } ); return true; } static bool load_component_celestial_body(entity::archetype& archetype, const json& element) { game::component::celestial_body component; if (element.contains("radius")) component.radius = element["radius"].get(); if (element.contains("mass")) component.mass = element["mass"].get(); if (element.contains("pole_ra")) { component.pole_ra.clear(); auto& pole_ra_element = element["pole_ra"]; for (auto it = pole_ra_element.rbegin(); it != pole_ra_element.rend(); ++it) component.pole_ra.push_back(math::radians(it->get())); } if (element.contains("pole_dec")) { component.pole_dec.clear(); auto& pole_dec_element = element["pole_dec"]; for (auto it = pole_dec_element.rbegin(); it != pole_dec_element.rend(); ++it) component.pole_dec.push_back(math::radians(it->get())); } if (element.contains("prime_meridian")) { component.prime_meridian.clear(); auto& prime_meridian_element = element["prime_meridian"]; for (auto it = prime_meridian_element.rbegin(); it != prime_meridian_element.rend(); ++it) component.prime_meridian.push_back(math::radians(it->get())); } if (element.contains("albedo")) component.albedo = element["albedo"].get(); archetype.stamps.push_back ( [component](entt::handle& handle) { handle.emplace_or_replace(component); } ); return true; } static bool load_component_collision(entity::archetype& archetype, resource_manager& resource_manager, const json& element) { game::component::collision component; component.mesh = nullptr; if (element.contains("file")) { component.mesh = resource_manager.load(element["file"].get()); } archetype.stamps.push_back ( [component](entt::handle& handle) { handle.emplace_or_replace(component); } ); return (component.mesh != nullptr); } static bool load_component_diffuse_reflector(entity::archetype& archetype, const json& element) { game::component::diffuse_reflector component; component.albedo = 0.0; if (element.contains("albedo")) component.albedo = element["albedo"].get(); archetype.stamps.push_back ( [component](entt::handle& handle) { handle.emplace_or_replace(component); } ); return true; } static bool load_component_model(entity::archetype& archetype, resource_manager& resource_manager, const json& element) { game::component::model component; component.instance_count = 0; //component.layers = ~0; component.layers = 1; if (element.contains("file")) { component.render_model = resource_manager.load(element["file"].get()); } archetype.stamps.push_back ( [component](entt::handle& handle) { handle.emplace_or_replace(component); } ); return true; } static bool load_component_orbit(entity::archetype& archetype, const json& element) { game::component::orbit component; component.parent = entt::null; component.ephemeris_index = -1; component.scale = 1.0; component.position = {0, 0, 0}; if (element.contains("ephemeris_index")) component.ephemeris_index = element["ephemeris_index"].get(); if (element.contains("scale")) component.scale = element["scale"].get(); archetype.stamps.push_back ( [component](entt::handle& handle) { handle.emplace_or_replace(component); } ); return true; } static bool load_component_transform(entity::archetype& archetype, const json& element) { game::component::transform component; component.local = math::transform::identity; component.warp = true; if (element.contains("translation")) { auto translation = element["translation"]; component.local.translation.x() = translation[0].get(); component.local.translation.y() = translation[1].get(); component.local.translation.z() = translation[2].get(); } if (element.contains("rotation")) { auto translation = element["rotation"]; component.local.rotation.w() = translation[0].get(); component.local.rotation.x() = translation[1].get(); component.local.rotation.y() = translation[2].get(); component.local.rotation.z() = translation[3].get(); } if (element.contains("scale")) { auto translation = element["scale"]; component.local.scale.x() = translation[0].get(); component.local.scale.y() = translation[1].get(); component.local.scale.z() = translation[2].get(); } component.world = component.local; archetype.stamps.push_back ( [component](entt::handle& handle) { handle.emplace_or_replace(component); } ); return true; } static bool load_component(entity::archetype& archetype, resource_manager& resource_manager, json::const_iterator element) { if (element.key() == "atmosphere") return load_component_atmosphere(archetype, element.value()); if (element.key() == "behavior") return load_component_behavior(archetype, resource_manager, element.value()); if (element.key() == "blackbody") return load_component_blackbody(archetype, element.value()); if (element.key() == "celestial_body") return load_component_celestial_body(archetype, element.value()); if (element.key() == "collision") return load_component_collision(archetype, resource_manager, element.value()); if (element.key() == "diffuse_reflector") return load_component_diffuse_reflector(archetype, element.value()); if (element.key() == "model") return load_component_model(archetype, resource_manager, element.value()); if (element.key() == "orbit") return load_component_orbit(archetype, element.value()); if (element.key() == "transform") return load_component_transform(archetype, element.value()); //throw std::runtime_error("Unknown component type \"" + element.key() + "\""); return false; } template <> entity::archetype* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) { // Allocate archetype entity::archetype* archetype = new entity::archetype(); // Read file into buffer std::size_t size = static_cast(PHYSFS_fileLength(file)); std::string buffer; buffer.resize(size); PHYSFS_readBytes(file, &buffer[0], size); // Parse JSON data from file buffer json data = nlohmann::json::parse(buffer, nullptr, true, true); // Load components from table rows for (json::const_iterator element = data.cbegin(); element != data.cend(); ++element) { if (!load_component(*archetype, *resource_manager, element)) { throw std::runtime_error("Failed to load component \"" + element.key() + "\""); } } return archetype; }