From a313430cb96b68c930773a04c4f704482992cdb1 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Sat, 11 Mar 2023 07:26:00 +0800 Subject: [PATCH] Fix and improve ant morphogenesis --- src/engine/animation/bone.hpp | 11 + src/engine/animation/skeleton.hpp | 3 + src/engine/gl/rasterizer.cpp | 2 +- src/engine/gl/vertex-array.cpp | 2 +- src/engine/render/material.cpp | 3 - src/engine/render/passes/material-pass.cpp | 10 +- src/game/ant/ant-morphogenesis.cpp | 1732 +++++++++++--------- src/game/ant/ant-morphogenesis.hpp | 4 +- src/game/controls.cpp | 31 +- src/game/controls.hpp | 4 +- src/game/game.cpp | 4 +- src/game/game.hpp | 14 +- src/game/states/nest-selection-state.cpp | 346 ++-- src/game/states/nest-selection-state.hpp | 8 +- src/game/states/nuptial-flight-state.cpp | 6 +- src/game/states/nuptial-flight-state.hpp | 2 +- 16 files changed, 1115 insertions(+), 1067 deletions(-) diff --git a/src/engine/animation/bone.hpp b/src/engine/animation/bone.hpp index 22fadae..f3e696d 100644 --- a/src/engine/animation/bone.hpp +++ b/src/engine/animation/bone.hpp @@ -62,6 +62,17 @@ struct bone_index_compare return (static_cast(parent_index) << 16) | index; } +/** + * Sets the parent of a bone. + * + * @param[out] child Bone to reparent. + * @param[in] parent New parent bone. + */ +inline void reparent_bone(bone& child, bone parent) noexcept +{ + child = ((parent & bone_index_mask) << 16) | (child & bone_index_mask); +} + /** * Constructs an orphan bone identifier. * diff --git a/src/engine/animation/skeleton.hpp b/src/engine/animation/skeleton.hpp index ca9e2b3..da2e371 100644 --- a/src/engine/animation/skeleton.hpp +++ b/src/engine/animation/skeleton.hpp @@ -25,6 +25,7 @@ #include #include #include +#include /** * Skeletal animation skeleton. @@ -37,6 +38,8 @@ struct skeleton /// Inverse skeleton-space bind pose of the skeleton. pose inverse_bind_pose; + std::vector bones; + /// Maps bone names to bone identifiers. std::unordered_map bone_map; }; diff --git a/src/engine/gl/rasterizer.cpp b/src/engine/gl/rasterizer.cpp index bdd7859..7055b66 100644 --- a/src/engine/gl/rasterizer.cpp +++ b/src/engine/gl/rasterizer.cpp @@ -161,7 +161,7 @@ void rasterizer::draw_elements(const vertex_array& vao, drawing_mode mode, std:: bound_vao = &vao; } - glDrawElements(gl_mode, static_cast(count), gl_type, (const GLvoid*)offset); + glDrawElements(gl_mode, static_cast(count), gl_type, reinterpret_cast(offset)); } } // namespace gl diff --git a/src/engine/gl/vertex-array.cpp b/src/engine/gl/vertex-array.cpp index d429e3b..079d49d 100644 --- a/src/engine/gl/vertex-array.cpp +++ b/src/engine/gl/vertex-array.cpp @@ -72,7 +72,7 @@ void vertex_array::bind(attribute_location_type location, const vertex_attribute gl_type, GL_FALSE, static_cast(attribute.stride), - (const GLvoid*)attribute.offset + reinterpret_cast(attribute.offset) ); glEnableVertexAttribArray(static_cast(location)); } diff --git a/src/engine/render/material.cpp b/src/engine/render/material.cpp index 7c72418..af000d5 100644 --- a/src/engine/render/material.cpp +++ b/src/engine/render/material.cpp @@ -210,9 +210,6 @@ static bool load_scalar_property(render::material& material, hash::fnv1a32_t key // If JSON element is an array if (json.is_array()) { - // Determine size of the array - std::size_t array_size = json.size(); - // Create variable auto variable = std::make_shared>(json.size()); diff --git a/src/engine/render/passes/material-pass.cpp b/src/engine/render/passes/material-pass.cpp index 3c78b77..c63a9d5 100644 --- a/src/engine/render/passes/material-pass.cpp +++ b/src/engine/render/passes/material-pass.cpp @@ -355,11 +355,11 @@ void material_pass::evaluate_lighting(const render::context& ctx) spot_light_cutoffs.resize(spot_light_count); } - spot_light_colors[spot_light_count] = spot_light.get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; - spot_light_positions[spot_light_count] = spot_light.get_transform_tween().interpolate(ctx.alpha).translation; - spot_light_directions[spot_light_count] = spot_light.get_direction_tween().interpolate(ctx.alpha); - spot_light_attenuations[spot_light_count] = spot_light.get_attenuation_tween().interpolate(ctx.alpha); - spot_light_cutoffs[spot_light_count] = spot_light.get_cosine_cutoff_tween().interpolate(ctx.alpha); + spot_light_colors[index] = spot_light.get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; + spot_light_positions[index] = spot_light.get_transform_tween().interpolate(ctx.alpha).translation; + spot_light_directions[index] = spot_light.get_direction_tween().interpolate(ctx.alpha); + spot_light_attenuations[index] = spot_light.get_attenuation_tween().interpolate(ctx.alpha); + spot_light_cutoffs[index] = spot_light.get_cosine_cutoff_tween().interpolate(ctx.alpha); break; } diff --git a/src/game/ant/ant-morphogenesis.cpp b/src/game/ant/ant-morphogenesis.cpp index a3fb41c..ff68334 100644 --- a/src/game/ant/ant-morphogenesis.cpp +++ b/src/game/ant/ant-morphogenesis.cpp @@ -25,38 +25,152 @@ #include #include -static void reskin_vertices +namespace { + +/// Set of pointers to all possible ant skeleton bones. +struct ant_bone_set +{ + ::bone* mesosoma{nullptr}; + ::bone* procoxa_l{nullptr}; + ::bone* profemur_l{nullptr}; + ::bone* protibia_l{nullptr}; + ::bone* protarsus_l{nullptr}; + ::bone* procoxa_r{nullptr}; + ::bone* profemur_r{nullptr}; + ::bone* protibia_r{nullptr}; + ::bone* protarsus_r{nullptr}; + ::bone* mesocoxa_l{nullptr}; + ::bone* mesofemur_l{nullptr}; + ::bone* mesotibia_l{nullptr}; + ::bone* mesotarsus_l{nullptr}; + ::bone* mesocoxa_r{nullptr}; + ::bone* mesofemur_r{nullptr}; + ::bone* mesotibia_r{nullptr}; + ::bone* mesotarsus_r{nullptr}; + ::bone* metacoxa_l{nullptr}; + ::bone* metafemur_l{nullptr}; + ::bone* metatibia_l{nullptr}; + ::bone* metatarsus_l{nullptr}; + ::bone* metacoxa_r{nullptr}; + ::bone* metafemur_r{nullptr}; + ::bone* metatibia_r{nullptr}; + ::bone* metatarsus_r{nullptr}; + ::bone* head{nullptr}; + ::bone* mandible_l{nullptr}; + ::bone* mandible_r{nullptr}; + ::bone* antennomere1_l{nullptr}; + ::bone* antennomere2_l{nullptr}; + ::bone* antennomere1_r{nullptr}; + ::bone* antennomere2_r{nullptr}; + ::bone* petiole{nullptr}; + ::bone* postpetiole{nullptr}; + ::bone* gaster{nullptr}; + ::bone* sting{nullptr}; + ::bone* forewing_l{nullptr}; + ::bone* forewing_r{nullptr}; + ::bone* hindwing_l{nullptr}; + ::bone* hindwing_r{nullptr}; +}; + +/** + * Reskins model vertices. + * + * @param vertex_data Vertex buffer data. + * @param vertex_count Number of vertices to reskin. + * @param position_attribute Vertex position attribute. + * @param normal_attribute Vertex normal attribute. + * @param tangent_attribute Vertex tangent attribute. + * @param bone_index_attribute Vertex bone index attribute. + * @param reskin_map Map of old bone index to a tuple containing the new bone index and a vertex transformation. + */ +void reskin_vertices ( std::byte* vertex_data, - std::size_t index_count, + std::size_t vertex_count, const gl::vertex_attribute& position_attribute, const gl::vertex_attribute& normal_attribute, const gl::vertex_attribute& tangent_attribute, const gl::vertex_attribute& bone_index_attribute, - const std::unordered_set& old_bone_indices, - std::uint8_t new_bone_index, - const math::transform& transform -); -static geom::aabb calculate_bounds(const std::byte* vertex_data, std::size_t index_count, const gl::vertex_attribute& position_attribute); -static render::model* build_model + const std::unordered_map*>>& reskin_map +) +{ + std::byte* position_data = vertex_data + position_attribute.offset; + std::byte* normal_data = vertex_data + normal_attribute.offset; + std::byte* tangent_data = vertex_data + tangent_attribute.offset; + std::byte* bone_index_data = vertex_data + bone_index_attribute.offset; + + for (std::size_t i = 0; i < vertex_count; ++i) + { + // Get bone index of current vertex + std::uint32_t& bone_index = reinterpret_cast(*(bone_index_data + bone_index_attribute.stride * i)); + + // Ignore vertices with unmapped bone indices + auto entry = reskin_map.find(static_cast(bone_index)); + if (entry == reskin_map.end()) + { + continue; + } + + const auto& [new_bone_index, transform] = entry->second; + + // Update bone index + bone_index = static_cast(new_bone_index); + + // Get vertex attributes + float3& position = reinterpret_cast(*(position_data + position_attribute.stride * i)); + float3& normal = reinterpret_cast(*(normal_data + normal_attribute.stride * i)); + float3& tangent = reinterpret_cast(*(tangent_data + tangent_attribute.stride * i)); + + // Transform vertex attributes + position = (*transform) * position; + normal = math::normalize(transform->rotation * normal); + tangent = math::normalize(transform->rotation * tangent); + } +} + +/** + * Calculates the bounds of vertex data. + * + * @param vertex_data Pointer to vertex data. + * @param vertex_count Number of vertices. + * @param position_attribute Vertex position attribute. + * + * @return Bounds of the vertex data. + */ +[[nodiscard]] geom::aabb calculate_bounds ( - render::material* material, - const render::model* antennae, - const render::model* eyes, - const render::model* forewings, - const render::model* gaster, - const render::model* head, - const render::model* hindwings, - const render::model* legs, - const render::model* mandibles, - const render::model* mesosoma, - const render::model* lateral_ocelli, - const render::model* median_ocellus, - const render::model* sting, - const render::model* waist -); + const std::byte* vertex_data, + std::size_t vertex_count, + const gl::vertex_attribute& position_attribute +) +{ + const std::byte* position_data = vertex_data + position_attribute.offset; + + geom::aabb bounds; + bounds.min_point = {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}; + bounds.max_point = {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::infinity()}; + + for (std::size_t i = 0; i < vertex_count; ++i) + { + // Get vertex position + const float3& position = reinterpret_cast(*(position_data + position_attribute.stride * i)); + + bounds.min_point = math::min(bounds.min_point, position); + bounds.max_point = math::max(bounds.max_point, position); + } + + return bounds; +} -static std::unique_ptr build_exoskeleton_material +/** + * Generates an ant exoskeleton material. + * + * @param pigmentation Ant pigmentation phene. + * @param sculpturing Ant sculpturing phene. + * + * @return Generated ant exoskeleton material. + */ +[[nodiscard]] std::unique_ptr generate_ant_exoskeleton_material ( const ant_pigmentation_phene& pigmentation, const ant_sculpturing_phene& sculpturing @@ -74,144 +188,518 @@ static std::unique_ptr build_exoskeleton_material return exoskeleton_material; } +/** + * Calculates the number of bones in an ant model skeleton. + * + * @return Ant model skeleton bone count. + * + * Required bones: + * + * * antennomere1_l + * * antennomere1_r + * * antennomere2_l + * * antennomere2_r + * * gaster + * * head + * * mandible_l + * * mandible_r + * * mesocoxa_l + * * mesocoxa_r + * * mesofemur_l + * * mesofemur_r + * * mesosoma + * * mesotarsus_l + * * mesotarsus_r + * * mesotibia_l + * * mesotibia_r + * * metacoxa_l + * * metacoxa_r + * * metafemur_l + * * metafemur_r + * * metatarsus_l + * * metatarsus_r + * * metatibia_l + * * metatibia_r + * * procoxa_l + * * procoxa_r + * * profemur_l + * * profemur_r + * * protarsus_l + * * protarsus_r + * * protibia_l + * * protibia_r + * + * Optional bones: + * + * * forewing_l + * * forewing_r + * * hindwing_l + * * hindwing_r + * * petiole + * * postpetiole + * * sting + * + */ +[[nodiscard]] std::uint16_t count_ant_skeleton_bones(const ant_phenome& phenome) noexcept +{ + constexpr std::uint16_t minimum_bone_count = 33; + + std::uint16_t bone_count = minimum_bone_count; + + if (phenome.waist->petiole_present) + { + bone_count += 1; + } + if (phenome.waist->postpetiole_present) + { + bone_count += 1; + } + if (phenome.sting->present) + { + bone_count += 1; + } + if (phenome.wings->present) + { + bone_count += 4; + } + + return bone_count; +} + +/** + * Builds an ant model skeleton. + * + * @param[in] phenome Ant phenome. + * @param[out] skeleton Ant model skeleton. + * @param[out] bones Set of ant model skeleton bones. + */ +void build_ant_skeleton(const ant_phenome& phenome, ::skeleton& skeleton, ant_bone_set& bones) +{ + // Allocate bones + const std::uint16_t bone_count = count_ant_skeleton_bones(phenome); + skeleton.bones.resize(bone_count); + + // Assign bone indices + for (std::uint16_t i = 0; i < bone_count; ++i) + { + skeleton.bones[i] = i; + } + + // Construct ant bone set + ::bone* current_bone = skeleton.bones.data(); + bones.mesosoma = current_bone; + bones.procoxa_l = ++current_bone; + bones.profemur_l = ++current_bone; + bones.protibia_l = ++current_bone; + bones.protarsus_l = ++current_bone; + bones.procoxa_r = ++current_bone; + bones.profemur_r = ++current_bone; + bones.protibia_r = ++current_bone; + bones.protarsus_r = ++current_bone; + bones.mesocoxa_l = ++current_bone; + bones.mesofemur_l = ++current_bone; + bones.mesotibia_l = ++current_bone; + bones.mesotarsus_l = ++current_bone; + bones.mesocoxa_r = ++current_bone; + bones.mesofemur_r = ++current_bone; + bones.mesotibia_r = ++current_bone; + bones.mesotarsus_r = ++current_bone; + bones.metacoxa_l = ++current_bone; + bones.metafemur_l = ++current_bone; + bones.metatibia_l = ++current_bone; + bones.metatarsus_l = ++current_bone; + bones.metacoxa_r = ++current_bone; + bones.metafemur_r = ++current_bone; + bones.metatibia_r = ++current_bone; + bones.metatarsus_r = ++current_bone; + bones.head = ++current_bone; + bones.mandible_l = ++current_bone; + bones.mandible_r = ++current_bone; + bones.antennomere1_l = ++current_bone; + bones.antennomere2_l = ++current_bone; + bones.antennomere1_r = ++current_bone; + bones.antennomere2_r = ++current_bone; + bones.petiole = phenome.waist->petiole_present ? ++current_bone : nullptr; + bones.postpetiole = phenome.waist->postpetiole_present ? ++current_bone : nullptr; + bones.gaster = ++current_bone; + bones.sting = phenome.sting->present ? ++current_bone : nullptr; + bones.forewing_l = phenome.wings->present ? ++current_bone : nullptr; + bones.forewing_r = phenome.wings->present ? ++current_bone : nullptr; + bones.hindwing_l = phenome.wings->present ? ++current_bone : nullptr; + bones.hindwing_r = phenome.wings->present ? ++current_bone : nullptr; + + // Assign bone parents + reparent_bone(*bones.mesosoma, *bones.mesosoma); + reparent_bone(*bones.procoxa_l, *bones.mesosoma); + reparent_bone(*bones.profemur_l, *bones.procoxa_l); + reparent_bone(*bones.protibia_l, *bones.profemur_l); + reparent_bone(*bones.protarsus_l, *bones.protibia_l); + reparent_bone(*bones.procoxa_r, *bones.mesosoma); + reparent_bone(*bones.profemur_r, *bones.procoxa_r); + reparent_bone(*bones.protibia_r, *bones.profemur_r); + reparent_bone(*bones.protarsus_r, *bones.protibia_r); + reparent_bone(*bones.mesocoxa_l, *bones.mesosoma); + reparent_bone(*bones.mesofemur_l, *bones.mesocoxa_l); + reparent_bone(*bones.mesotibia_l, *bones.mesofemur_l); + reparent_bone(*bones.mesotarsus_l, *bones.mesotibia_l); + reparent_bone(*bones.mesocoxa_r, *bones.mesosoma); + reparent_bone(*bones.mesofemur_r, *bones.mesocoxa_r); + reparent_bone(*bones.mesotibia_r, *bones.mesofemur_r); + reparent_bone(*bones.mesotarsus_r, *bones.mesotibia_r); + reparent_bone(*bones.metacoxa_l, *bones.mesosoma); + reparent_bone(*bones.metafemur_l, *bones.metacoxa_l); + reparent_bone(*bones.metatibia_l, *bones.metafemur_l); + reparent_bone(*bones.metatarsus_l, *bones.metatibia_l); + reparent_bone(*bones.metacoxa_r, *bones.mesosoma); + reparent_bone(*bones.metafemur_r, *bones.metacoxa_r); + reparent_bone(*bones.metatibia_r, *bones.metafemur_r); + reparent_bone(*bones.metatarsus_r, *bones.metatibia_r); + reparent_bone(*bones.head, *bones.mesosoma); + reparent_bone(*bones.mandible_l, *bones.head); + reparent_bone(*bones.mandible_r, *bones.head); + reparent_bone(*bones.antennomere1_l, *bones.head); + reparent_bone(*bones.antennomere2_l, *bones.antennomere1_l); + reparent_bone(*bones.antennomere1_r, *bones.head); + reparent_bone(*bones.antennomere2_r, *bones.antennomere1_r); + + if (bones.petiole) + { + reparent_bone(*bones.petiole, *bones.mesosoma); + } + if (bones.postpetiole) + { + if (bones.petiole) + { + reparent_bone(*bones.postpetiole, *bones.petiole); + } + else + { + reparent_bone(*bones.postpetiole, *bones.mesosoma); + } + } + + if (bones.postpetiole) + { + reparent_bone(*bones.gaster, *bones.postpetiole); + } + else + { + if (bones.petiole) + { + reparent_bone(*bones.gaster, *bones.petiole); + } + else + { + reparent_bone(*bones.gaster, *bones.mesosoma); + } + } + + if (bones.sting) + { + reparent_bone(*bones.sting, *bones.gaster); + } + + if (bones.forewing_l) + { + reparent_bone(*bones.forewing_l, *bones.mesosoma); + reparent_bone(*bones.forewing_r, *bones.mesosoma); + reparent_bone(*bones.hindwing_l, *bones.mesosoma); + reparent_bone(*bones.hindwing_r, *bones.mesosoma); + } + + // Map bone names to bones + skeleton.bone_map["mesosoma"] = *bones.mesosoma; + skeleton.bone_map["procoxa_l"] = *bones.procoxa_l; + skeleton.bone_map["profemur_l"] = *bones.profemur_l; + skeleton.bone_map["protibia_l"] = *bones.protibia_l; + skeleton.bone_map["protarsus_l"] = *bones.protarsus_l; + skeleton.bone_map["procoxa_r"] = *bones.procoxa_r; + skeleton.bone_map["profemur_r"] = *bones.profemur_r; + skeleton.bone_map["protibia_r"] = *bones.protibia_r; + skeleton.bone_map["protarsus_r"] = *bones.protarsus_r; + skeleton.bone_map["mesocoxa_l"] = *bones.mesocoxa_l; + skeleton.bone_map["mesofemur_l"] = *bones.mesofemur_l; + skeleton.bone_map["mesotibia_l"] = *bones.mesotibia_l; + skeleton.bone_map["mesotarsus_l"] = *bones.mesotarsus_l; + skeleton.bone_map["mesocoxa_r"] = *bones.mesocoxa_r; + skeleton.bone_map["mesofemur_r"] = *bones.mesofemur_r; + skeleton.bone_map["mesotibia_r"] = *bones.mesotibia_r; + skeleton.bone_map["mesotarsus_r"] = *bones.mesotarsus_r; + skeleton.bone_map["metacoxa_l"] = *bones.metacoxa_l; + skeleton.bone_map["metafemur_l"] = *bones.metafemur_l; + skeleton.bone_map["metatibia_l"] = *bones.metatibia_l; + skeleton.bone_map["metatarsus_l"] = *bones.metatarsus_l; + skeleton.bone_map["metacoxa_r"] = *bones.metacoxa_r; + skeleton.bone_map["metafemur_r"] = *bones.metafemur_r; + skeleton.bone_map["metatibia_r"] = *bones.metatibia_r; + skeleton.bone_map["metatarsus_r"] = *bones.metatarsus_r; + skeleton.bone_map["head"] = *bones.head; + skeleton.bone_map["mandible_l"] = *bones.mandible_l; + skeleton.bone_map["mandible_r"] = *bones.mandible_r; + skeleton.bone_map["antennomere1_l"] = *bones.antennomere1_l; + skeleton.bone_map["antennomere2_l"] = *bones.antennomere2_l; + skeleton.bone_map["antennomere1_r"] = *bones.antennomere1_r; + skeleton.bone_map["antennomere2_r"] = *bones.antennomere2_r; + if (bones.petiole) + { + skeleton.bone_map["petiole"] = *bones.petiole; + } + if (bones.postpetiole) + { + skeleton.bone_map["postpetiole"] = *bones.postpetiole; + } + skeleton.bone_map["gaster"] = *bones.gaster; + if (bones.sting) + { + skeleton.bone_map["sting"] = *bones.sting; + } + if (bones.forewing_l) + { + skeleton.bone_map["forewing_l"] = *bones.forewing_l; + skeleton.bone_map["forewing_r"] = *bones.forewing_r; + skeleton.bone_map["hindwing_l"] = *bones.hindwing_l; + skeleton.bone_map["hindwing_r"] = *bones.hindwing_r; + } +} + +/** + * Builds the bind pose of an ant skeleton. + * + * @param[in] phenome Ant phenome. + * @param[in] bones Set of ant skeleton bones. + * @param[in,out] skeleton Ant skeleton. + * @param[out] bind_pose_ss Skeleton-space bind pose. + */ +void build_ant_bind_pose(const ant_phenome& phenome, const ant_bone_set& bones, ::skeleton& skeleton, ::pose& bind_pose_ss) +{ + // Get body part skeletons + const ::skeleton& mesosoma_skeleton = phenome.mesosoma->model->get_skeleton(); + const ::skeleton& legs_skeleton = phenome.legs->model->get_skeleton(); + const ::skeleton& head_skeleton = phenome.head->model->get_skeleton(); + const ::skeleton& mandibles_skeleton = phenome.mandibles->model->get_skeleton(); + const ::skeleton& antennae_skeleton = phenome.antennae->model->get_skeleton(); + const ::skeleton& waist_skeleton = phenome.waist->model->get_skeleton(); + const ::skeleton& gaster_skeleton = phenome.gaster->model->get_skeleton(); + const ::skeleton* sting_skeleton = (phenome.sting->present) ? &phenome.sting->model->get_skeleton() : nullptr; + const ::skeleton* eyes_skeleton = (phenome.eyes->present) ? &phenome.eyes->model->get_skeleton() : nullptr; + const ::skeleton* lateral_ocelli_skeleton = (phenome.ocelli->lateral_ocelli_present) ? &phenome.ocelli->lateral_ocelli_model->get_skeleton() : nullptr; + const ::skeleton* median_ocellus_skeleton = (phenome.ocelli->median_ocellus_present) ? &phenome.ocelli->median_ocellus_model->get_skeleton() : nullptr; + const ::skeleton* forewings_skeleton = (phenome.wings->present) ? &phenome.wings->forewings_model->get_skeleton() : nullptr; + const ::skeleton* hindwings_skeleton = (phenome.wings->present) ? &phenome.wings->hindwings_model->get_skeleton() : nullptr; + + // Get reference to skeleton bind pose + pose& bind_pose = skeleton.bind_pose; + + // Build skeleton bind pose + bind_pose[*bones.mesosoma] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("mesosoma")); + bind_pose[*bones.procoxa_l] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("procoxa_l")); + bind_pose[*bones.profemur_l] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("profemur_l")); + bind_pose[*bones.protibia_l] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("protibia_l")); + bind_pose[*bones.protarsus_l] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("protarsus1_l")); + bind_pose[*bones.procoxa_r] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("procoxa_r")); + bind_pose[*bones.profemur_r] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("profemur_r")); + bind_pose[*bones.protibia_r] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("protibia_r")); + bind_pose[*bones.protarsus_r] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("protarsus1_r")); + bind_pose[*bones.mesocoxa_l] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("mesocoxa_l")); + bind_pose[*bones.mesofemur_l] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("mesofemur_l")); + bind_pose[*bones.mesotibia_l] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("mesotibia_l")); + bind_pose[*bones.mesotarsus_l] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("mesotarsus1_l")); + bind_pose[*bones.mesocoxa_r] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("mesocoxa_r")); + bind_pose[*bones.mesofemur_r] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("mesofemur_r")); + bind_pose[*bones.mesotibia_r] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("mesotibia_r")); + bind_pose[*bones.mesotarsus_r] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("mesotarsus1_r")); + bind_pose[*bones.metacoxa_l] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("metacoxa_l")); + bind_pose[*bones.metafemur_l] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("metafemur_l")); + bind_pose[*bones.metatibia_l] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("metatibia_l")); + bind_pose[*bones.metatarsus_l] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("metatarsus1_l")); + bind_pose[*bones.metacoxa_r] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("metacoxa_r")); + bind_pose[*bones.metafemur_r] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("metafemur_r")); + bind_pose[*bones.metatibia_r] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("metatibia_r")); + bind_pose[*bones.metatarsus_r] = legs_skeleton.bind_pose.at(legs_skeleton.bone_map.at("metatarsus1_r")); + bind_pose[*bones.head] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("head")) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("head")); + bind_pose[*bones.mandible_l] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_l")) * mandibles_skeleton.bind_pose.at(mandibles_skeleton.bone_map.at("mandible_l")); + bind_pose[*bones.mandible_r] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_r")) * mandibles_skeleton.bind_pose.at(mandibles_skeleton.bone_map.at("mandible_r")); + bind_pose[*bones.antennomere1_l] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_l")) * antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere1_l")); + bind_pose[*bones.antennomere2_l] = antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere2_l")); + bind_pose[*bones.antennomere1_r] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_r")) * antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere1_r")); + bind_pose[*bones.antennomere2_r] = antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere2_r")); + + if (bones.petiole) + { + bind_pose[*bones.petiole] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")) * waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("petiole")); + } + + if (bones.postpetiole) + { + bind_pose[*bones.postpetiole] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("postpetiole")); + bind_pose[*bones.gaster] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("postpetiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); + } + else if (bones.petiole) + { + bind_pose[*bones.gaster] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("petiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); + } + else + { + bind_pose[*bones.gaster] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); + } + + if (bones.sting) + { + bind_pose[*bones.sting] = gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("sting")) * sting_skeleton->bind_pose.at(sting_skeleton->bone_map.at("sting")); + } + + if (bones.forewing_l) + { + bind_pose[*bones.forewing_l] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("forewing_l")) * forewings_skeleton->bind_pose.at(forewings_skeleton->bone_map.at("forewing_l")); + bind_pose[*bones.forewing_r] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("forewing_r")) * forewings_skeleton->bind_pose.at(forewings_skeleton->bone_map.at("forewing_r")); + bind_pose[*bones.hindwing_l] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("hindwing_l")) * hindwings_skeleton->bind_pose.at(hindwings_skeleton->bone_map.at("hindwing_l")); + bind_pose[*bones.hindwing_r] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("hindwing_r")) * hindwings_skeleton->bind_pose.at(hindwings_skeleton->bone_map.at("hindwing_r")); + } + + // Calculate the skeleton-space bind pose + ::concatenate(bind_pose, bind_pose_ss); + + // Calculate inverse skeleton-space bind pose + ::inverse(bind_pose_ss, skeleton.inverse_bind_pose); +} + +} // namespace + std::unique_ptr ant_morphogenesis(const ant_phenome& phenome) { - // Build exoskeleton material - std::shared_ptr exoskeleton_material = build_exoskeleton_material(*phenome.pigmentation, *phenome.sculpturing); - - // Determine presence of optional parts - const bool eyes_present = phenome.eyes->present; - const bool lateral_ocelli_present = phenome.ocelli->lateral_ocelli_present; - const bool median_ocellus_present = phenome.ocelli->median_ocellus_present; - const bool petiole_present = phenome.waist->petiole_present; - const bool postpetiole_present = phenome.waist->postpetiole_present; - const bool sting_present = phenome.sting->present; - const bool wings_present = phenome.wings->present; + // Generate exoskeleton material + std::shared_ptr exoskeleton_material = generate_ant_exoskeleton_material(*phenome.pigmentation, *phenome.sculpturing); // Get body part models + const render::model* mesosoma_model = phenome.mesosoma->model.get(); + const render::model* legs_model = phenome.legs->model.get(); + const render::model* head_model = phenome.head->model.get(); + const render::model* mandibles_model = phenome.mandibles->model.get(); const render::model* antennae_model = phenome.antennae->model.get(); - const render::model* eyes_model = phenome.eyes->model.get(); - const render::model* forewings_model = phenome.wings->forewings_model.get(); + const render::model* waist_model = phenome.waist->model.get(); const render::model* gaster_model = phenome.gaster->model.get(); - const render::model* head_model = phenome.head->model.get(); - const render::model* hindwings_model = phenome.wings->hindwings_model.get(); + const render::model* sting_model = phenome.sting->model.get(); + const render::model* eyes_model = phenome.eyes->model.get(); const render::model* lateral_ocelli_model = phenome.ocelli->lateral_ocelli_model.get(); - const render::model* legs_model = phenome.legs->model.get(); - const render::model* mandibles_model = phenome.mandibles->model.get(); const render::model* median_ocellus_model = phenome.ocelli->median_ocellus_model.get(); - const render::model* mesosoma_model = phenome.mesosoma->model.get(); - const render::model* sting_model = phenome.sting->model.get(); - const render::model* waist_model = phenome.waist->model.get(); + const render::model* forewings_model = phenome.wings->forewings_model.get(); + const render::model* hindwings_model = phenome.wings->hindwings_model.get(); // Check for presence of required part models - if (!antennae_model) + if (!mesosoma_model) { - throw std::runtime_error("Ant phenome missing antennae model"); + throw std::runtime_error("Ant phenome missing mesosoma model"); } - if (eyes_present && !eyes_model) + if (!legs_model) { - throw std::runtime_error("Ant phenome missing eyes model"); + throw std::runtime_error("Ant phenome missing legs model"); } - if (wings_present && !forewings_model) + if (!head_model) { - throw std::runtime_error("Ant phenome missing forewings model"); + throw std::runtime_error("Ant phenome missing head model"); } - if (!gaster_model) + if (!mandibles_model) { - throw std::runtime_error("Ant phenome missing gaster model"); + throw std::runtime_error("Ant phenome missing mandibles model"); } - if (wings_present && !hindwings_model) + if (!antennae_model) { - throw std::runtime_error("Ant phenome missing hindwings model"); + throw std::runtime_error("Ant phenome missing antennae model"); } - if (lateral_ocelli_present && !lateral_ocelli_model) + if (!waist_model) { - throw std::runtime_error("Ant phenome missing lateral ocelli model"); + throw std::runtime_error("Ant phenome missing waist model"); } - if (!legs_model) + if (!gaster_model) { - throw std::runtime_error("Ant phenome missing legs model"); + throw std::runtime_error("Ant phenome missing gaster model"); } - if (!mandibles_model) + if (phenome.sting->present && !sting_model) { - throw std::runtime_error("Ant phenome missing mandibles model"); + throw std::runtime_error("Ant phenome missing sting model"); } - if (median_ocellus_present && !median_ocellus_model) + if (phenome.eyes->present && !eyes_model) { - throw std::runtime_error("Ant phenome missing median ocellus model"); + throw std::runtime_error("Ant phenome missing eyes model"); } - if (!mesosoma_model) + if (phenome.ocelli->lateral_ocelli_present && !lateral_ocelli_model) { - throw std::runtime_error("Ant phenome missing mesosoma model"); + throw std::runtime_error("Ant phenome missing lateral ocelli model"); } - if (sting_present && !sting_model) + if (phenome.ocelli->median_ocellus_present && !median_ocellus_model) { - throw std::runtime_error("Ant phenome missing sting model"); + throw std::runtime_error("Ant phenome missing median ocellus model"); } - if (!waist_model) + if (phenome.wings->present) { - throw std::runtime_error("Ant phenome missing waist model"); + if (!forewings_model) + { + throw std::runtime_error("Ant phenome missing forewings model"); + } + if (!hindwings_model) + { + throw std::runtime_error("Ant phenome missing hindwings model"); + } } // Get body part vertex buffers - const gl::vertex_buffer* antennae_vbo = antennae_model->get_vertex_buffer().get(); - const gl::vertex_buffer* eyes_vbo = (eyes_present) ? eyes_model->get_vertex_buffer().get() : nullptr; - const gl::vertex_buffer* forewings_vbo = (wings_present) ? forewings_model->get_vertex_buffer().get() : nullptr; - const gl::vertex_buffer* gaster_vbo = gaster_model->get_vertex_buffer().get(); - const gl::vertex_buffer* head_vbo = head_model->get_vertex_buffer().get(); - const gl::vertex_buffer* hindwings_vbo = (wings_present) ? hindwings_model->get_vertex_buffer().get() : nullptr; - const gl::vertex_buffer* lateral_ocelli_vbo = (lateral_ocelli_present) ? lateral_ocelli_model->get_vertex_buffer().get() : nullptr; + const gl::vertex_buffer* mesosoma_vbo = mesosoma_model->get_vertex_buffer().get(); const gl::vertex_buffer* legs_vbo = legs_model->get_vertex_buffer().get(); + const gl::vertex_buffer* head_vbo = head_model->get_vertex_buffer().get(); const gl::vertex_buffer* mandibles_vbo = mandibles_model->get_vertex_buffer().get(); - const gl::vertex_buffer* median_ocellus_vbo = (median_ocellus_present) ? median_ocellus_model->get_vertex_buffer().get() : nullptr; - const gl::vertex_buffer* mesosoma_vbo = mesosoma_model->get_vertex_buffer().get(); - const gl::vertex_buffer* sting_vbo = (sting_present) ? sting_model->get_vertex_buffer().get() : nullptr; + const gl::vertex_buffer* antennae_vbo = antennae_model->get_vertex_buffer().get(); const gl::vertex_buffer* waist_vbo = waist_model->get_vertex_buffer().get(); + const gl::vertex_buffer* gaster_vbo = gaster_model->get_vertex_buffer().get(); + const gl::vertex_buffer* sting_vbo = (phenome.sting->present) ? sting_model->get_vertex_buffer().get() : nullptr; + const gl::vertex_buffer* eyes_vbo = (phenome.eyes->present) ? eyes_model->get_vertex_buffer().get() : nullptr; + const gl::vertex_buffer* lateral_ocelli_vbo = (phenome.ocelli->lateral_ocelli_present) ? lateral_ocelli_model->get_vertex_buffer().get() : nullptr; + const gl::vertex_buffer* median_ocellus_vbo = (phenome.ocelli->median_ocellus_present) ? median_ocellus_model->get_vertex_buffer().get() : nullptr; + const gl::vertex_buffer* forewings_vbo = (phenome.wings->present) ? forewings_model->get_vertex_buffer().get() : nullptr; + const gl::vertex_buffer* hindwings_vbo = (phenome.wings->present) ? hindwings_model->get_vertex_buffer().get() : nullptr; // Determine combined size of vertex buffers and save offsets std::size_t vertex_buffer_size = 0; - std::size_t mesosoma_vbo_offset = vertex_buffer_size; + const std::size_t mesosoma_vbo_offset = vertex_buffer_size; vertex_buffer_size += mesosoma_vbo->size(); - std::size_t legs_vbo_offset = vertex_buffer_size; + const std::size_t legs_vbo_offset = vertex_buffer_size; vertex_buffer_size += legs_vbo->size(); - std::size_t head_vbo_offset = vertex_buffer_size; + const std::size_t head_vbo_offset = vertex_buffer_size; vertex_buffer_size += head_vbo->size(); - std::size_t mandibles_vbo_offset = vertex_buffer_size; + const std::size_t mandibles_vbo_offset = vertex_buffer_size; vertex_buffer_size += mandibles_vbo->size(); - std::size_t antennae_vbo_offset = vertex_buffer_size; + const std::size_t antennae_vbo_offset = vertex_buffer_size; vertex_buffer_size += antennae_vbo->size(); - std::size_t waist_vbo_offset = vertex_buffer_size; + const std::size_t waist_vbo_offset = vertex_buffer_size; vertex_buffer_size += waist_vbo->size(); - std::size_t gaster_vbo_offset = vertex_buffer_size; + const std::size_t gaster_vbo_offset = vertex_buffer_size; vertex_buffer_size += gaster_vbo->size(); - std::size_t sting_vbo_offset = vertex_buffer_size; - if (sting_present) + const std::size_t sting_vbo_offset = vertex_buffer_size; + if (phenome.sting->present) { vertex_buffer_size += sting_vbo->size(); } - std::size_t eyes_vbo_offset = vertex_buffer_size; - if (eyes_present) + const std::size_t eyes_vbo_offset = vertex_buffer_size; + if (phenome.eyes->present) { vertex_buffer_size += eyes_vbo->size(); } - std::size_t lateral_ocelli_vbo_offset = vertex_buffer_size; - if (lateral_ocelli_present) + const std::size_t lateral_ocelli_vbo_offset = vertex_buffer_size; + if (phenome.ocelli->lateral_ocelli_present) { vertex_buffer_size += lateral_ocelli_vbo->size(); } - std::size_t median_ocellus_vbo_offset = vertex_buffer_size; - if (median_ocellus_present) + const std::size_t median_ocellus_vbo_offset = vertex_buffer_size; + if (phenome.ocelli->median_ocellus_present) { vertex_buffer_size += median_ocellus_vbo->size(); } - std::size_t forewings_vbo_offset = vertex_buffer_size; - if (wings_present) + std::size_t forewings_vbo_offset; + std::size_t hindwings_vbo_offset; + if (phenome.wings->present) { + forewings_vbo_offset = vertex_buffer_size; vertex_buffer_size += forewings_vbo->size(); - } - std::size_t hindwings_vbo_offset = vertex_buffer_size; - if (wings_present) - { + hindwings_vbo_offset = vertex_buffer_size; vertex_buffer_size += hindwings_vbo->size(); } @@ -226,23 +714,23 @@ std::unique_ptr ant_morphogenesis(const ant_phenome& phenome) antennae_vbo->read({vertex_buffer_data.data() + antennae_vbo_offset, antennae_vbo->size()}); waist_vbo->read({vertex_buffer_data.data() + waist_vbo_offset, waist_vbo->size()}); gaster_vbo->read({vertex_buffer_data.data() + gaster_vbo_offset, gaster_vbo->size()}); - if (sting_present) + if (phenome.sting->present) { sting_vbo->read({vertex_buffer_data.data() + sting_vbo_offset, sting_vbo->size()}); } - if (eyes_present) + if (phenome.eyes->present) { eyes_vbo->read({vertex_buffer_data.data() + eyes_vbo_offset, eyes_vbo->size()}); } - if (lateral_ocelli_present) + if (phenome.ocelli->lateral_ocelli_present) { lateral_ocelli_vbo->read({vertex_buffer_data.data() + lateral_ocelli_vbo_offset, lateral_ocelli_vbo->size()}); } - if (median_ocellus_present) + if (phenome.ocelli->median_ocellus_present) { median_ocellus_vbo->read({vertex_buffer_data.data() + median_ocellus_vbo_offset, median_ocellus_vbo->size()}); } - if (wings_present) + if (phenome.wings->present) { forewings_vbo->read({vertex_buffer_data.data() + forewings_vbo_offset, forewings_vbo->size()}); hindwings_vbo->read({vertex_buffer_data.data() + hindwings_vbo_offset, hindwings_vbo->size()}); @@ -252,11 +740,11 @@ std::unique_ptr ant_morphogenesis(const ant_phenome& phenome) std::unique_ptr model = std::make_unique(); // Setup model VAO - gl::vertex_array* model_vao = model->get_vertex_array().get(); + gl::vertex_array& model_vao = *model->get_vertex_array(); for (auto [location, attribute]: mesosoma_model->get_vertex_array()->attributes()) { attribute.buffer = model->get_vertex_buffer().get(); - model_vao->bind(location, attribute); + model_vao.bind(location, attribute); } // Get vertex attributes @@ -264,688 +752,419 @@ std::unique_ptr ant_morphogenesis(const ant_phenome& phenome) const gl::vertex_attribute* normal_attribute = nullptr; const gl::vertex_attribute* tangent_attribute = nullptr; const gl::vertex_attribute* bone_index_attribute = nullptr; - const auto& vertex_attribute_map = model_vao->attributes(); + const auto& vertex_attribute_map = model_vao.attributes(); if (auto it = vertex_attribute_map.find(render::vertex_attribute::position); it != vertex_attribute_map.end()) + { position_attribute = &it->second; + } if (auto it = vertex_attribute_map.find(render::vertex_attribute::normal); it != vertex_attribute_map.end()) + { normal_attribute = &it->second; + } if (auto it = vertex_attribute_map.find(render::vertex_attribute::tangent); it != vertex_attribute_map.end()) + { tangent_attribute = &it->second; + } if (auto it = vertex_attribute_map.find(render::vertex_attribute::bone_index); it != vertex_attribute_map.end()) + { bone_index_attribute = &it->second; + } - // Get body part skeletons - const ::skeleton& mesosoma_skeleton = mesosoma_model->get_skeleton(); - const ::skeleton& legs_skeleton = legs_model->get_skeleton(); - const ::skeleton& head_skeleton = head_model->get_skeleton(); - const ::skeleton& mandibles_skeleton = mandibles_model->get_skeleton(); - const ::skeleton& antennae_skeleton = antennae_model->get_skeleton(); - const ::skeleton& waist_skeleton = waist_model->get_skeleton(); - const ::skeleton& gaster_skeleton = gaster_model->get_skeleton(); - const ::skeleton* sting_skeleton = (sting_present) ? &sting_model->get_skeleton() : nullptr; - const ::skeleton* eyes_skeleton = (eyes_present) ? &eyes_model->get_skeleton() : nullptr; - const ::skeleton* lateral_ocelli_skeleton = (lateral_ocelli_present) ? &lateral_ocelli_model->get_skeleton() : nullptr; - const ::skeleton* median_ocellus_skeleton = (median_ocellus_present) ? &median_ocellus_model->get_skeleton() : nullptr; - - // Allocate skeleton bones + // Build ant skeleton ::skeleton& skeleton = model->get_skeleton(); - std::size_t bone_count = 33; - if (petiole_present) - bone_count += 1; - if (postpetiole_present) - bone_count += 1; - if (sting_present) - bone_count += 1; - if (wings_present) - bone_count += 4; - - // Assign bone indices - std::uint8_t bone_index = 0; - std::uint8_t mesosoma_bone_index = bone_index++; - std::uint8_t procoxa_l_bone_index = bone_index++; - std::uint8_t procoxa_r_bone_index = bone_index++; - std::uint8_t profemur_l_bone_index = bone_index++; - std::uint8_t profemur_r_bone_index = bone_index++; - std::uint8_t protibia_l_bone_index = bone_index++; - std::uint8_t protibia_r_bone_index = bone_index++; - std::uint8_t protarsus_l_bone_index = bone_index++; - std::uint8_t protarsus_r_bone_index = bone_index++; - std::uint8_t mesocoxa_l_bone_index = bone_index++; - std::uint8_t mesocoxa_r_bone_index = bone_index++; - std::uint8_t mesofemur_l_bone_index = bone_index++; - std::uint8_t mesofemur_r_bone_index = bone_index++; - std::uint8_t mesotibia_l_bone_index = bone_index++; - std::uint8_t mesotibia_r_bone_index = bone_index++; - std::uint8_t mesotarsus_l_bone_index = bone_index++; - std::uint8_t mesotarsus_r_bone_index = bone_index++; - std::uint8_t metacoxa_l_bone_index = bone_index++; - std::uint8_t metacoxa_r_bone_index = bone_index++; - std::uint8_t metafemur_l_bone_index = bone_index++; - std::uint8_t metafemur_r_bone_index = bone_index++; - std::uint8_t metatibia_l_bone_index = bone_index++; - std::uint8_t metatibia_r_bone_index = bone_index++; - std::uint8_t metatarsus_l_bone_index = bone_index++; - std::uint8_t metatarsus_r_bone_index = bone_index++; - std::uint8_t head_bone_index = bone_index++; - std::uint8_t mandible_l_bone_index = bone_index++; - std::uint8_t mandible_r_bone_index = bone_index++; - std::uint8_t antennomere1_l_bone_index = bone_index++; - std::uint8_t antennomere1_r_bone_index = bone_index++; - std::uint8_t antennomere2_l_bone_index = bone_index++; - std::uint8_t antennomere2_r_bone_index = bone_index++; - std::uint8_t petiole_bone_index =(petiole_present) ? bone_index++ : static_cast(bone_count); - std::uint8_t postpetiole_bone_index = (postpetiole_present) ? bone_index++ : static_cast(bone_count); - std::uint8_t gaster_bone_index = bone_index++; - std::uint8_t sting_bone_index = (sting_present) ? bone_index++ : static_cast(bone_count); - - // Construct bone identifiers - ::bone mesosoma_bone = make_bone(mesosoma_bone_index); - ::bone procoxa_l_bone = make_bone(procoxa_l_bone_index, mesosoma_bone_index); - ::bone procoxa_r_bone = make_bone(procoxa_r_bone_index, mesosoma_bone_index); - ::bone profemur_l_bone = make_bone(profemur_l_bone_index, procoxa_l_bone_index); - ::bone profemur_r_bone = make_bone(profemur_r_bone_index, procoxa_r_bone_index); - ::bone protibia_l_bone = make_bone(protibia_l_bone_index, profemur_l_bone_index); - ::bone protibia_r_bone = make_bone(protibia_r_bone_index, profemur_r_bone_index); - ::bone protarsus_l_bone = make_bone(protarsus_l_bone_index, protibia_l_bone_index); - ::bone protarsus_r_bone = make_bone(protarsus_r_bone_index, protibia_r_bone_index); - ::bone mesocoxa_l_bone = make_bone(mesocoxa_l_bone_index, mesosoma_bone_index); - ::bone mesocoxa_r_bone = make_bone(mesocoxa_r_bone_index, mesosoma_bone_index); - ::bone mesofemur_l_bone = make_bone(mesofemur_l_bone_index, mesocoxa_l_bone_index); - ::bone mesofemur_r_bone = make_bone(mesofemur_r_bone_index, mesocoxa_r_bone_index); - ::bone mesotibia_l_bone = make_bone(mesotibia_l_bone_index, mesofemur_l_bone_index); - ::bone mesotibia_r_bone = make_bone(mesotibia_r_bone_index, mesofemur_r_bone_index); - ::bone mesotarsus_l_bone = make_bone(mesotarsus_l_bone_index, mesotibia_l_bone_index); - ::bone mesotarsus_r_bone = make_bone(mesotarsus_r_bone_index, mesotibia_r_bone_index); - ::bone metacoxa_l_bone = make_bone(metacoxa_l_bone_index, mesosoma_bone_index); - ::bone metacoxa_r_bone = make_bone(metacoxa_r_bone_index, mesosoma_bone_index); - ::bone metafemur_l_bone = make_bone(metafemur_l_bone_index, metacoxa_l_bone_index); - ::bone metafemur_r_bone = make_bone(metafemur_r_bone_index, metacoxa_r_bone_index); - ::bone metatibia_l_bone = make_bone(metatibia_l_bone_index, metafemur_l_bone_index); - ::bone metatibia_r_bone = make_bone(metatibia_r_bone_index, metafemur_r_bone_index); - ::bone metatarsus_l_bone = make_bone(metatarsus_l_bone_index, metatibia_l_bone_index); - ::bone metatarsus_r_bone = make_bone(metatarsus_r_bone_index, metatibia_r_bone_index); - ::bone head_bone = make_bone(head_bone_index, mesosoma_bone_index); - ::bone mandible_l_bone = make_bone(mandible_l_bone_index, head_bone_index); - ::bone mandible_r_bone = make_bone(mandible_r_bone_index, head_bone_index); - ::bone antennomere1_l_bone = make_bone(antennomere1_l_bone_index, head_bone_index); - ::bone antennomere1_r_bone = make_bone(antennomere1_r_bone_index, head_bone_index); - ::bone antennomere2_l_bone = make_bone(antennomere2_l_bone_index, antennomere1_l_bone_index); - ::bone antennomere2_r_bone = make_bone(antennomere2_r_bone_index, antennomere1_r_bone_index); - ::bone petiole_bone = make_bone(petiole_bone_index, mesosoma_bone_index); - ::bone postpetiole_bone = make_bone(postpetiole_bone_index, petiole_bone_index); - ::bone gaster_bone = make_bone(gaster_bone_index, (postpetiole_present) ? postpetiole_bone_index : petiole_bone_index); - ::bone sting_bone = make_bone(sting_bone_index, gaster_bone_index); + ant_bone_set bones; + build_ant_skeleton(phenome, skeleton, bones); - // Map bone names to bones - skeleton.bone_map["mesosoma"] = mesosoma_bone; - skeleton.bone_map["procoxa_l"] = procoxa_l_bone; - skeleton.bone_map["procoxa_r"] = procoxa_r_bone; - skeleton.bone_map["profemur_l"] = profemur_l_bone; - skeleton.bone_map["profemur_r"] = profemur_r_bone; - skeleton.bone_map["protibia_l"] = protibia_l_bone; - skeleton.bone_map["protibia_r"] = protibia_r_bone; - skeleton.bone_map["protarsus_l"] = protarsus_l_bone; - skeleton.bone_map["protarsus_r"] = protarsus_r_bone; - skeleton.bone_map["mesocoxa_l"] = mesocoxa_l_bone; - skeleton.bone_map["mesocoxa_r"] = mesocoxa_r_bone; - skeleton.bone_map["mesofemur_l"] = mesofemur_l_bone; - skeleton.bone_map["mesofemur_r"] = mesofemur_r_bone; - skeleton.bone_map["mesotibia_l"] = mesotibia_l_bone; - skeleton.bone_map["mesotibia_r"] = mesotibia_r_bone; - skeleton.bone_map["mesotarsus_l"] = mesotarsus_l_bone; - skeleton.bone_map["mesotarsus_r"] = mesotarsus_r_bone; - skeleton.bone_map["metacoxa_l"] = metacoxa_l_bone; - skeleton.bone_map["metacoxa_r"] = metacoxa_r_bone; - skeleton.bone_map["metafemur_l"] = metafemur_l_bone; - skeleton.bone_map["metafemur_r"] = metafemur_r_bone; - skeleton.bone_map["metatibia_l"] = metatibia_l_bone; - skeleton.bone_map["metatibia_r"] = metatibia_r_bone; - skeleton.bone_map["metatarsus_l"] = metatarsus_l_bone; - skeleton.bone_map["metatarsus_r"] = metatarsus_r_bone; - skeleton.bone_map["head"] = head_bone; - skeleton.bone_map["mandible_l"] = mandible_l_bone; - skeleton.bone_map["mandible_r"] = mandible_r_bone; - skeleton.bone_map["antennomere1_l"] = antennomere1_l_bone; - skeleton.bone_map["antennomere1_r"] = antennomere1_r_bone; - skeleton.bone_map["antennomere2_l"] = antennomere2_l_bone; - skeleton.bone_map["antennomere2_r"] = antennomere2_r_bone; - if (petiole_present) - skeleton.bone_map["petiole"] = petiole_bone; - if (postpetiole_present) - skeleton.bone_map["postpetiole"] = postpetiole_bone; - skeleton.bone_map["gaster"] = gaster_bone; - if (sting_present) - skeleton.bone_map["sting"] = sting_bone; - - // Get reference to skeleton bind pose - pose& bind_pose = skeleton.bind_pose; + // Build ant bind pose + pose bind_pose_ss; + build_ant_bind_pose(phenome, bones, skeleton, bind_pose_ss); + + // Get number of vertices for each body part + const std::uint32_t mesosoma_vertex_count = (mesosoma_model->get_groups()).front().index_count; + const std::uint32_t legs_vertex_count = (legs_model->get_groups()).front().index_count; + const std::uint32_t head_vertex_count = (head_model->get_groups()).front().index_count; + const std::uint32_t mandibles_vertex_count = (mandibles_model->get_groups()).front().index_count; + const std::uint32_t antennae_vertex_count = (antennae_model->get_groups()).front().index_count; + const std::uint32_t waist_vertex_count = (waist_model->get_groups()).front().index_count; + const std::uint32_t gaster_vertex_count = (gaster_model->get_groups()).front().index_count; + const std::uint32_t sting_vertex_count = (phenome.sting->present) ? (sting_model->get_groups()).front().index_count : 0; + const std::uint32_t exoskeleton_vertex_count = + mesosoma_vertex_count + + legs_vertex_count + + head_vertex_count + + mandibles_vertex_count + + antennae_vertex_count + + waist_vertex_count + + gaster_vertex_count + + sting_vertex_count; + const std::uint32_t eyes_vertex_count = (phenome.eyes->present) ? (eyes_model->get_groups()).front().index_count : 0; + const std::uint32_t lateral_ocelli_vertex_count = (phenome.ocelli->lateral_ocelli_present) ? (lateral_ocelli_model->get_groups()).front().index_count : 0; + const std::uint32_t median_ocellus_vertex_count = (phenome.ocelli->median_ocellus_present) ? (median_ocellus_model->get_groups()).front().index_count : 0; + const std::uint32_t forewings_vertex_count = (phenome.wings->present) ? (forewings_model->get_groups()).front().index_count : 0; + const std::uint32_t hindwings_vertex_count = (phenome.wings->present) ? (hindwings_model->get_groups()).front().index_count : 0; - // Skeleton mesosoma pose - if (auto it = mesosoma_skeleton.bone_map.find("mesosoma"); it != mesosoma_skeleton.bone_map.end()) - bind_pose[mesosoma_bone] = mesosoma_skeleton.bind_pose.at(it->second); - - // Skeleton forelegs pose - if (auto it = legs_skeleton.bone_map.find("procoxa_l"); it != legs_skeleton.bone_map.end()) - bind_pose[procoxa_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("procoxa_r"); it != legs_skeleton.bone_map.end()) - bind_pose[procoxa_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("profemur_l"); it != legs_skeleton.bone_map.end()) - bind_pose[profemur_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("profemur_r"); it != legs_skeleton.bone_map.end()) - bind_pose[profemur_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("protibia_l"); it != legs_skeleton.bone_map.end()) - bind_pose[protibia_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("protibia_r"); it != legs_skeleton.bone_map.end()) - bind_pose[protibia_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus1_l"); it != legs_skeleton.bone_map.end()) - bind_pose[protarsus_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus1_r"); it != legs_skeleton.bone_map.end()) - bind_pose[protarsus_r_bone] = legs_skeleton.bind_pose.at(it->second); - - // Skeleton midlegs pose - if (auto it = legs_skeleton.bone_map.find("mesocoxa_l"); it != legs_skeleton.bone_map.end()) - bind_pose[mesocoxa_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesocoxa_r"); it != legs_skeleton.bone_map.end()) - bind_pose[mesocoxa_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesofemur_l"); it != legs_skeleton.bone_map.end()) - bind_pose[mesofemur_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesofemur_r"); it != legs_skeleton.bone_map.end()) - bind_pose[mesofemur_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotibia_l"); it != legs_skeleton.bone_map.end()) - bind_pose[mesotibia_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotibia_r"); it != legs_skeleton.bone_map.end()) - bind_pose[mesotibia_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus1_l"); it != legs_skeleton.bone_map.end()) - bind_pose[mesotarsus_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus1_r"); it != legs_skeleton.bone_map.end()) - bind_pose[mesotarsus_r_bone] = legs_skeleton.bind_pose.at(it->second); - - // Skeleton hindlegs pose - if (auto it = legs_skeleton.bone_map.find("metacoxa_l"); it != legs_skeleton.bone_map.end()) - bind_pose[metacoxa_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metacoxa_r"); it != legs_skeleton.bone_map.end()) - bind_pose[metacoxa_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metafemur_l"); it != legs_skeleton.bone_map.end()) - bind_pose[metafemur_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metafemur_r"); it != legs_skeleton.bone_map.end()) - bind_pose[metafemur_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metatibia_l"); it != legs_skeleton.bone_map.end()) - bind_pose[metatibia_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metatibia_r"); it != legs_skeleton.bone_map.end()) - bind_pose[metatibia_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus1_l"); it != legs_skeleton.bone_map.end()) - bind_pose[metatarsus_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus1_r"); it != legs_skeleton.bone_map.end()) - bind_pose[metatarsus_r_bone] = legs_skeleton.bind_pose.at(it->second); - - // Skeleton head pose - bind_pose[head_bone] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("head")) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("head")); - - // Skeleton mandibles pose - bind_pose[mandible_l_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_l")) * mandibles_skeleton.bind_pose.at(mandibles_skeleton.bone_map.at("mandible_l")); - bind_pose[mandible_r_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_r")) * mandibles_skeleton.bind_pose.at(mandibles_skeleton.bone_map.at("mandible_r")); - - // Skeleton antennae pose - bind_pose[antennomere1_l_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_l")) * antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere1_l")); - bind_pose[antennomere1_r_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_r")) * antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere1_r")); - bind_pose[antennomere2_l_bone] = antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere2_l")); - bind_pose[antennomere2_r_bone] = antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere2_r")); - - // Skeleton waist pose - if (petiole_present) - { - bind_pose[petiole_bone] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")) * waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("petiole")); - } - if (postpetiole_present) - { - bind_pose[postpetiole_bone] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("postpetiole")); - } - - // Skeleton gaster pose - if (postpetiole_present) - { - bind_pose[gaster_bone] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("postpetiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); - } - else if (petiole_present) - { - bind_pose[gaster_bone] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("petiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); + // Get body part skeletons + const ::skeleton& mesosoma_skeleton = phenome.mesosoma->model->get_skeleton(); + const ::skeleton& legs_skeleton = phenome.legs->model->get_skeleton(); + const ::skeleton& head_skeleton = phenome.head->model->get_skeleton(); + const ::skeleton& mandibles_skeleton = phenome.mandibles->model->get_skeleton(); + const ::skeleton& antennae_skeleton = phenome.antennae->model->get_skeleton(); + const ::skeleton& waist_skeleton = phenome.waist->model->get_skeleton(); + const ::skeleton& gaster_skeleton = phenome.gaster->model->get_skeleton(); + const ::skeleton* sting_skeleton = (phenome.sting->present) ? &phenome.sting->model->get_skeleton() : nullptr; + const ::skeleton* eyes_skeleton = (phenome.eyes->present) ? &phenome.eyes->model->get_skeleton() : nullptr; + const ::skeleton* lateral_ocelli_skeleton = (phenome.ocelli->lateral_ocelli_present) ? &phenome.ocelli->lateral_ocelli_model->get_skeleton() : nullptr; + const ::skeleton* median_ocellus_skeleton = (phenome.ocelli->median_ocellus_present) ? &phenome.ocelli->median_ocellus_model->get_skeleton() : nullptr; + const ::skeleton* forewings_skeleton = (phenome.wings->present) ? &phenome.wings->forewings_model->get_skeleton() : nullptr; + const ::skeleton* hindwings_skeleton = (phenome.wings->present) ? &phenome.wings->hindwings_model->get_skeleton() : nullptr; + + // Calculate transformations from part space to body space + const math::transform legs_to_body = math::transform::identity; + const math::transform head_to_body = bind_pose_ss.at(*bones.mesosoma) * mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("head")); + const math::transform mandible_l_to_body = bind_pose_ss.at(*bones.head) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_l")); + const math::transform mandible_r_to_body = bind_pose_ss.at(*bones.head) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_r")); + const math::transform antenna_l_to_body = bind_pose_ss.at(*bones.head) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_l")); + const math::transform antenna_r_to_body = bind_pose_ss.at(*bones.head) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_r")); + const math::transform waist_to_body = bind_pose_ss.at(*bones.mesosoma) * mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")); + + math::transform gaster_to_body; + if (phenome.waist->postpetiole_present) + { + gaster_to_body = bind_pose_ss.at(*bones.postpetiole) * waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("gaster")); + } + else if (phenome.waist->petiole_present) + { + gaster_to_body = bind_pose_ss.at(*bones.petiole) * waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("gaster")); } else { - bind_pose[gaster_bone] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); + gaster_to_body = bind_pose_ss.at(*bones.mesosoma) * waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("gaster")); } - // Skeleton sting pose - if (sting_present) + math::transform sting_to_body; + if (phenome.sting->present) { - bind_pose[sting_bone] = gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("sting")) * sting_skeleton->bind_pose.at(sting_skeleton->bone_map.at("sting")); + sting_to_body = gaster_to_body * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("sting")); } - // Calculate the skeleton-space bind pose - pose bind_pose_ss; - ::concatenate(bind_pose, bind_pose_ss); + math::transform eye_l_to_body; + math::transform eye_r_to_body; + if (phenome.eyes->present) + { + eye_l_to_body = bind_pose_ss.at(*bones.head) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("eye_l")); + eye_r_to_body = bind_pose_ss.at(*bones.head) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("eye_r")); + } - // Calculate inverse skeleton-space bind pose - ::inverse(bind_pose_ss, skeleton.inverse_bind_pose); + math::transform ocellus_l_to_body; + math::transform ocellus_r_to_body; + math::transform ocellus_m_to_body; + if (phenome.ocelli->lateral_ocelli_present) + { + ocellus_l_to_body = bind_pose_ss.at(*bones.head) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_l")); + ocellus_r_to_body = bind_pose_ss.at(*bones.head) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_r")); + } + if (phenome.ocelli->median_ocellus_present) + { + ocellus_m_to_body = bind_pose_ss.at(*bones.head) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_m")); + } - // Get number of vertex indices for each body part - std::uint32_t mesosoma_index_count = (mesosoma_model->get_groups()).front().index_count; - std::uint32_t legs_index_count = (legs_model->get_groups()).front().index_count; - std::uint32_t head_index_count = (head_model->get_groups()).front().index_count; - std::uint32_t mandibles_index_count = (mandibles_model->get_groups()).front().index_count; - std::uint32_t antennae_index_count = (antennae_model->get_groups()).front().index_count; - std::uint32_t waist_index_count = (waist_model->get_groups()).front().index_count; - std::uint32_t gaster_index_count = (gaster_model->get_groups()).front().index_count; - std::uint32_t sting_index_count = (sting_present) ? (sting_model->get_groups()).front().index_count : 0; - std::uint32_t eyes_index_count = (eyes_present) ? (eyes_model->get_groups()).front().index_count : 0; - std::uint32_t lateral_ocelli_index_count = (lateral_ocelli_present) ? (lateral_ocelli_model->get_groups()).front().index_count : 0; - std::uint32_t median_ocellus_index_count = (median_ocellus_present) ? (median_ocellus_model->get_groups()).front().index_count : 0; - std::uint32_t forewings_index_count = (wings_present) ? (forewings_model->get_groups()).front().index_count : 0; - std::uint32_t hindwings_index_count = (wings_present) ? (hindwings_model->get_groups()).front().index_count : 0; - std::uint32_t exoskeleton_index_count = - mesosoma_index_count - + legs_index_count - + head_index_count - + mandibles_index_count - + antennae_index_count - + waist_index_count - + gaster_index_count - + sting_index_count; - - // Calculate transform from legs space to body space - const math::transform& legs_to_body = math::transform::identity; - - // Reskin leg bones - std::unordered_set old_procoxa_l_indices; - if (auto it = legs_skeleton.bone_map.find("procoxa_l"); it != legs_skeleton.bone_map.end()) - old_procoxa_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_procoxa_l_indices, procoxa_l_bone_index, legs_to_body); - std::unordered_set old_profemur_l_indices; - if (auto it = legs_skeleton.bone_map.find("profemur_l"); it != legs_skeleton.bone_map.end()) - old_profemur_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_profemur_l_indices, profemur_l_bone_index, legs_to_body); - std::unordered_set old_protibia_l_indices; - if (auto it = legs_skeleton.bone_map.find("protibia_l"); it != legs_skeleton.bone_map.end()) - old_protibia_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protibia_l_indices, protibia_l_bone_index, legs_to_body); - std::unordered_set old_protarsus_l_indices; - if (auto it = legs_skeleton.bone_map.find("protarsus1_l"); it != legs_skeleton.bone_map.end()) - old_protarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus2_l"); it != legs_skeleton.bone_map.end()) - old_protarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus3_l"); it != legs_skeleton.bone_map.end()) - old_protarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus4_l"); it != legs_skeleton.bone_map.end()) - old_protarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus5_l"); it != legs_skeleton.bone_map.end()) - old_protarsus_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protarsus_l_indices, protarsus_l_bone_index, legs_to_body); - - std::unordered_set old_procoxa_r_indices; - if (auto it = legs_skeleton.bone_map.find("procoxa_r"); it != legs_skeleton.bone_map.end()) - old_procoxa_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_procoxa_r_indices, procoxa_r_bone_index, legs_to_body); - std::unordered_set old_profemur_r_indices; - if (auto it = legs_skeleton.bone_map.find("profemur_r"); it != legs_skeleton.bone_map.end()) - old_profemur_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_profemur_r_indices, profemur_r_bone_index, legs_to_body); - std::unordered_set old_protibia_r_indices; - if (auto it = legs_skeleton.bone_map.find("protibia_r"); it != legs_skeleton.bone_map.end()) - old_protibia_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protibia_r_indices, protibia_r_bone_index, legs_to_body); - std::unordered_set old_protarsus_r_indices; - if (auto it = legs_skeleton.bone_map.find("protarsus1_r"); it != legs_skeleton.bone_map.end()) - old_protarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus2_r"); it != legs_skeleton.bone_map.end()) - old_protarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus3_r"); it != legs_skeleton.bone_map.end()) - old_protarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus4_r"); it != legs_skeleton.bone_map.end()) - old_protarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus5_r"); it != legs_skeleton.bone_map.end()) - old_protarsus_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protarsus_r_indices, protarsus_r_bone_index, legs_to_body); - - std::unordered_set old_mesocoxa_l_indices; - if (auto it = legs_skeleton.bone_map.find("mesocoxa_l"); it != legs_skeleton.bone_map.end()) - old_mesocoxa_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesocoxa_l_indices, mesocoxa_l_bone_index, legs_to_body); - std::unordered_set old_mesofemur_l_indices; - if (auto it = legs_skeleton.bone_map.find("mesofemur_l"); it != legs_skeleton.bone_map.end()) - old_mesofemur_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesofemur_l_indices, mesofemur_l_bone_index, legs_to_body); - std::unordered_set old_mesotibia_l_indices; - if (auto it = legs_skeleton.bone_map.find("mesotibia_l"); it != legs_skeleton.bone_map.end()) - old_mesotibia_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotibia_l_indices, mesotibia_l_bone_index, legs_to_body); - std::unordered_set old_mesotarsus_l_indices; - if (auto it = legs_skeleton.bone_map.find("mesotarsus1_l"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus2_l"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus3_l"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus4_l"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus5_l"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotarsus_l_indices, mesotarsus_l_bone_index, legs_to_body); - - std::unordered_set old_mesocoxa_r_indices; - if (auto it = legs_skeleton.bone_map.find("mesocoxa_r"); it != legs_skeleton.bone_map.end()) - old_mesocoxa_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesocoxa_r_indices, mesocoxa_r_bone_index, legs_to_body); - std::unordered_set old_mesofemur_r_indices; - if (auto it = legs_skeleton.bone_map.find("mesofemur_r"); it != legs_skeleton.bone_map.end()) - old_mesofemur_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesofemur_r_indices, mesofemur_r_bone_index, legs_to_body); - std::unordered_set old_mesotibia_r_indices; - if (auto it = legs_skeleton.bone_map.find("mesotibia_r"); it != legs_skeleton.bone_map.end()) - old_mesotibia_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotibia_r_indices, mesotibia_r_bone_index, legs_to_body); - std::unordered_set old_mesotarsus_r_indices; - if (auto it = legs_skeleton.bone_map.find("mesotarsus1_r"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus2_r"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus3_r"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus4_r"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus5_r"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotarsus_r_indices, mesotarsus_r_bone_index, legs_to_body); - - std::unordered_set old_metacoxa_l_indices; - if (auto it = legs_skeleton.bone_map.find("metacoxa_l"); it != legs_skeleton.bone_map.end()) - old_metacoxa_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metacoxa_l_indices, metacoxa_l_bone_index, legs_to_body); - std::unordered_set old_metafemur_l_indices; - if (auto it = legs_skeleton.bone_map.find("metafemur_l"); it != legs_skeleton.bone_map.end()) - old_metafemur_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metafemur_l_indices, metafemur_l_bone_index, legs_to_body); - std::unordered_set old_metatibia_l_indices; - if (auto it = legs_skeleton.bone_map.find("metatibia_l"); it != legs_skeleton.bone_map.end()) - old_metatibia_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatibia_l_indices, metatibia_l_bone_index, legs_to_body); - std::unordered_set old_metatarsus_l_indices; - if (auto it = legs_skeleton.bone_map.find("metatarsus1_l"); it != legs_skeleton.bone_map.end()) - old_metatarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus2_l"); it != legs_skeleton.bone_map.end()) - old_metatarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus3_l"); it != legs_skeleton.bone_map.end()) - old_metatarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus4_l"); it != legs_skeleton.bone_map.end()) - old_metatarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus5_l"); it != legs_skeleton.bone_map.end()) - old_metatarsus_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatarsus_l_indices, metatarsus_l_bone_index, legs_to_body); - - std::unordered_set old_metacoxa_r_indices; - if (auto it = legs_skeleton.bone_map.find("metacoxa_r"); it != legs_skeleton.bone_map.end()) - old_metacoxa_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metacoxa_r_indices, metacoxa_r_bone_index, legs_to_body); - std::unordered_set old_metafemur_r_indices; - if (auto it = legs_skeleton.bone_map.find("metafemur_r"); it != legs_skeleton.bone_map.end()) - old_metafemur_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metafemur_r_indices, metafemur_r_bone_index, legs_to_body); - std::unordered_set old_metatibia_r_indices; - if (auto it = legs_skeleton.bone_map.find("metatibia_r"); it != legs_skeleton.bone_map.end()) - old_metatibia_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatibia_r_indices, metatibia_r_bone_index, legs_to_body); - std::unordered_set old_metatarsus_r_indices; - if (auto it = legs_skeleton.bone_map.find("metatarsus1_r"); it != legs_skeleton.bone_map.end()) - old_metatarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus2_r"); it != legs_skeleton.bone_map.end()) - old_metatarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus3_r"); it != legs_skeleton.bone_map.end()) - old_metatarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus4_r"); it != legs_skeleton.bone_map.end()) - old_metatarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus5_r"); it != legs_skeleton.bone_map.end()) - old_metatarsus_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatarsus_r_indices, metatarsus_r_bone_index, legs_to_body); - - // Calculate transform from head space to body space - math::transform head_to_body = bind_pose_ss.at(mesosoma_bone) * mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("head")); - - // Reskin head bone - std::unordered_set old_head_bone_indices; - if (auto it = head_skeleton.bone_map.find("head"); it != head_skeleton.bone_map.end()) - old_head_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + head_vbo_offset, head_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_head_bone_indices, head_bone_index, head_to_body); - - // Calculate transforms from mandibles space to body space - math::transform mandible_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_l")); - math::transform mandible_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_r")); - - // Reskin mandible bones - std::unordered_set old_head_mandible_l_bone_indices; - if (auto it = mandibles_skeleton.bone_map.find("mandible_l"); it != mandibles_skeleton.bone_map.end()) - old_head_mandible_l_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + mandibles_vbo_offset, mandibles_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_head_mandible_l_bone_indices, mandible_l_bone_index, mandible_l_to_body); - std::unordered_set old_head_mandible_r_bone_indices; - if (auto it = mandibles_skeleton.bone_map.find("mandible_r"); it != mandibles_skeleton.bone_map.end()) - old_head_mandible_r_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + mandibles_vbo_offset, mandibles_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_head_mandible_r_bone_indices, mandible_r_bone_index, mandible_r_to_body); - - // Calculate transforms from antennae space to body space - math::transform antenna_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_l")); - math::transform antenna_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_r")); - - // Reskin antennomere1 bones - std::unordered_set old_antennomere1_l_indices; - if (auto it = antennae_skeleton.bone_map.find("antennomere1_l"); it != antennae_skeleton.bone_map.end()) - old_antennomere1_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere1_l_indices, antennomere1_l_bone_index, antenna_l_to_body); - std::unordered_set old_antennomere1_r_indices; - if (auto it = antennae_skeleton.bone_map.find("antennomere1_r"); it != antennae_skeleton.bone_map.end()) - old_antennomere1_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere1_r_indices, antennomere1_r_bone_index, antenna_r_to_body); - - // Reskin antennomere2+ bones - const std::vector antennomere_l_bone_keys = - { - "antennomere2_l", - "antennomere3_l", - "antennomere4_l", - "antennomere5_l", - "antennomere6_l", - "antennomere7_l", - "antennomere8_l", - "antennomere9_l", - "antennomere10_l", - "antennomere11_l", - "antennomere12_l", - "antennomere13_l" + math::transform forewing_l_to_body; + math::transform forewing_r_to_body; + math::transform hindwing_l_to_body; + math::transform hindwing_r_to_body; + if (phenome.wings->present) + { + forewing_l_to_body = bind_pose_ss.at(*bones.mesosoma) * mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("forewing_l")); + forewing_r_to_body = bind_pose_ss.at(*bones.mesosoma) * mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("forewing_r")); + hindwing_l_to_body = bind_pose_ss.at(*bones.mesosoma) * mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("hindwing_l")); + hindwing_r_to_body = bind_pose_ss.at(*bones.mesosoma) * mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("hindwing_r")); + } + + // Build legs vertex reskin map + const std::unordered_map*>> legs_reskin_map + { + {bone_index(legs_skeleton.bone_map.at("procoxa_l")), {bone_index(*bones.procoxa_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("profemur_l")), {bone_index(*bones.profemur_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("protibia_l")), {bone_index(*bones.protibia_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("protarsus1_l")), {bone_index(*bones.protarsus_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("protarsus2_l")), {bone_index(*bones.protarsus_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("protarsus3_l")), {bone_index(*bones.protarsus_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("protarsus4_l")), {bone_index(*bones.protarsus_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("protarsus5_l")), {bone_index(*bones.protarsus_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("procoxa_r")), {bone_index(*bones.procoxa_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("profemur_r")), {bone_index(*bones.profemur_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("protibia_r")), {bone_index(*bones.protibia_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("protarsus1_r")), {bone_index(*bones.protarsus_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("protarsus2_r")), {bone_index(*bones.protarsus_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("protarsus3_r")), {bone_index(*bones.protarsus_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("protarsus4_r")), {bone_index(*bones.protarsus_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("protarsus5_r")), {bone_index(*bones.protarsus_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("mesocoxa_l")), {bone_index(*bones.mesocoxa_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("mesofemur_l")), {bone_index(*bones.mesofemur_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("mesotibia_l")), {bone_index(*bones.mesotibia_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("mesotarsus1_l")), {bone_index(*bones.mesotarsus_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("mesotarsus2_l")), {bone_index(*bones.mesotarsus_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("mesotarsus3_l")), {bone_index(*bones.mesotarsus_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("mesotarsus4_l")), {bone_index(*bones.mesotarsus_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("mesotarsus5_l")), {bone_index(*bones.mesotarsus_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("mesocoxa_r")), {bone_index(*bones.mesocoxa_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("mesofemur_r")), {bone_index(*bones.mesofemur_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("mesotibia_r")), {bone_index(*bones.mesotibia_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("mesotarsus1_r")), {bone_index(*bones.mesotarsus_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("mesotarsus2_r")), {bone_index(*bones.mesotarsus_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("mesotarsus3_r")), {bone_index(*bones.mesotarsus_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("mesotarsus4_r")), {bone_index(*bones.mesotarsus_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("mesotarsus5_r")), {bone_index(*bones.mesotarsus_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("metacoxa_l")), {bone_index(*bones.metacoxa_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("metafemur_l")), {bone_index(*bones.metafemur_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("metatibia_l")), {bone_index(*bones.metatibia_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("metatarsus1_l")), {bone_index(*bones.metatarsus_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("metatarsus2_l")), {bone_index(*bones.metatarsus_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("metatarsus3_l")), {bone_index(*bones.metatarsus_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("metatarsus4_l")), {bone_index(*bones.metatarsus_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("metatarsus5_l")), {bone_index(*bones.metatarsus_l), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("metacoxa_r")), {bone_index(*bones.metacoxa_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("metafemur_r")), {bone_index(*bones.metafemur_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("metatibia_r")), {bone_index(*bones.metatibia_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("metatarsus1_r")), {bone_index(*bones.metatarsus_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("metatarsus2_r")), {bone_index(*bones.metatarsus_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("metatarsus3_r")), {bone_index(*bones.metatarsus_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("metatarsus4_r")), {bone_index(*bones.metatarsus_r), &legs_to_body}}, + {bone_index(legs_skeleton.bone_map.at("metatarsus5_r")), {bone_index(*bones.metatarsus_r), &legs_to_body}} }; - const std::vector antennomere_r_bone_keys = - { - "antennomere2_r", - "antennomere3_r", - "antennomere4_r", - "antennomere5_r", - "antennomere6_r", - "antennomere7_r", - "antennomere8_r", - "antennomere9_r", - "antennomere10_r", - "antennomere11_r", - "antennomere12_r", - "antennomere13_r" + + // Build head vertex reskin map + const std::unordered_map*>> head_reskin_map + { + {bone_index(head_skeleton.bone_map.at("head")), {bone_index(*bones.head), &head_to_body}} }; - std::unordered_set old_antennomere_l_indices; - for (const auto& bone_key: antennomere_l_bone_keys) - if (auto it = antennae_skeleton.bone_map.find(bone_key); it != antennae_skeleton.bone_map.end()) - old_antennomere_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere_l_indices, antennomere2_l_bone_index, antenna_l_to_body); - std::unordered_set old_antennomere_r_indices; - for (const auto& bone_key: antennomere_r_bone_keys) - if (auto it = antennae_skeleton.bone_map.find(bone_key); it != antennae_skeleton.bone_map.end()) - old_antennomere_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere_r_indices, antennomere2_r_bone_index, antenna_r_to_body); - - // Calculate transform from waist space to body space - math::transform waist_to_body = bind_pose_ss.at(mesosoma_bone) * mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")); - - // Reskin waist bones - std::unordered_set old_petiole_bone_indices; - if (auto it = waist_skeleton.bone_map.find("petiole"); it != waist_skeleton.bone_map.end()) - old_petiole_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + waist_vbo_offset, waist_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_petiole_bone_indices, petiole_bone_index, waist_to_body); - if (postpetiole_present) - { - std::unordered_set old_postpetiole_bone_indices; - if (auto it = waist_skeleton.bone_map.find("postpetiole"); it != waist_skeleton.bone_map.end()) - old_postpetiole_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + waist_vbo_offset, waist_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_postpetiole_bone_indices, postpetiole_bone_index, waist_to_body); - } - - // Calculate transform from gaster space to body space - math::transform gaster_to_body = bind_pose_ss.at(bone_parent_index(gaster_bone)) * waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("gaster")); - - // Reskin gaster bones - std::unordered_set old_gaster_bone_indices; - if (auto it = gaster_skeleton.bone_map.find("gaster"); it != gaster_skeleton.bone_map.end()) - old_gaster_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + gaster_vbo_offset, gaster_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_gaster_bone_indices, gaster_bone_index, gaster_to_body); - - if (sting_present) - { - // Calculate transform from sting space to body space - math::transform sting_to_body = gaster_to_body * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("sting")); - - // Reskin sting bones - std::unordered_set old_sting_bone_indices; - if (auto it = sting_skeleton->bone_map.find("sting"); it != sting_skeleton->bone_map.end()) - old_sting_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + sting_vbo_offset, sting_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_sting_bone_indices, sting_bone_index, sting_to_body); - } + // Build mandibles vertex reskin map + const std::unordered_map*>> mandibles_reskin_map + { + {bone_index(mandibles_skeleton.bone_map.at("mandible_l")), {bone_index(*bones.mandible_l), &mandible_l_to_body}}, + {bone_index(mandibles_skeleton.bone_map.at("mandible_r")), {bone_index(*bones.mandible_r), &mandible_r_to_body}} + }; - if (eyes_present) + // Build antennae vertex reskin map + std::unordered_map*>> antennae_reskin_map { - // Calculate transforms from eyes space to body space - math::transform eye_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("eye_l")); - math::transform eye_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("eye_r")); + {bone_index(antennae_skeleton.bone_map.at("antennomere1_l")), {bone_index(*bones.antennomere1_l), &antenna_l_to_body}}, + {bone_index(antennae_skeleton.bone_map.at("antennomere2_l")), {bone_index(*bones.antennomere2_l), &antenna_l_to_body}}, + {bone_index(antennae_skeleton.bone_map.at("antennomere1_r")), {bone_index(*bones.antennomere1_r), &antenna_r_to_body}}, + {bone_index(antennae_skeleton.bone_map.at("antennomere2_r")), {bone_index(*bones.antennomere2_r), &antenna_r_to_body}} + }; + for (std::uint8_t i = 3; i <= phenome.antennae->total_antennomere_count; ++i) + { + const std::string antennomere_l_name = std::format("antennomere{}_l", i); + const std::string antennomere_r_name = std::format("antennomere{}_r", i); - // Reskin eye bones - std::unordered_set old_eye_l_bone_indices; - if (auto it = eyes_skeleton->bone_map.find("eye_l"); it != eyes_skeleton->bone_map.end()) - old_eye_l_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + eyes_vbo_offset, eyes_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_eye_l_bone_indices, head_bone_index, eye_l_to_body); - std::unordered_set old_eye_r_bone_indices; - if (auto it = eyes_skeleton->bone_map.find("eye_r"); it != eyes_skeleton->bone_map.end()) - old_eye_r_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + eyes_vbo_offset, eyes_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_eye_r_bone_indices, head_bone_index, eye_r_to_body); - } - - if (lateral_ocelli_present) - { - // Calculate transforms from lateral ocelli space to body space - math::transform ocellus_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_l")); - math::transform ocellus_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_r")); + const hash::fnv1a32_t antennomere_l_key = hash::fnv1a32(antennomere_l_name); + const hash::fnv1a32_t antennomere_r_key = hash::fnv1a32(antennomere_r_name); - // Reskin lateral ocelli bones - std::unordered_set old_ocellus_l_bone_indices; - if (auto it = lateral_ocelli_skeleton->bone_map.find("ocellus_l"); it != lateral_ocelli_skeleton->bone_map.end()) - old_ocellus_l_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + lateral_ocelli_vbo_offset, lateral_ocelli_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_ocellus_l_bone_indices, head_bone_index, ocellus_l_to_body); - std::unordered_set old_ocellus_r_bone_indices; - if (auto it = lateral_ocelli_skeleton->bone_map.find("ocellus_r"); it != lateral_ocelli_skeleton->bone_map.end()) - old_ocellus_r_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + lateral_ocelli_vbo_offset, lateral_ocelli_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_ocellus_r_bone_indices, head_bone_index, ocellus_r_to_body); + antennae_reskin_map.emplace(bone_index(antennae_skeleton.bone_map.at(antennomere_l_key)), std::tuple(bone_index(*bones.antennomere2_l), &antenna_l_to_body)); + antennae_reskin_map.emplace(bone_index(antennae_skeleton.bone_map.at(antennomere_r_key)), std::tuple(bone_index(*bones.antennomere2_r), &antenna_r_to_body)); + } + + // Build waist vertex reskin map + std::unordered_map*>> waist_reskin_map; + if (phenome.waist->petiole_present) + { + waist_reskin_map.emplace(bone_index(waist_skeleton.bone_map.at("petiole")), std::tuple(bone_index(*bones.petiole), &waist_to_body)); + } + if (phenome.waist->postpetiole_present) + { + waist_reskin_map.emplace(bone_index(waist_skeleton.bone_map.at("postpetiole")), std::tuple(bone_index(*bones.postpetiole), &waist_to_body)); + } + + // Build gaster vertex reskin map + const std::unordered_map*>> gaster_reskin_map + { + {bone_index(gaster_skeleton.bone_map.at("gaster")), {bone_index(*bones.gaster), &gaster_to_body}} + }; + + // Build sting vertex reskin map + std::unordered_map*>> sting_reskin_map; + if (phenome.sting->present) + { + sting_reskin_map.emplace(bone_index(sting_skeleton->bone_map.at("sting")), std::tuple(bone_index(*bones.sting), &sting_to_body)); + } + + // Build eyes vertex reskin map + std::unordered_map*>> eyes_reskin_map; + if (phenome.eyes->present) + { + eyes_reskin_map.emplace(bone_index(eyes_skeleton->bone_map.at("eye_l")), std::tuple(bone_index(*bones.head), &eye_l_to_body)); + eyes_reskin_map.emplace(bone_index(eyes_skeleton->bone_map.at("eye_r")), std::tuple(bone_index(*bones.head), &eye_r_to_body)); + } + + // Build lateral ocelli vertex reskin map + std::unordered_map*>> lateral_ocelli_reskin_map; + if (phenome.ocelli->lateral_ocelli_present) + { + lateral_ocelli_reskin_map.emplace(bone_index(lateral_ocelli_skeleton->bone_map.at("ocellus_l")), std::tuple(bone_index(*bones.head), &ocellus_l_to_body)); + lateral_ocelli_reskin_map.emplace(bone_index(lateral_ocelli_skeleton->bone_map.at("ocellus_r")), std::tuple(bone_index(*bones.head), &ocellus_r_to_body)); + } + + // Build median ocellus vertex reskin map + std::unordered_map*>> median_ocellus_reskin_map; + if (phenome.ocelli->median_ocellus_present) + { + median_ocellus_reskin_map.emplace(bone_index(median_ocellus_skeleton->bone_map.at("ocellus_m")), std::tuple(bone_index(*bones.head), &ocellus_m_to_body)); + } + + // Build wings vertex reskin maps + std::unordered_map*>> forewings_reskin_map; + std::unordered_map*>> hindwings_reskin_map; + if (phenome.wings->present) + { + forewings_reskin_map.emplace(bone_index(forewings_skeleton->bone_map.at("forewing_l")), std::tuple(bone_index(*bones.forewing_l), &forewing_l_to_body)); + forewings_reskin_map.emplace(bone_index(forewings_skeleton->bone_map.at("forewing_r")), std::tuple(bone_index(*bones.forewing_r), &forewing_r_to_body)); + hindwings_reskin_map.emplace(bone_index(hindwings_skeleton->bone_map.at("hindwing_l")), std::tuple(bone_index(*bones.hindwing_l), &hindwing_l_to_body)); + hindwings_reskin_map.emplace(bone_index(hindwings_skeleton->bone_map.at("hindwing_r")), std::tuple(bone_index(*bones.hindwing_r), &hindwing_r_to_body)); + } + + // Reskin legs vertices + reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, legs_reskin_map); + + // Reskin head vertices + reskin_vertices(vertex_buffer_data.data() + head_vbo_offset, head_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, head_reskin_map); + + // Reskin mandibles vertices + reskin_vertices(vertex_buffer_data.data() + mandibles_vbo_offset, mandibles_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, mandibles_reskin_map); + + // Reskin antennae vertices + reskin_vertices(vertex_buffer_data.data() + antennae_vbo_offset, antennae_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, antennae_reskin_map); + + // Reskin waist vertices + reskin_vertices(vertex_buffer_data.data() + waist_vbo_offset, waist_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, waist_reskin_map); + + // Reskin gaster vertices + reskin_vertices(vertex_buffer_data.data() + gaster_vbo_offset, gaster_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, gaster_reskin_map); + + // Reskin sting vertices + if (phenome.sting->present) + { + reskin_vertices(vertex_buffer_data.data() + sting_vbo_offset, sting_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, sting_reskin_map); + } + + // Reskin eyes vertices + if (phenome.eyes->present) + { + reskin_vertices(vertex_buffer_data.data() + eyes_vbo_offset, eyes_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, eyes_reskin_map); + } + + // Reskin lateral ocelli vertices + if (phenome.ocelli->lateral_ocelli_present) + { + reskin_vertices(vertex_buffer_data.data() + lateral_ocelli_vbo_offset, lateral_ocelli_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, lateral_ocelli_reskin_map); + } + + // Reskin median ocellus vertices + if (phenome.ocelli->median_ocellus_present) + { + reskin_vertices(vertex_buffer_data.data() + median_ocellus_vbo_offset, median_ocellus_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, median_ocellus_reskin_map); } - if (median_ocellus_present) + // Reskin wings vertices + if (phenome.wings->present) { - // Calculate transforms from lateral ocelli space to body space - math::transform ocellus_m_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_m")); + reskin_vertices(vertex_buffer_data.data() + forewings_vbo_offset, forewings_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, forewings_reskin_map); - // Reskin lateral ocelli bones - std::unordered_set old_ocellus_m_bone_indices; - if (auto it = median_ocellus_skeleton->bone_map.find("ocellus_m"); it != median_ocellus_skeleton->bone_map.end()) - old_ocellus_m_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data.data() + median_ocellus_vbo_offset, median_ocellus_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_ocellus_m_bone_indices, head_bone_index, ocellus_m_to_body); + reskin_vertices(vertex_buffer_data.data() + hindwings_vbo_offset, hindwings_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, hindwings_reskin_map); } // Upload vertex buffer data to model VBO model->get_vertex_buffer()->repurpose(gl::buffer_usage::static_draw, vertex_buffer_size, vertex_buffer_data); + // Count number of model groups + std::size_t model_group_count = 1; + if (phenome.eyes->present) + { + ++model_group_count; + } + if (phenome.ocelli->lateral_ocelli_present || phenome.ocelli->median_ocellus_present) + { + ++model_group_count; + } + if (phenome.wings->present) + { + model_group_count += 2; + } + + // Allocate model groups + model->get_groups().resize(model_group_count); + + std::size_t model_group_index = 0; + std::uint32_t index_offset = 0; + // Construct exoskeleton model group - model->get_groups().resize(1); - render::model_group& exoskeleton_group = model->get_groups().back(); + render::model_group& exoskeleton_group = model->get_groups()[model_group_index]; exoskeleton_group.id = "exoskeleton"; exoskeleton_group.material = exoskeleton_material; exoskeleton_group.drawing_mode = gl::drawing_mode::triangles; - exoskeleton_group.start_index = 0; - exoskeleton_group.index_count = exoskeleton_index_count; + exoskeleton_group.start_index = index_offset; + exoskeleton_group.index_count = exoskeleton_vertex_count; - std::uint32_t index_offset = exoskeleton_index_count; - if (eyes_present) + index_offset += exoskeleton_group.index_count; + + if (phenome.eyes->present) { // Construct eyes model group - model->get_groups().resize(model->get_groups().size() + 1); - render::model_group& eyes_group = model->get_groups().back(); + render::model_group& eyes_group = model->get_groups()[++model_group_index]; eyes_group.id = "eyes"; eyes_group.material = eyes_model->get_groups().front().material; eyes_group.drawing_mode = gl::drawing_mode::triangles; eyes_group.start_index = index_offset; - eyes_group.index_count = eyes_index_count; + eyes_group.index_count = eyes_vertex_count; - index_offset += eyes_index_count; + index_offset += eyes_group.index_count; } - if (lateral_ocelli_present || median_ocellus_present) + if (phenome.ocelli->lateral_ocelli_present || phenome.ocelli->median_ocellus_present) { // Construct ocelli model group - model->get_groups().resize(model->get_groups().size() + 1); - render::model_group& ocelli_group = model->get_groups().back(); + render::model_group& ocelli_group = model->get_groups()[++model_group_index]; ocelli_group.id = "ocelli"; ocelli_group.drawing_mode = gl::drawing_mode::triangles; ocelli_group.start_index = index_offset; + ocelli_group.index_count = 0; - std::uint32_t index_count = 0; - if (lateral_ocelli_present) + if (phenome.ocelli->lateral_ocelli_present) { - index_count += lateral_ocelli_index_count; - index_offset += lateral_ocelli_index_count; + ocelli_group.index_count += lateral_ocelli_vertex_count; ocelli_group.material = lateral_ocelli_model->get_groups().front().material; } - if (median_ocellus_present) + if (phenome.ocelli->median_ocellus_present) { - index_count += median_ocellus_index_count; - index_offset += median_ocellus_index_count; - if (!lateral_ocelli_present) - { - ocelli_group.material = median_ocellus_model->get_groups().front().material; - } + ocelli_group.index_count += median_ocellus_vertex_count; + ocelli_group.material = median_ocellus_model->get_groups().front().material; } - ocelli_group.index_count = index_count; + index_offset += ocelli_group.index_count; } - if (wings_present) + if (phenome.wings->present) { // Construct forewings model group - model->get_groups().resize(model->get_groups().size() + 1); - render::model_group& forewings_group = model->get_groups().back(); + render::model_group& forewings_group = model->get_groups()[++model_group_index]; forewings_group.id = "forewings"; forewings_group.material = forewings_model->get_groups().front().material; forewings_group.drawing_mode = gl::drawing_mode::triangles; forewings_group.start_index = index_offset; - forewings_group.index_count = forewings_index_count; - index_offset += forewings_index_count; + forewings_group.index_count = forewings_vertex_count; + + index_offset += forewings_group.index_count; // Construct hindwings model group - model->get_groups().resize(model->get_groups().size() + 1); - render::model_group& hindwings_group = model->get_groups().back(); + render::model_group& hindwings_group = model->get_groups()[++model_group_index]; hindwings_group.id = "hindwings"; hindwings_group.material = hindwings_model->get_groups().front().material; hindwings_group.drawing_mode = gl::drawing_mode::triangles; hindwings_group.start_index = index_offset; - hindwings_group.index_count = hindwings_index_count; - index_offset += hindwings_index_count; + hindwings_group.index_count = hindwings_vertex_count; + + index_offset += hindwings_group.index_count; } // Calculate model bounding box @@ -953,96 +1172,3 @@ std::unique_ptr ant_morphogenesis(const ant_phenome& phenome) return model; } - -void reskin_vertices -( - std::byte* vertex_data, - std::size_t index_count, - const gl::vertex_attribute& position_attribute, - const gl::vertex_attribute& normal_attribute, - const gl::vertex_attribute& tangent_attribute, - const gl::vertex_attribute& bone_index_attribute, - const std::unordered_set& old_bone_indices, - std::uint8_t new_bone_index, - const math::transform& transform -) -{ - std::byte* position_data = vertex_data + position_attribute.offset; - std::byte* normal_data = vertex_data + normal_attribute.offset; - std::byte* tangent_data = vertex_data + tangent_attribute.offset; - std::byte* bone_index_data = vertex_data + bone_index_attribute.offset; - - for (std::size_t i = 0; i < index_count; ++i) - { - // Get bone index of current vertex - float* bone_index = reinterpret_cast(bone_index_data + bone_index_attribute.stride * i); - - // Skip irrelevant bones - if (!old_bone_indices.count(static_cast(*bone_index + 0.5f))) - continue; - - // Get vertex position - float* x = reinterpret_cast(position_data + position_attribute.stride * i); - float* y = x + 1; - float* z = y + 1; - - // Get vertex normal - float* nx = reinterpret_cast(normal_data + normal_attribute.stride * i); - float* ny = nx + 1; - float* nz = ny + 1; - - // Get vertex tangent - float* tx = reinterpret_cast(tangent_data + tangent_attribute.stride * i); - float* ty = tx + 1; - float* tz = ty + 1; - //float* bts = tz + 1; - - // Transform vertex attributes - float3 position = transform * float3{*x, *y, *z}; - float3 normal = math::normalize(transform.rotation * float3{*nx, *ny, *nz}); - float3 tangent = transform.rotation * float3{*tx, *ty, *tz}; - - // Update vertex data - *x = position.x(); - *y = position.y(); - *z = position.z(); - *nx = normal.x(); - *ny = normal.y(); - *nz = normal.z(); - *tx = tangent.x(); - *ty = tangent.y(); - *tz = tangent.z(); - //*bts = ... - *bone_index = static_cast(new_bone_index); - } -} - -geom::aabb calculate_bounds(const std::byte* vertex_data, std::size_t index_count, const gl::vertex_attribute& position_attribute) -{ - const std::byte* position_data = vertex_data + position_attribute.offset; - - geom::aabb bounds; - bounds.min_point.x() = std::numeric_limits::infinity(); - bounds.min_point.y() = std::numeric_limits::infinity(); - bounds.min_point.z() = std::numeric_limits::infinity(); - bounds.max_point.x() = -std::numeric_limits::infinity(); - bounds.max_point.y() = -std::numeric_limits::infinity(); - bounds.max_point.z() = -std::numeric_limits::infinity(); - - for (std::size_t i = 0; i < index_count; ++i) - { - // Get vertex position - const float* x = reinterpret_cast(position_data + position_attribute.stride * i); - const float* y = x + 1; - const float* z = y + 1; - - bounds.min_point.x() = std::min(*x, bounds.min_point.x()); - bounds.min_point.y() = std::min(*y, bounds.min_point.y()); - bounds.min_point.z() = std::min(*z, bounds.min_point.z()); - bounds.max_point.x() = std::max(*x, bounds.max_point.x()); - bounds.max_point.y() = std::max(*y, bounds.max_point.y()); - bounds.max_point.z() = std::max(*z, bounds.max_point.z()); - } - - return bounds; -} diff --git a/src/game/ant/ant-morphogenesis.hpp b/src/game/ant/ant-morphogenesis.hpp index 9be523c..0f79367 100644 --- a/src/game/ant/ant-morphogenesis.hpp +++ b/src/game/ant/ant-morphogenesis.hpp @@ -27,9 +27,9 @@ /** * Generates a 3D model of an ant given its phenome. * - * @param phenome Phenome of an ant. + * @param phenome Ant phenome. * - * @return 3D model of the given phenome. + * @return Generated 3D model of the given phenome. */ [[nodiscard]] std::unique_ptr ant_morphogenesis(const ant_phenome& phenome); diff --git a/src/game/controls.cpp b/src/game/controls.cpp index 994aa31..2d67aeb 100644 --- a/src/game/controls.cpp +++ b/src/game/controls.cpp @@ -125,10 +125,11 @@ void reset_control_profile(::control_profile& profile) mappings.emplace("pause", std::make_unique(nullptr, input::scancode::escape, 0, false)); mappings.emplace("pause", std::make_unique(nullptr, input::gamepad_button::start)); - // Pick mate - mappings.emplace("pick_mate", std::make_unique(nullptr, input::mouse_button::left)); - mappings.emplace("pick_mate", std::make_unique(nullptr, input::mouse_button::middle)); - mappings.emplace("pick_mate", std::make_unique(nullptr, input::mouse_button::right)); + // Mouse pick + mappings.emplace("mouse_pick", std::make_unique(nullptr, input::mouse_button::left)); + + // Mouse look + mappings.emplace("mouse_look", std::make_unique(nullptr, input::mouse_button::right)); } void apply_control_profile(::game& ctx, const ::control_profile& profile) @@ -167,8 +168,10 @@ void apply_control_profile(::game& ctx, const ::control_profile& profile) add_mappings(ctx.movement_action_map, ctx.move_down_action, "move_down"); add_mappings(ctx.movement_action_map, ctx.pause_action, "pause"); - // Nuptial flight controls - add_mappings(ctx.nuptial_flight_action_map, ctx.pick_mate_action, "pick_mate"); + // Keeper controls + ctx.keeper_action_map.remove_mappings(); + add_mappings(ctx.keeper_action_map, ctx.mouse_pick_action, "mouse_pick"); + add_mappings(ctx.keeper_action_map, ctx.mouse_look_action, "mouse_look"); } void update_control_profile(::game& ctx, ::control_profile& profile) @@ -232,8 +235,9 @@ void update_control_profile(::game& ctx, ::control_profile& profile) add_mappings(ctx.movement_action_map, ctx.move_down_action, "move_down"); add_mappings(ctx.movement_action_map, ctx.pause_action, "pause"); - // Nuptial flight controls - add_mappings(ctx.nuptial_flight_action_map, ctx.pick_mate_action, "pick_mate"); + // Keeper controls + add_mappings(ctx.keeper_action_map, ctx.mouse_pick_action, "mouse_pick"); + add_mappings(ctx.keeper_action_map, ctx.mouse_look_action, "mouse_look"); } void setup_window_controls(::game& ctx) @@ -500,9 +504,9 @@ void enable_game_controls(::game& ctx) ctx.movement_action_map.enable(); } -void enable_nuptial_flight_controls(::game& ctx) +void enable_keeper_controls(::game& ctx) { - ctx.nuptial_flight_action_map.enable(); + ctx.keeper_action_map.enable(); } void disable_window_controls(::game& ctx) @@ -538,10 +542,11 @@ void disable_game_controls(::game& ctx) ctx.pause_action.reset(); } -void disable_nuptial_flight_controls(::game& ctx) +void disable_keeper_controls(::game& ctx) { - ctx.nuptial_flight_action_map.disable(); + ctx.keeper_action_map.disable(); - ctx.pick_mate_action.reset(); + ctx.mouse_pick_action.reset(); + ctx.mouse_look_action.reset(); } diff --git a/src/game/controls.hpp b/src/game/controls.hpp index 61061bc..5a7fba2 100644 --- a/src/game/controls.hpp +++ b/src/game/controls.hpp @@ -56,12 +56,12 @@ void setup_game_controls(::game& ctx); void enable_window_controls(::game& ctx); void enable_menu_controls(::game& ctx); void enable_game_controls(::game& ctx); -void enable_nuptial_flight_controls(::game& ctx); +void enable_keeper_controls(::game& ctx); void disable_window_controls(::game& ctx); void disable_menu_controls(::game& ctx); void disable_game_controls(::game& ctx); -void disable_nuptial_flight_controls(::game& ctx); +void disable_keeper_controls(::game& ctx); #endif // ANTKEEPER_GAME_CONTROLS_HPP diff --git a/src/game/game.cpp b/src/game/game.cpp index 8078711..00424c8 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -839,7 +839,7 @@ void game::setup_rendering() barycentric_attribute.stride = billboard_vertex_stride; barycentric_attribute.type = gl::vertex_attribute_type::float_32; barycentric_attribute.components = 3; - attribute_offset += barycentric_attribute.components * sizeof(float); + //attribute_offset += barycentric_attribute.components * sizeof(float); // Bind vertex attributes to VAO billboard_vao->bind(render::vertex_attribute::position, position_attribute); @@ -1187,7 +1187,7 @@ void game::setup_controls() window_action_map.set_event_queue(input_event_queue); menu_action_map.set_event_queue(input_event_queue); movement_action_map.set_event_queue(input_event_queue); - nuptial_flight_action_map.set_event_queue(input_event_queue); + keeper_action_map.set_event_queue(input_event_queue); // Default control profile settings control_profile_filename = "controls.cfg"; diff --git a/src/game/game.hpp b/src/game/game.hpp index 08cce7a..2c10167 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -194,7 +194,7 @@ public: input::action_map window_action_map; input::action_map menu_action_map; input::action_map movement_action_map; - input::action_map nuptial_flight_action_map; + input::action_map keeper_action_map; input::mapper input_mapper; input::action fullscreen_action; @@ -213,13 +213,21 @@ public: input::action move_up_action; input::action move_down_action; input::action pause_action; - input::action pick_mate_action; + input::action mouse_pick_action; + input::action mouse_look_action; std::vector> window_action_subscriptions; std::vector> menu_action_subscriptions; std::vector> menu_mouse_subscriptions; std::vector> movement_action_subscriptions; + // Control settings + float mouse_pan_sensitivity{1.0f}; + float mouse_tilt_sensitivity{1.0f}; + bool toggle_mouse_look{false}; + bool invert_mouse_pan{false}; + bool invert_mouse_tilt{false}; + // Debugging math::moving_average average_frame_time; std::unique_ptr frame_time_text; @@ -232,7 +240,7 @@ public: // Queue for scheduling "next frame" function calls std::queue> function_queue; - bool mouse_look; + /// Game loop ::loop loop; diff --git a/src/game/states/nest-selection-state.cpp b/src/game/states/nest-selection-state.cpp index 6aff80b..a4b364e 100644 --- a/src/game/states/nest-selection-state.cpp +++ b/src/game/states/nest-selection-state.cpp @@ -157,6 +157,9 @@ nest_selection_state::nest_selection_state(::game& ctx): // Satisfy first person camera rig constraints satisfy_first_person_camera_rig_constraints(); + // Setup controls + setup_controls(); + // auto color_checker_archetype = ctx.resource_manager->load("color-checker.ent"); // color_checker_archetype->create(*ctx.entity_registry); // auto ruler_archetype = ctx.resource_manager->load("ruler-10cm.ent"); @@ -195,6 +198,7 @@ nest_selection_state::nest_selection_state(::game& ctx): [&ctx]() { ::enable_game_controls(ctx); + ::enable_keeper_controls(ctx); } ); @@ -211,6 +215,7 @@ nest_selection_state::~nest_selection_state() // Disable game controls ::disable_game_controls(ctx); + ::disable_keeper_controls(ctx); destroy_first_person_camera_rig(); @@ -387,6 +392,120 @@ void nest_selection_state::satisfy_first_person_camera_rig_constraints() ); } +void nest_selection_state::setup_controls() +{ + // Enable/toggle mouse look + action_subscriptions.emplace_back + ( + ctx.mouse_look_action.get_activated_channel().subscribe + ( + [&](const auto& event) + { + if (ctx.toggle_mouse_look) + { + mouse_look = !mouse_look; + } + else + { + mouse_look = true; + } + + //ctx.app->set_relative_mouse_mode(mouse_look); + } + ) + ); + + // Disable mouse look + action_subscriptions.emplace_back + ( + ctx.mouse_look_action.get_deactivated_channel().subscribe + ( + [&](const auto& event) + { + if (!ctx.toggle_mouse_look && mouse_look) + { + mouse_look = false; + //ctx.app->set_relative_mouse_mode(false); + } + } + ) + ); + + // Mouse look + mouse_motion_subscription = ctx.input_manager->get_event_queue().subscribe + ( + [&](const auto& event) + { + if (!mouse_look) + { + return; + } + + const float mouse_tilt_factor = ctx.mouse_tilt_sensitivity * 0.01f * (ctx.invert_mouse_tilt ? -1.0f : 1.0f); + const float mouse_pan_factor = ctx.mouse_pan_sensitivity * 0.01f * (ctx.invert_mouse_pan ? -1.0f : 1.0f); + + ctx.entity_registry->patch + ( + first_person_camera_rig_spring_rotation_eid, + [&](auto& component) + { + component.spring.x1[0] -= mouse_pan_factor * static_cast(event.difference.x()); + component.spring.x1[1] += mouse_tilt_factor * static_cast(event.difference.y()); + component.spring.x1[1] = std::min(math::half_pi, std::max(-math::half_pi, component.spring.x1[1])); + } + ); + } + ); + + // Move forward + action_subscriptions.emplace_back + ( + ctx.move_forward_action.get_active_channel().subscribe + ( + [&](const auto& event) + { + move_first_person_camera_rig({0, -1}, event.input_value); + } + ) + ); + + // Move back + action_subscriptions.emplace_back + ( + ctx.move_back_action.get_active_channel().subscribe + ( + [&](const auto& event) + { + move_first_person_camera_rig({0, 1}, event.input_value); + } + ) + ); + + // Move left + action_subscriptions.emplace_back + ( + ctx.move_left_action.get_active_channel().subscribe + ( + [&](const auto& event) + { + move_first_person_camera_rig({-1, 0}, event.input_value); + } + ) + ); + + // Move right + action_subscriptions.emplace_back + ( + ctx.move_right_action.get_active_channel().subscribe + ( + [&](const auto& event) + { + move_first_person_camera_rig({1, 0}, event.input_value); + } + ) + ); +} + void nest_selection_state::enable_controls() { /* @@ -433,49 +552,7 @@ void nest_selection_state::enable_controls() const float gamepad_tilt_factor = gamepad_tilt_sensitivity * (gamepad_invert_tilt ? -1.0f : 1.0f); const float gamepad_pan_factor = gamepad_pan_sensitivity * (gamepad_invert_pan ? -1.0f : 1.0f); - // Mouse look control - ctx.controls["mouse_look"]->set_activated_callback - ( - [&, mouse_look_toggle]() - { - if (mouse_look_toggle) - mouse_look = !mouse_look; - else - mouse_look = true; - - ctx.app->set_relative_mouse_mode(mouse_look); - } - ); - ctx.controls["mouse_look"]->set_deactivated_callback - ( - [&, mouse_look_toggle]() - { - if (!mouse_look_toggle && mouse_look) - { - mouse_look = false; - ctx.app->set_relative_mouse_mode(false); - } - } - ); - // Look right control - ctx.controls["look_right_mouse"]->set_active_callback - ( - [&, mouse_pan_factor](float value) - { - if (!mouse_look) - return; - - ctx.entity_registry->patch - ( - first_person_camera_rig_spring_rotation_eid, - [&, mouse_pan_factor](auto& component) - { - component.spring.x1[0] -= mouse_pan_factor * value; - } - ); - } - ); ctx.controls["look_right_gamepad"]->set_active_callback ( [&, gamepad_pan_factor](float value) @@ -490,25 +567,6 @@ void nest_selection_state::enable_controls() ); } ); - - // Look left control - ctx.controls["look_left_mouse"]->set_active_callback - ( - [&, mouse_pan_factor](float value) - { - if (!mouse_look) - return; - - ctx.entity_registry->patch - ( - first_person_camera_rig_spring_rotation_eid, - [&, mouse_pan_factor](auto& component) - { - component.spring.x1[0] += mouse_pan_factor * value; - } - ); - } - ); ctx.controls["look_left_gamepad"]->set_active_callback ( [&, gamepad_pan_factor](float value) @@ -523,26 +581,6 @@ void nest_selection_state::enable_controls() ); } ); - - // Look up control - ctx.controls["look_up_mouse"]->set_active_callback - ( - [&, mouse_tilt_factor](float value) - { - if (!mouse_look) - return; - - ctx.entity_registry->patch - ( - first_person_camera_rig_spring_rotation_eid, - [&, mouse_tilt_factor](auto& component) - { - component.spring.x1[1] -= mouse_tilt_factor * value; - component.spring.x1[1] = std::max(-math::half_pi, component.spring.x1[1]); - } - ); - } - ); ctx.controls["look_up_gamepad"]->set_active_callback ( [&, gamepad_tilt_factor](float value) @@ -558,26 +596,6 @@ void nest_selection_state::enable_controls() ); } ); - - // Look down control - ctx.controls["look_down_mouse"]->set_active_callback - ( - [&, mouse_tilt_factor](float value) - { - if (!mouse_look) - return; - - ctx.entity_registry->patch - ( - first_person_camera_rig_spring_rotation_eid, - [&, mouse_tilt_factor](auto& component) - { - component.spring.x1[1] += mouse_tilt_factor * value; - component.spring.x1[1] = std::min(math::half_pi, component.spring.x1[1]); - } - ); - } - ); ctx.controls["look_down_gamepad"]->set_active_callback ( [&, gamepad_tilt_factor](float value) @@ -612,59 +630,6 @@ void nest_selection_state::enable_controls() } ); - // Mouse select control - ctx.controls["select_mouse"]->set_activated_callback - ( - [&]() - { - - } - ); - - // Move forward control - ctx.controls["move_forward"]->set_active_callback - ( - [&](float value) - { - move_first_person_camera_rig({0, -1}, value); - } - ); - - // Move back control - ctx.controls["move_back"]->set_active_callback - ( - [&](float value) - { - move_first_person_camera_rig({0, 1}, value); - } - ); - - // Move right control - ctx.controls["move_right"]->set_active_callback - ( - [&](float value) - { - move_first_person_camera_rig({1, 0}, value); - } - ); - - // Move left control - ctx.controls["move_left"]->set_active_callback - ( - [&](float value) - { - move_first_person_camera_rig({-1, 0}, value); - } - ); - - // Action control - ctx.controls["action"]->set_activated_callback - ( - [&]() - { - - } - ); // Fast-forward ctx.controls["fast_forward"]->set_activated_callback @@ -695,83 +660,10 @@ void nest_selection_state::enable_controls() ::world::set_time_scale(ctx, time_scale); } ); - - // Setup pause control - ctx.controls["pause"]->set_activated_callback - ( - [this, &ctx = this->ctx]() - { - // Disable controls - this->disable_controls(); - - // Set resume callback - ctx.resume_callback = [this, &ctx]() - { - this->enable_controls(); - ctx.resume_callback = nullptr; - }; - - // Push pause menu state - ctx.state_machine.emplace(std::make_unique(ctx)); - } - ); - - ctx.controls["increase_exposure"]->set_active_callback - ( - [&ctx = this->ctx](float) - { - //ctx.astronomy_system->set_exposure_offset(ctx.astronomy_system->get_exposure_offset() - 1.0f); - ctx.surface_camera->set_exposure(ctx.surface_camera->get_exposure() + 2.0f * static_cast(ctx.loop.get_update_period())); - debug::log::info("EV100: " + std::to_string(ctx.surface_camera->get_exposure())); - } - ); - - ctx.controls["decrease_exposure"]->set_active_callback - ( - [&ctx = this->ctx](float) - { - //ctx.astronomy_system->set_exposure_offset(ctx.astronomy_system->get_exposure_offset() + 1.0f); - ctx.surface_camera->set_exposure(ctx.surface_camera->get_exposure() - 2.0f * static_cast(ctx.loop.get_update_period())); - debug::log::info("EV100: " + std::to_string(ctx.surface_camera->get_exposure())); - } - ); */ } void nest_selection_state::disable_controls() { - /* - if (mouse_look) - { - mouse_look = false; - ctx.app->set_relative_mouse_mode(false); - } - - ctx.controls["mouse_look"]->set_activated_callback(nullptr); - ctx.controls["mouse_look"]->set_deactivated_callback(nullptr); - ctx.controls["look_right_mouse"]->set_active_callback(nullptr); - ctx.controls["look_right_gamepad"]->set_active_callback(nullptr); - ctx.controls["look_left_mouse"]->set_active_callback(nullptr); - ctx.controls["look_left_gamepad"]->set_active_callback(nullptr); - ctx.controls["look_up_mouse"]->set_active_callback(nullptr); - ctx.controls["look_up_gamepad"]->set_active_callback(nullptr); - ctx.controls["look_down_mouse"]->set_active_callback(nullptr); - ctx.controls["look_down_gamepad"]->set_active_callback(nullptr); - ctx.controls["move_up"]->set_active_callback(nullptr); - ctx.controls["move_down"]->set_active_callback(nullptr); - ctx.controls["select_mouse"]->set_activated_callback(nullptr); - ctx.controls["move_forward"]->set_active_callback(nullptr); - ctx.controls["move_back"]->set_active_callback(nullptr); - ctx.controls["move_right"]->set_active_callback(nullptr); - ctx.controls["move_left"]->set_active_callback(nullptr); - ctx.controls["action"]->set_activated_callback(nullptr); - ctx.controls["fast_forward"]->set_activated_callback(nullptr); - ctx.controls["fast_forward"]->set_deactivated_callback(nullptr); - ctx.controls["rewind"]->set_activated_callback(nullptr); - ctx.controls["rewind"]->set_deactivated_callback(nullptr); - ctx.controls["pause"]->set_activated_callback(nullptr); - ctx.controls["increase_exposure"]->set_active_callback(nullptr); - ctx.controls["decrease_exposure"]->set_active_callback(nullptr); - */ } diff --git a/src/game/states/nest-selection-state.hpp b/src/game/states/nest-selection-state.hpp index 889f128..b50bec0 100644 --- a/src/game/states/nest-selection-state.hpp +++ b/src/game/states/nest-selection-state.hpp @@ -37,9 +37,16 @@ private: void set_first_person_camera_rig_pedestal(float pedestal); void move_first_person_camera_rig(const float2& direction, float factor); void satisfy_first_person_camera_rig_constraints(); + + void setup_controls(); void enable_controls(); void disable_controls(); + std::vector> action_subscriptions; + std::shared_ptr<::event::subscription> mouse_motion_subscription; + + bool mouse_look{false}; + entity::id first_person_camera_rig_eid; entity::id first_person_camera_rig_spring_translation_eid; entity::id first_person_camera_rig_spring_rotation_eid; @@ -55,7 +62,6 @@ private: float first_person_camera_far_speed; float first_person_camera_rig_pedestal_speed; float first_person_camera_rig_pedestal; - bool mouse_look; }; #endif // ANTKEEPER_NEST_SELECTION_STATE_HPP diff --git a/src/game/states/nuptial-flight-state.cpp b/src/game/states/nuptial-flight-state.cpp index 1c716c1..6423417 100644 --- a/src/game/states/nuptial-flight-state.cpp +++ b/src/game/states/nuptial-flight-state.cpp @@ -201,7 +201,7 @@ nuptial_flight_state::nuptial_flight_state(::game& ctx): [&ctx]() { - ::enable_nuptial_flight_controls(ctx); + ::enable_keeper_controls(ctx); ::enable_game_controls(ctx); } ); @@ -221,7 +221,7 @@ nuptial_flight_state::~nuptial_flight_state() debug::log::trace("Exiting nuptial flight state..."); // Disable game controls - ::disable_nuptial_flight_controls(ctx); + ::disable_keeper_controls(ctx); ::disable_game_controls(ctx); // Remove text from UI @@ -468,7 +468,7 @@ void nuptial_flight_state::setup_controls() { action_subscriptions.emplace_back ( - ctx.pick_mate_action.get_activated_channel().subscribe + ctx.mouse_pick_action.get_activated_channel().subscribe ( [&](const auto& event) { diff --git a/src/game/states/nuptial-flight-state.hpp b/src/game/states/nuptial-flight-state.hpp index 070525e..362bd78 100644 --- a/src/game/states/nuptial-flight-state.hpp +++ b/src/game/states/nuptial-flight-state.hpp @@ -83,7 +83,7 @@ private: scene::text selection_text; // Controls - bool mouse_look; + bool mouse_look{false}; std::vector> action_subscriptions; };