From 8165f8da3af0fc4e34a386c128e411f86e23bd7d Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Sun, 19 Feb 2023 16:07:17 +0800 Subject: [PATCH] Replace CBOR-based model loader with a custom binary format loader --- src/animation/bone.hpp | 28 +- src/animation/pose.cpp | 2 +- src/game/context.hpp | 2 + src/game/controls.cpp | 52 +++- src/game/controls.hpp | 2 + src/game/state/boot.cpp | 1 + src/game/state/main-menu.cpp | 6 +- src/game/state/nuptial-flight.cpp | 17 +- src/resources/model-loader.cpp | 485 ++++++++++++++++-------------- 9 files changed, 349 insertions(+), 246 deletions(-) diff --git a/src/animation/bone.hpp b/src/animation/bone.hpp index f3c5374..a99c9e1 100644 --- a/src/animation/bone.hpp +++ b/src/animation/bone.hpp @@ -27,10 +27,10 @@ /** * Skeletal animation bone identifier, consisting of a bone index in the lower half, and a parent bone index in the upper half. */ -typedef std::uint16_t bone; +typedef std::uint32_t bone; /// Mask to extract the index of a bone. -constexpr bone bone_index_mask = 0xFF; +constexpr bone bone_index_mask = 0xffff; /** * Bone index comparison function object. @@ -54,7 +54,7 @@ struct bone_index_compare * @param parent_index Index of the parent bone. * @return Bone identifier. */ -bone make_bone(std::uint8_t index, std::uint8_t parent_index); +bone make_bone(std::uint16_t index, std::uint16_t parent_index); /** * Constructs an orphan bone identifier. @@ -62,7 +62,7 @@ bone make_bone(std::uint8_t index, std::uint8_t parent_index); * @param index Index of the orphan bone. * @return Orphan bone identifier. */ -bone make_bone(std::uint8_t index); +bone make_bone(std::uint16_t index); /** * Returns the index of a bone. @@ -70,7 +70,7 @@ bone make_bone(std::uint8_t index); * @param x Bone identifier. * @return Index of the bone. */ -std::uint8_t bone_index(bone x); +std::uint16_t bone_index(bone x); /** * Returns the parent index of a bone. @@ -78,7 +78,7 @@ std::uint8_t bone_index(bone x); * @param x Bone identifier. * @return Index of the parent bone. */ -std::uint8_t bone_parent_index(bone x); +std::uint16_t bone_parent_index(bone x); /** * Returns `true` if a bone has a parent, `false` otherwise. @@ -93,29 +93,29 @@ inline bool bone_index_compare::operator()(const bone& lhs, const bone& rhs) con return (lhs & bone_index_mask) < (rhs & bone_index_mask); } -inline bone make_bone(std::uint8_t index, std::uint8_t parent_index) +inline bone make_bone(std::uint16_t index, std::uint16_t parent_index) { - return (static_cast(parent_index) << 8) | index; + return (static_cast(parent_index) << 16) | index; } -inline bone make_bone(std::uint8_t index) +inline bone make_bone(std::uint16_t index) { return make_bone(index, index); } -inline std::uint8_t bone_index(bone x) +inline std::uint16_t bone_index(bone x) { - return static_cast(x & bone_index_mask); + return static_cast(x & bone_index_mask); } -inline std::uint8_t bone_parent_index(bone x) +inline std::uint16_t bone_parent_index(bone x) { - return static_cast(x >> 8); + return static_cast(x >> 16); } inline bool bone_has_parent(bone x) { - return (x & bone_index_mask) != (x >> 8); + return (x & bone_index_mask) != (x >> 16); } #endif // ANTKEEPER_ANIMATION_BONE_HPP diff --git a/src/animation/pose.cpp b/src/animation/pose.cpp index c53dbf0..e01ec0f 100644 --- a/src/animation/pose.cpp +++ b/src/animation/pose.cpp @@ -51,7 +51,7 @@ void matrix_palette(const pose& inverse_bind_pose, const pose& pose, float4x4* p { for (auto&& [bone, transform]: pose) { - std::uint8_t index = ::bone_index(bone); + auto index = ::bone_index(bone); palette[index] = math::matrix_cast(inverse_bind_pose.at(bone) * transform); } } diff --git a/src/game/context.hpp b/src/game/context.hpp index 1fbf01d..eedf721 100644 --- a/src/game/context.hpp +++ b/src/game/context.hpp @@ -201,6 +201,8 @@ struct context input::action move_up_action; input::action move_down_action; input::action pause_action; + std::vector> movement_action_subscriptions; + // Debugging scene::text* frame_time_text; diff --git a/src/game/controls.cpp b/src/game/controls.cpp index 4b19150..e38c4ea 100644 --- a/src/game/controls.cpp +++ b/src/game/controls.cpp @@ -21,6 +21,7 @@ #include "game/graphics.hpp" #include "game/menu.hpp" #include "game/control-profile.hpp" +#include "game/state/pause-menu.hpp" #include "resources/resource-manager.hpp" #include "resources/json.hpp" #include "input/modifier-key.hpp" @@ -320,7 +321,38 @@ void setup_menu_controls(game::context& ctx) void setup_game_controls(game::context& ctx) { - + // Setup pause control + ctx.movement_action_subscriptions.emplace_back + ( + ctx.pause_action.get_activated_channel().subscribe + ( + [&ctx](const auto& event) + { + if (!ctx.resume_callback) + { + // Queue disable game controls and push pause state + ctx.function_queue.push + ( + [&ctx]() + { + // Disable game controls + game::disable_game_controls(ctx); + + // Push pause menu state + ctx.state_machine.emplace(new game::state::pause_menu(ctx)); + } + ); + + // Set resume callback + ctx.resume_callback = [&ctx]() + { + enable_game_controls(ctx); + ctx.resume_callback = nullptr; + }; + } + } + ) + ); } void enable_window_controls(game::context& ctx) @@ -429,6 +461,11 @@ void enable_menu_controls(game::context& ctx) ); } +void enable_game_controls(game::context& ctx) +{ + ctx.movement_actions.connect(ctx.input_manager->get_event_queue()); +} + void disable_window_controls(game::context& ctx) { ctx.window_actions.disconnect(); @@ -449,4 +486,17 @@ void disable_menu_controls(game::context& ctx) ctx.menu_mouse_subscriptions.clear(); } +void disable_game_controls(game::context& ctx) +{ + ctx.movement_actions.disconnect(); + + ctx.move_forward_action.reset(); + ctx.move_back_action.reset(); + ctx.move_left_action.reset(); + ctx.move_right_action.reset(); + ctx.move_up_action.reset(); + ctx.move_down_action.reset(); + ctx.pause_action.reset(); +} + } // namespace game diff --git a/src/game/controls.hpp b/src/game/controls.hpp index 7c87480..a6de50a 100644 --- a/src/game/controls.hpp +++ b/src/game/controls.hpp @@ -56,9 +56,11 @@ void setup_game_controls(game::context& ctx); void enable_window_controls(game::context& ctx); void enable_menu_controls(game::context& ctx); +void enable_game_controls(game::context& ctx); void disable_window_controls(game::context& ctx); void disable_menu_controls(game::context& ctx); +void disable_game_controls(game::context& ctx); } // namespace game diff --git a/src/game/state/boot.cpp b/src/game/state/boot.cpp index 405989f..eff825b 100644 --- a/src/game/state/boot.cpp +++ b/src/game/state/boot.cpp @@ -1166,6 +1166,7 @@ void boot::setup_controls() // Setup action callbacks setup_window_controls(ctx); setup_menu_controls(ctx); + setup_game_controls(ctx); // Enable window controls enable_window_controls(ctx); diff --git a/src/game/state/main-menu.cpp b/src/game/state/main-menu.cpp index c39690f..8969eb5 100644 --- a/src/game/state/main-menu.cpp +++ b/src/game/state/main-menu.cpp @@ -32,6 +32,7 @@ #include "game/state/collection-menu.hpp" #include "game/state/extras-menu.hpp" #include "game/state/nuptial-flight.hpp" +#include "game/state/nest-selection.hpp" #include "game/state/options-menu.hpp" #include "game/strings.hpp" #include "game/world.hpp" @@ -133,8 +134,9 @@ main_menu::main_menu(game::context& ctx, bool fade_in): [&ctx]() { ctx.state_machine.pop(); - //ctx.state_machine.emplace(new game::state::nuptial_flight(ctx)); - ctx.state_machine.emplace(new game::state::collection_menu(ctx)); + ctx.state_machine.emplace(new game::state::nuptial_flight(ctx)); + //ctx.state_machine.emplace(new game::state::collection_menu(ctx)); + //ctx.state_machine.emplace(new game::state::nest_selection(ctx)); } ); }; diff --git a/src/game/state/nuptial-flight.cpp b/src/game/state/nuptial-flight.cpp index 44024ee..e439716 100644 --- a/src/game/state/nuptial-flight.cpp +++ b/src/game/state/nuptial-flight.cpp @@ -129,11 +129,17 @@ nuptial_flight::nuptial_flight(game::context& ctx): satisfy_camera_rig_constraints(); // Queue fade in - ctx.fade_transition_color->set_value({1, 1, 1}); - ctx.function_queue.push(std::bind(&screen_transition::transition, ctx.fade_transition, config::nuptial_flight_fade_in_duration, true, ease::out_sine, true, nullptr)); + ctx.fade_transition_color->set_value({0, 0, 0}); + ctx.function_queue.push(std::bind(&screen_transition::transition, ctx.fade_transition, 1.0f, true, ease::out_sine, true, nullptr)); - // Queue control setup - ctx.function_queue.push(std::bind(&nuptial_flight::enable_controls, this)); + // Queue enable game controls + ctx.function_queue.push + ( + [&ctx]() + { + game::enable_game_controls(ctx); + } + ); debug::log::trace("Entered nuptial flight state"); } @@ -142,6 +148,9 @@ nuptial_flight::~nuptial_flight() { debug::log::trace("Exiting nuptial flight state..."); + // Disable game controls + game::disable_game_controls(ctx); + // Deselect selected entity select_entity(entt::null); diff --git a/src/resources/model-loader.cpp b/src/resources/model-loader.cpp index c9c343b..fed3f98 100644 --- a/src/resources/model-loader.cpp +++ b/src/resources/model-loader.cpp @@ -19,278 +19,315 @@ #include "resources/resource-loader.hpp" #include "resources/resource-manager.hpp" +#include "resources/deserializer.hpp" #include "render/model.hpp" #include "render/vertex-attribute.hpp" #include "gl/vertex-attribute.hpp" #include "gl/drawing-mode.hpp" -#include "utility/fundamental-types.hpp" #include "math/numbers.hpp" -#include -#include -#include -#include -#include +#include -static const float3 barycentric_coords[3] = -{ - float3{1, 0, 0}, - float3{0, 1, 0}, - float3{0, 0, 1} -}; +inline constexpr std::uint16_t vertex_attribute_position = 0b0000000000000001; +inline constexpr std::uint16_t vertex_attribute_uv = 0b0000000000000010; +inline constexpr std::uint16_t vertex_attribute_normal = 0b0000000000000100; +inline constexpr std::uint16_t vertex_attribute_tangent = 0b0000000000001000; +inline constexpr std::uint16_t vertex_attribute_color = 0b0000000000010000; +inline constexpr std::uint16_t vertex_attribute_bone = 0b0000000000100000; +inline constexpr std::uint16_t vertex_attribute_barycentric = 0b0000000001000000; +inline constexpr std::uint16_t vertex_attribute_morph_target = 0b0000000010000000; +inline constexpr std::uint16_t vertex_attribute_index = 0b0000000100000000; template <> render::model* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) { - // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); - std::vector buffer(size); - PHYSFS_readBytes(file, &buffer.front(), size); + deserialize_context ctx(file); + + // Read vertex format + std::uint16_t vertex_format_flags = 0; + ctx.read16(reinterpret_cast(&vertex_format_flags), 1); - // Parse CBOR in file buffer - nlohmann::json json = nlohmann::json::from_cbor(buffer); + // Read bone per vertex (if any) + std::uint8_t bones_per_vertex = 0; + if (vertex_format_flags & vertex_attribute_bone) + { + ctx.read8(reinterpret_cast(&bones_per_vertex), 1); + } - // Find model name - std::string model_name; - if (auto it = json.find("name"); it != json.end()) - model_name = it.value(); + // Read vertex count + std::uint32_t vertex_count = 0; + ctx.read32(reinterpret_cast(&vertex_count), 1); - // Load attributes - std::unordered_map>> attributes; - if (auto attributes_node = json.find("attributes"); attributes_node != json.end()) + // Determine vertex size + std::size_t vertex_size = 0; + if (vertex_format_flags & vertex_attribute_position) { - for (const auto& attribute_node: attributes_node.value().items()) - { - // Look up attribute name - std::string attribute_name; - if (auto type_node = attribute_node.value().find("name"); type_node != attribute_node.value().end()) - attribute_name = type_node.value().get(); - - // Allocate attribute in attribute map - auto& attribute = attributes[attribute_name]; - std::size_t& attribute_size = std::get<0>(attribute); - std::vector& attribute_data = std::get<1>(attribute); - - // Look up attribute size (per vertex) - attribute_size = 0; - if (auto size_node = attribute_node.value().find("size"); size_node != attribute_node.value().end()) - attribute_size = size_node.value().get(); - - // Look up attribute data - if (auto data_node = attribute_node.value().find("data"); data_node != attribute_node.value().end()) - { - // Resize attribute data - attribute_data.resize(data_node.value().size()); - - // Fill attribute data - float* v = &attribute_data.front(); - for (const auto& element: data_node.value()) - *(v++) = element.get(); - } - } + vertex_size += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_uv) + { + vertex_size += sizeof(float) * 2; + } + if (vertex_format_flags & vertex_attribute_normal) + { + vertex_size += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_tangent) + { + vertex_size += sizeof(float) * 4; + } + if (vertex_format_flags & vertex_attribute_color) + { + vertex_size += sizeof(float) * 4; + } + if (vertex_format_flags & vertex_attribute_bone) + { + vertex_size += sizeof(std::uint32_t) * bones_per_vertex; + vertex_size += sizeof(float) * bones_per_vertex; + } + if (vertex_format_flags & vertex_attribute_barycentric) + { + vertex_size += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_morph_target) + { + vertex_size += sizeof(float) * 3; } - // Load bounds - geom::aabb bounds = + // Allocate vertex data + std::byte* vertex_data = new std::byte[vertex_count * vertex_size]; + + // Read vertices + if constexpr (std::endian::native == std::endian::little) { - {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}, - {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::infinity()} - }; - if (auto bounds_node = json.find("bounds"); bounds_node != json.end()) + ctx.read8(vertex_data, vertex_count * vertex_size); + } + else { - if (auto min_node = bounds_node.value().find("min"); min_node != bounds_node.value().end()) + std::byte* vertex_data_offset = vertex_data; + for (std::size_t i = 0; i < vertex_count; ++i) { - float* v = &bounds.min_point.x(); - for (const auto& element: min_node.value()) - *(v++) = element.get(); - } - - if (auto max_node = bounds_node.value().find("max"); max_node != bounds_node.value().end()) - { - float* v = &bounds.max_point.x(); - for (const auto& element: max_node.value()) - *(v++) = element.get(); + if (vertex_format_flags & vertex_attribute_position) + { + ctx.read32(vertex_data_offset, 3); + vertex_data_offset += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_uv) + { + ctx.read32(vertex_data_offset, 2); + vertex_data_offset += sizeof(float) * 2; + } + if (vertex_format_flags & vertex_attribute_normal) + { + ctx.read32(vertex_data_offset, 3); + vertex_data_offset += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_tangent) + { + ctx.read32(vertex_data_offset, 4); + vertex_data_offset += sizeof(float) * 4; + } + if (vertex_format_flags & vertex_attribute_color) + { + ctx.read32(vertex_data_offset, 4); + vertex_data_offset += sizeof(float) * 4; + } + if (vertex_format_flags & vertex_attribute_bone) + { + ctx.read32(vertex_data_offset, bones_per_vertex); + ctx.read32(vertex_data_offset, bones_per_vertex); + + vertex_data_offset += sizeof(std::uint32_t) * bones_per_vertex; + vertex_data_offset += sizeof(float) * bones_per_vertex; + } + if (vertex_format_flags & vertex_attribute_barycentric) + { + ctx.read32(vertex_data_offset, 3); + vertex_data_offset += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_morph_target) + { + ctx.read32(vertex_data_offset, 3); + vertex_data_offset += sizeof(float) * 3; + } } } + // Read geometry bounds + geom::aabb bounds; + ctx.read32(reinterpret_cast(bounds.min_point.data()), 3); + ctx.read32(reinterpret_cast(bounds.max_point.data()), 3); + // Allocate a model render::model* model = new render::model(); // Set the model bounds model->set_bounds(bounds); - // Calculate vertex size, count, and stride - std::size_t vertex_size = 0; - std::size_t vertex_count = 0; - for (auto it = attributes.begin(); it != attributes.end(); ++it) + // Resize model VBO and upload vertex data + gl::vertex_buffer* vbo = model->get_vertex_buffer(); + vbo->resize(vertex_count * vertex_size, vertex_data); + + // Free vertex data + delete[] vertex_data; + + // Bind vertex attributes to VAO + gl::vertex_array* vao = model->get_vertex_array(); + gl::vertex_attribute attribute; + attribute.buffer = vbo; + attribute.offset = 0; + attribute.stride = vertex_size; + if (vertex_format_flags & vertex_attribute_position) { - vertex_size += std::get<0>(it->second); - vertex_count = std::get<1>(it->second).size() / std::get<0>(it->second); + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 3; + vao->bind(render::vertex_attribute::position, attribute); + attribute.offset += sizeof(float) * attribute.components; } - std::size_t vertex_stride = sizeof(float) * vertex_size; - - // Build interleaved vertex data buffer - float* vertex_data = new float[vertex_size * vertex_count]; - float* v = &vertex_data[0]; - for (std::size_t i = 0; i < vertex_count; ++i) + if (vertex_format_flags & vertex_attribute_uv) { - for (auto it = attributes.begin(); it != attributes.end(); ++it) - { - std::size_t attribute_size = std::get<0>(it->second); - const float* a = &(std::get<1>(it->second)[i * attribute_size]); - - for (std::size_t j = 0; j < attribute_size; ++j) - *(v++) = *(a++); - } + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 2; + vao->bind(render::vertex_attribute::uv, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_normal) + { + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 3; + vao->bind(render::vertex_attribute::normal, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_tangent) + { + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 4; + vao->bind(render::vertex_attribute::tangent, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_color) + { + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 4; + vao->bind(render::vertex_attribute::color, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_bone) + { + attribute.type = gl::vertex_attribute_type::uint_16; + attribute.components = bones_per_vertex; + vao->bind(render::vertex_attribute::bone_index, attribute); + attribute.offset += sizeof(std::uint32_t) * attribute.components; + + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = bones_per_vertex; + vao->bind(render::vertex_attribute::bone_weight, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_barycentric) + { + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 3; + vao->bind(render::vertex_attribute::barycentric, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_morph_target) + { + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 3; + vao->bind(render::vertex_attribute::target, attribute); + attribute.offset += sizeof(float) * attribute.components; } - // Resize VBO and upload vertex data - gl::vertex_buffer* vbo = model->get_vertex_buffer(); - vbo->resize(sizeof(float) * vertex_size * vertex_count, vertex_data); - - // Free interleaved vertex data buffer - delete[] vertex_data; + // Read material count + std::uint16_t material_count = 0; + ctx.read16(reinterpret_cast(&material_count), 1); - // Map attribute names to locations - static const std::unordered_map attribute_location_map = + // Read materials + for (std::uint16_t i = 0; i < material_count; ++i) { - {"position", render::vertex_attribute::position}, - {"texcoord", render::vertex_attribute::uv}, - {"normal", render::vertex_attribute::normal}, - {"tangent", render::vertex_attribute::tangent}, - {"color", render::vertex_attribute::color}, - {"bone_index", render::vertex_attribute::bone_index}, - {"bone_weight", render::vertex_attribute::bone_weight}, - {"barycentric", render::vertex_attribute::barycentric}, - {"target", render::vertex_attribute::target} - }; + // Read material name length + std::uint8_t material_name_length = 0; + ctx.read8(reinterpret_cast(&material_name_length), 1); + + // Read material name + std::string material_name(static_cast(material_name_length), '\0'); + ctx.read8(reinterpret_cast(material_name.data()), material_name_length); + + // Read offset to index of first vertex + std::uint32_t material_vertex_offset = 0; + ctx.read32(reinterpret_cast(&material_vertex_offset), 1); + + // Read vertex count + std::uint32_t material_vertex_count = 0; + ctx.read32(reinterpret_cast(&material_vertex_count), 1); + + // Slugify material filename + std::string material_filename = material_name + ".mtl"; + std::replace(material_filename.begin(), material_filename.end(), '_', '-'); + + // Load material from file + render::material* material = resource_manager->load(material_filename); + + // Create model material group + render::model_group* material_group = model->add_group(material_name); + material_group->set_drawing_mode(gl::drawing_mode::triangles); + material_group->set_start_index(material_vertex_offset); + material_group->set_index_count(material_vertex_count); + material_group->set_material(material); + } - // Bind attributes to VAO - gl::vertex_array* vao = model->get_vertex_array(); - std::size_t attribute_offset = 0; - for (auto attribute_it = attributes.begin(); attribute_it != attributes.end(); ++attribute_it) + // Read skeleton + if (vertex_format_flags & vertex_attribute_bone) { - std::string attribute_name = attribute_it->first; + ::skeleton& skeleton = model->get_skeleton(); + pose& bind_pose = skeleton.bind_pose; + + // Read bone count + std::uint16_t bone_count = 0; + ctx.read16(reinterpret_cast(&bone_count), 1); - if (auto location_it = attribute_location_map.find(attribute_name); location_it != attribute_location_map.end()) + // Read bones + for (std::uint16_t i = 0; i < bone_count; ++i) { - gl::vertex_attribute attribute; - attribute.buffer = vbo; - attribute.offset = attribute_offset; - attribute.stride = vertex_stride; - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = std::get<0>(attribute_it->second); + // Read bone name length + std::uint8_t bone_name_length = 0; + ctx.read8(reinterpret_cast(&bone_name_length), 1); - vao->bind(location_it->second, attribute); + // Read bone name + std::string bone_name(static_cast(bone_name_length), '\0'); + ctx.read8(reinterpret_cast(bone_name.data()), bone_name_length); - attribute_offset += attribute.components * sizeof(float); - } - } - - // Load materials - if (auto materials_node = json.find("materials"); materials_node != json.end()) - { - for (const auto& material_node: materials_node.value().items()) - { - std::string group_name; - std::size_t group_offset = 0; - std::size_t group_size = 0; - render::material* group_material = nullptr; + // Read parent bone index + std::uint16_t parent_bone_index = i; + ctx.read16(reinterpret_cast(&parent_bone_index), 1); - if (auto name_node = material_node.value().find("name"); name_node != material_node.value().end()) - group_name = name_node.value().get(); - if (auto offset_node = material_node.value().find("offset"); offset_node != material_node.value().end()) - group_offset = offset_node.value().get(); - if (auto size_node = material_node.value().find("size"); size_node != material_node.value().end()) - group_size = size_node.value().get(); + // Construct bone identifier + ::bone bone = make_bone(i, parent_bone_index); - // Slugify material filename - std::string material_filename = group_name + ".mtl"; - std::replace(material_filename.begin(), material_filename.end(), '_', '-'); + // Add bone to bone map + skeleton.bone_map[bone_name] = bone; - // Load material from file - group_material = resource_manager->load(material_filename); + // Get reference to the bone's bind pose transform + auto& bone_transform = bind_pose[bone]; - render::model_group* model_group = model->add_group(group_name); - model_group->set_drawing_mode(gl::drawing_mode::triangles); - model_group->set_start_index(group_offset * 3); - model_group->set_index_count(group_size * 3); - model_group->set_material(group_material); - } - } - - // Build skeleton - if (auto skeleton_node = json.find("skeleton"); skeleton_node != json.end()) - { - if (auto bones_node = skeleton_node->find("bones"); bones_node != skeleton_node->end()) - { - ::skeleton& skeleton = model->get_skeleton(); - pose& bind_pose = skeleton.bind_pose; + // Read bone translation + ctx.read32(reinterpret_cast(bone_transform.translation.data()), 3); - std::uint8_t bone_index = 0; - for (const auto& bone_node: bones_node.value()) - { - // Find parent bone - std::uint8_t bone_parent_index = bone_index; - if (auto parent_node = bone_node.find("parent"); parent_node != bone_node.end()) - { - if (!parent_node->is_null()) - bone_parent_index = parent_node->get(); - } - - // Construct bone identifier - ::bone bone = make_bone(bone_index, bone_parent_index); - - // Get reference to the bone's bind pose transform - auto& bone_transform = bind_pose[bone]; - - // Get bone translation - if (auto translation_node = bone_node.find("translation"); translation_node != bone_node.end()) - { - if (translation_node->size() == 3) - { - bone_transform.translation.x() = (*translation_node)[0].get(); - bone_transform.translation.y() = (*translation_node)[1].get(); - bone_transform.translation.z() = (*translation_node)[2].get(); - } - } - - // Get bone rotation - if (auto rotation_node = bone_node.find("rotation"); rotation_node != bone_node.end()) - { - if (rotation_node->size() == 4) - { - bone_transform.rotation.w() = (*rotation_node)[0].get(); - bone_transform.rotation.x() = (*rotation_node)[1].get(); - bone_transform.rotation.y() = (*rotation_node)[2].get(); - bone_transform.rotation.z() = (*rotation_node)[3].get(); - } - } - - // Set bone scale - bone_transform.scale = {1, 1, 1}; - - // Get bone length - /* - if (auto length_node = bone_node.find("length"); length_node != bone_node.end()) - bone.length = length_node->get(); - else - bone.length = 0.0f; - */ - - // Get bone name - if (auto name_node = bone_node.find("name"); name_node != bone_node.end()) - { - // Add bone to bone map - skeleton.bone_map[name_node->get()] = bone; - } - - ++bone_index; - } + // Read bone rotation + ctx.read32(reinterpret_cast(&bone_transform.rotation.r), 1); + ctx.read32(reinterpret_cast(bone_transform.rotation.i.data()), 3); + + // Set bone scale + bone_transform.scale = {1, 1, 1}; - // Calculate inverse skeleton-space bind pose - ::concatenate(skeleton.bind_pose, skeleton.inverse_bind_pose); - ::inverse(skeleton.inverse_bind_pose, skeleton.inverse_bind_pose); + // Read bone length + float bone_length = 0.0f; + ctx.read32(reinterpret_cast(&bone_length), 1); } + + // Calculate inverse skeleton-space bind pose + ::concatenate(skeleton.bind_pose, skeleton.inverse_bind_pose); + ::inverse(skeleton.inverse_bind_pose, skeleton.inverse_bind_pose); } return model;