Browse Source

Replace CBOR-based model loader with a custom binary format loader

master
C. J. Howard 1 year ago
parent
commit
8165f8da3a
9 changed files with 349 additions and 246 deletions
  1. +14
    -14
      src/animation/bone.hpp
  2. +1
    -1
      src/animation/pose.cpp
  3. +2
    -0
      src/game/context.hpp
  4. +51
    -1
      src/game/controls.cpp
  5. +2
    -0
      src/game/controls.hpp
  6. +1
    -0
      src/game/state/boot.cpp
  7. +4
    -2
      src/game/state/main-menu.cpp
  8. +13
    -4
      src/game/state/nuptial-flight.cpp
  9. +261
    -224
      src/resources/model-loader.cpp

+ 14
- 14
src/animation/bone.hpp View File

@ -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<std::uint16_t>(parent_index) << 8) | index;
return (static_cast<std::uint32_t>(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<std::uint8_t>(x & bone_index_mask);
return static_cast<std::uint16_t>(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<std::uint8_t>(x >> 8);
return static_cast<std::uint16_t>(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

+ 1
- 1
src/animation/pose.cpp View File

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

+ 2
- 0
src/game/context.hpp View File

@ -201,6 +201,8 @@ struct context
input::action move_up_action;
input::action move_down_action;
input::action pause_action;
std::vector<std::shared_ptr<::event::subscription>> movement_action_subscriptions;
// Debugging
scene::text* frame_time_text;

+ 51
- 1
src/game/controls.cpp View File

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

+ 2
- 0
src/game/controls.hpp View File

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

+ 1
- 0
src/game/state/boot.cpp View File

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

+ 4
- 2
src/game/state/main-menu.cpp View File

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

+ 13
- 4
src/game/state/nuptial-flight.cpp View File

@ -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<float>::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<float>::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);

+ 261
- 224
src/resources/model-loader.cpp View File

@ -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 <sstream>
#include <stdexcept>
#include <limits>
#include <physfs.h>
#include <nlohmann/json.hpp>
#include <cstdint>
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<render::model>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{
// Read file into buffer
std::size_t size = static_cast<int>(PHYSFS_fileLength(file));
std::vector<std::uint8_t> buffer(size);
PHYSFS_readBytes(file, &buffer.front(), size);
deserialize_context ctx(file);
// Read vertex format
std::uint16_t vertex_format_flags = 0;
ctx.read16<std::endian::little>(reinterpret_cast<std::byte*>(&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<std::byte*>(&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<std::endian::little>(reinterpret_cast<std::byte*>(&vertex_count), 1);
// Load attributes
std::unordered_map<std::string, std::tuple<std::size_t, std::vector<float>>> 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<std::string>();
// Allocate attribute in attribute map
auto& attribute = attributes[attribute_name];
std::size_t& attribute_size = std::get<0>(attribute);
std::vector<float>& 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<std::size_t>();
// 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<float>();
}
}
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<float> 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<float>::infinity(), std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()},
{-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::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<float>();
}
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<float>();
if (vertex_format_flags & vertex_attribute_position)
{
ctx.read32<std::endian::little>(vertex_data_offset, 3);
vertex_data_offset += sizeof(float) * 3;
}
if (vertex_format_flags & vertex_attribute_uv)
{
ctx.read32<std::endian::little>(vertex_data_offset, 2);
vertex_data_offset += sizeof(float) * 2;
}
if (vertex_format_flags & vertex_attribute_normal)
{
ctx.read32<std::endian::little>(vertex_data_offset, 3);
vertex_data_offset += sizeof(float) * 3;
}
if (vertex_format_flags & vertex_attribute_tangent)
{
ctx.read32<std::endian::little>(vertex_data_offset, 4);
vertex_data_offset += sizeof(float) * 4;
}
if (vertex_format_flags & vertex_attribute_color)
{
ctx.read32<std::endian::little>(vertex_data_offset, 4);
vertex_data_offset += sizeof(float) * 4;
}
if (vertex_format_flags & vertex_attribute_bone)
{
ctx.read32<std::endian::little>(vertex_data_offset, bones_per_vertex);
ctx.read32<std::endian::little>(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<std::endian::little>(vertex_data_offset, 3);
vertex_data_offset += sizeof(float) * 3;
}
if (vertex_format_flags & vertex_attribute_morph_target)
{
ctx.read32<std::endian::little>(vertex_data_offset, 3);
vertex_data_offset += sizeof(float) * 3;
}
}
}
// Read geometry bounds
geom::aabb<float> bounds;
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(bounds.min_point.data()), 3);
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(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<std::endian::little>(reinterpret_cast<std::byte*>(&material_count), 1);
// Map attribute names to locations
static const std::unordered_map<std::string, unsigned int> 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<std::byte*>(&material_name_length), 1);
// Read material name
std::string material_name(static_cast<std::size_t>(material_name_length), '\0');
ctx.read8(reinterpret_cast<std::byte*>(material_name.data()), material_name_length);
// Read offset to index of first vertex
std::uint32_t material_vertex_offset = 0;
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&material_vertex_offset), 1);
// Read vertex count
std::uint32_t material_vertex_count = 0;
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&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<render::material>(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<std::endian::little>(reinterpret_cast<std::byte*>(&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<std::byte*>(&bone_name_length), 1);
vao->bind(location_it->second, attribute);
// Read bone name
std::string bone_name(static_cast<std::size_t>(bone_name_length), '\0');
ctx.read8(reinterpret_cast<std::byte*>(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<std::endian::little>(reinterpret_cast<std::byte*>(&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<std::string>();
if (auto offset_node = material_node.value().find("offset"); offset_node != material_node.value().end())
group_offset = offset_node.value().get<std::size_t>();
if (auto size_node = material_node.value().find("size"); size_node != material_node.value().end())
group_size = size_node.value().get<std::size_t>();
// 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<render::material>(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<std::endian::little>(reinterpret_cast<std::byte*>(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<std::uint8_t>();
}
// 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<float>();
bone_transform.translation.y() = (*translation_node)[1].get<float>();
bone_transform.translation.z() = (*translation_node)[2].get<float>();
}
}
// 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<float>();
bone_transform.rotation.x() = (*rotation_node)[1].get<float>();
bone_transform.rotation.y() = (*rotation_node)[2].get<float>();
bone_transform.rotation.z() = (*rotation_node)[3].get<float>();
}
}
// 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<float>();
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<std::string>()] = bone;
}
++bone_index;
}
// Read bone rotation
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(&bone_transform.rotation.r), 1);
ctx.read32<std::endian::little>(reinterpret_cast<std::byte*>(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<std::endian::little>(reinterpret_cast<std::byte*>(&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;

Loading…
Cancel
Save