/* * Copyright (C) 2021 Christopher J. Howard * * This file is part of Antkeeper source code. * * Antkeeper source code is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Antkeeper source code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Antkeeper source code. If not, see . */ #include "game/ant/morphogenesis.hpp" #include "render/material.hpp" #include "render/vertex-attribute.hpp" #include #include namespace game { namespace ant { static render::model* generate_queen(const ant::breed& breed); static render::model* generate_worker(const ant::breed& breed); static render::model* generate_soldier(const ant::breed& breed); static render::model* generate_male(const ant::breed& breed); static render::material* build_exoskeleton_material ( const ant::trait::pigmentation& pigmentation, const ant::trait::sculpturing& sculpturing ); static void reskin_vertices ( std::uint8_t* 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::uint16_t new_bone_index, const math::transform& transform ); static geom::aabb calculate_bounds(std::uint8_t* vertex_data, std::size_t index_count, const gl::vertex_attribute& position_attribute); static render::model* build_model ( 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 ); render::model* morphogenesis(const ant::breed& breed, ant::caste caste) { switch (caste) { case ant::caste::queen: return generate_queen(breed); case ant::caste::worker: return generate_worker(breed); case ant::caste::soldier: return generate_soldier(breed); case ant::caste::male: return generate_male(breed); } return nullptr; } render::model* generate_queen(const ant::breed& breed) { return nullptr; } render::model* generate_worker(const ant::breed& breed) { // Get material parameters // Build exoskeleton material render::material* exoskeleton_material = build_exoskeleton_material(*breed.pigmentation, *breed.sculpturing); // Get worker body part models render::model* antennae_model = breed.antennae->model; render::model* eyes_model = breed.eyes->model; render::model* gaster_model = breed.gaster->model; render::model* head_model = breed.head->model; render::model* legs_model = breed.legs->model; render::model* mandibles_model = breed.mandibles->model; render::model* mesosoma_model = breed.mesosoma->model; render::model* sting_model = breed.sting->model; render::model* waist_model = breed.waist->model; //render::model* lateral_ocelli_model = breed.ocelli->lateral_ocelli_model; //render::model* median_ocellus_model = breed.ocelli->median_ocellus_model; // Build worker model render::model* model = build_model ( exoskeleton_material, antennae_model, eyes_model, nullptr, // forewings gaster_model, head_model, nullptr, // hindwings legs_model, mandibles_model, mesosoma_model, nullptr, // lateral ocelli nullptr, // median ocellus sting_model, waist_model ); return model; } render::model* generate_soldier(const ant::breed& breed) { return nullptr; } render::model* generate_male(const ant::breed& breed) { return nullptr; } render::material* build_exoskeleton_material ( const ant::trait::pigmentation& pigmentation, const ant::trait::sculpturing& sculpturing ) { // Allocate copy of pigmentation material render::material* exoskeleton_material = new render::material(*pigmentation.material); // Adjust roughness parameter if (render::material_property_base* property = exoskeleton_material->get_property("roughness"); property != nullptr) { static_cast*>(property)->set_value(sculpturing.roughness); } else { exoskeleton_material->add_property("roughness")->set_value(sculpturing.roughness); } // Adjust normal map parameter if (render::material_property_base* property = exoskeleton_material->get_property("normal_map"); property != nullptr) { static_cast*>(property)->set_value(sculpturing.normal_map); } else { exoskeleton_material->add_property("normal_map")->set_value(sculpturing.normal_map); } return exoskeleton_material; } render::model* build_model ( render::material* exoskeleton_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 ) { // Get vertex buffers of required body parts const gl::vertex_buffer* mesosoma_vbo = mesosoma->get_vertex_buffer(); const gl::vertex_buffer* legs_vbo = legs->get_vertex_buffer(); const gl::vertex_buffer* head_vbo = head->get_vertex_buffer(); const gl::vertex_buffer* mandibles_vbo = mandibles->get_vertex_buffer(); const gl::vertex_buffer* antennae_vbo = antennae->get_vertex_buffer(); const gl::vertex_buffer* waist_vbo = waist->get_vertex_buffer(); const gl::vertex_buffer* gaster_vbo = gaster->get_vertex_buffer(); // Get vertex buffers of optional body parts const gl::vertex_buffer* sting_vbo = (sting) ? sting->get_vertex_buffer() : nullptr; const gl::vertex_buffer* eyes_vbo = (eyes) ? eyes->get_vertex_buffer() : nullptr; const gl::vertex_buffer* lateral_ocelli_vbo = (lateral_ocelli) ? lateral_ocelli->get_vertex_buffer() : nullptr; const gl::vertex_buffer* median_ocellus_vbo = (median_ocellus) ? median_ocellus->get_vertex_buffer() : nullptr; const gl::vertex_buffer* forewings_vbo = (forewings) ? forewings->get_vertex_buffer() : nullptr; const gl::vertex_buffer* hindwings_vbo = (hindwings) ? hindwings->get_vertex_buffer() : 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; vertex_buffer_size += mesosoma_vbo->get_size(); std::size_t legs_vbo_offset = vertex_buffer_size; vertex_buffer_size += legs_vbo->get_size(); std::size_t head_vbo_offset = vertex_buffer_size; vertex_buffer_size += head_vbo->get_size(); std::size_t mandibles_vbo_offset = vertex_buffer_size; vertex_buffer_size += mandibles_vbo->get_size(); std::size_t antennae_vbo_offset = vertex_buffer_size; vertex_buffer_size += antennae_vbo->get_size(); std::size_t waist_vbo_offset = vertex_buffer_size; vertex_buffer_size += waist_vbo->get_size(); std::size_t gaster_vbo_offset = vertex_buffer_size; vertex_buffer_size += gaster_vbo->get_size(); std::size_t sting_vbo_offset = vertex_buffer_size; if (sting) vertex_buffer_size += sting_vbo->get_size(); std::size_t eyes_vbo_offset = vertex_buffer_size; if (eyes) vertex_buffer_size += eyes_vbo->get_size(); std::size_t lateral_ocelli_vbo_offset = vertex_buffer_size; if (lateral_ocelli) vertex_buffer_size += lateral_ocelli_vbo->get_size(); std::size_t median_ocellus_vbo_offset = vertex_buffer_size; if (median_ocellus) vertex_buffer_size += median_ocellus_vbo->get_size(); std::size_t forewings_vbo_offset = vertex_buffer_size; if (forewings) vertex_buffer_size += forewings_vbo->get_size(); std::size_t hindwings_vbo_offset = vertex_buffer_size; if (hindwings) vertex_buffer_size += hindwings_vbo->get_size(); // Allocate combined vertex buffer data std::uint8_t* vertex_buffer_data = new std::uint8_t[vertex_buffer_size]; // Read body part vertex buffer data into combined vertex buffer data mesosoma_vbo->read(0, mesosoma_vbo->get_size(), vertex_buffer_data + mesosoma_vbo_offset); legs_vbo->read(0, legs_vbo->get_size(), vertex_buffer_data + legs_vbo_offset); head_vbo->read(0, head_vbo->get_size(), vertex_buffer_data + head_vbo_offset); mandibles_vbo->read(0, mandibles_vbo->get_size(), vertex_buffer_data + mandibles_vbo_offset); antennae_vbo->read(0, antennae_vbo->get_size(), vertex_buffer_data + antennae_vbo_offset); waist_vbo->read(0, waist_vbo->get_size(), vertex_buffer_data + waist_vbo_offset); gaster_vbo->read(0, gaster_vbo->get_size(), vertex_buffer_data + gaster_vbo_offset); if (sting) sting_vbo->read(0, sting_vbo->get_size(), vertex_buffer_data + sting_vbo_offset); if (eyes) eyes_vbo->read(0, eyes_vbo->get_size(), vertex_buffer_data + eyes_vbo_offset); if (lateral_ocelli) lateral_ocelli_vbo->read(0, lateral_ocelli_vbo->get_size(), vertex_buffer_data + lateral_ocelli_vbo_offset); if (median_ocellus) median_ocellus_vbo->read(0, median_ocellus_vbo->get_size(), vertex_buffer_data + median_ocellus_vbo_offset); if (forewings) forewings_vbo->read(0, forewings_vbo->get_size(), vertex_buffer_data + forewings_vbo_offset); if (hindwings) hindwings_vbo->read(0, hindwings_vbo->get_size(), vertex_buffer_data + hindwings_vbo_offset); // Allocate model render::model* model = new render::model(); // Setup model VAO gl::vertex_array* model_vao = model->get_vertex_array(); for (auto [location, attribute]: mesosoma->get_vertex_array()->get_attributes()) { attribute.buffer = model->get_vertex_buffer(); model_vao->bind(location, attribute); } // Get vertex attributes const gl::vertex_attribute* position_attribute = nullptr; 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->get_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 render::skeleton& mesosoma_skeleton = mesosoma->get_skeleton(); const render::skeleton& legs_skeleton = legs->get_skeleton(); const render::skeleton& head_skeleton = head->get_skeleton(); const render::skeleton& mandibles_skeleton = mandibles->get_skeleton(); const render::skeleton& antennae_skeleton = antennae->get_skeleton(); const render::skeleton& waist_skeleton = waist->get_skeleton(); const render::skeleton& gaster_skeleton = gaster->get_skeleton(); const render::skeleton* sting_skeleton = (sting) ? &sting->get_skeleton() : nullptr; const render::skeleton* eyes_skeleton = (eyes) ? &eyes->get_skeleton() : nullptr; const render::skeleton* lateral_ocelli_skeleton = (lateral_ocelli) ? &lateral_ocelli->get_skeleton() : nullptr; const render::skeleton* median_ocellus_skeleton = (median_ocellus) ? &median_ocellus->get_skeleton() : nullptr; bool postpetiole = (waist_skeleton.bone_map.find("postpetiole") != waist_skeleton.bone_map.end()); // Allocate skeleton bones render::skeleton& skeleton = model->get_skeleton(); std::size_t bone_count = 34; if (postpetiole) bone_count += 1; if (sting) bone_count += 1; if (forewings) bone_count += 2; if (hindwings) bone_count += 2; skeleton.bones.resize(bone_count); // Assign bone indices std::uint16_t bone_index = 0; std::uint16_t mesosoma_bone_index = bone_index++; std::uint16_t foreleg_coxa_l_bone_index = bone_index++; std::uint16_t foreleg_coxa_r_bone_index = bone_index++; std::uint16_t foreleg_femur_l_bone_index = bone_index++; std::uint16_t foreleg_femur_r_bone_index = bone_index++; std::uint16_t foreleg_tibia_l_bone_index = bone_index++; std::uint16_t foreleg_tibia_r_bone_index = bone_index++; std::uint16_t foreleg_tarsus_l_bone_index = bone_index++; std::uint16_t foreleg_tarsus_r_bone_index = bone_index++; std::uint16_t midleg_coxa_l_bone_index = bone_index++; std::uint16_t midleg_coxa_r_bone_index = bone_index++; std::uint16_t midleg_femur_l_bone_index = bone_index++; std::uint16_t midleg_femur_r_bone_index = bone_index++; std::uint16_t midleg_tibia_l_bone_index = bone_index++; std::uint16_t midleg_tibia_r_bone_index = bone_index++; std::uint16_t midleg_tarsus_l_bone_index = bone_index++; std::uint16_t midleg_tarsus_r_bone_index = bone_index++; std::uint16_t hindleg_coxa_l_bone_index = bone_index++; std::uint16_t hindleg_coxa_r_bone_index = bone_index++; std::uint16_t hindleg_femur_l_bone_index = bone_index++; std::uint16_t hindleg_femur_r_bone_index = bone_index++; std::uint16_t hindleg_tibia_l_bone_index = bone_index++; std::uint16_t hindleg_tibia_r_bone_index = bone_index++; std::uint16_t hindleg_tarsus_l_bone_index = bone_index++; std::uint16_t hindleg_tarsus_r_bone_index = bone_index++; std::uint16_t head_bone_index = bone_index++; std::uint16_t mandible_l_bone_index = bone_index++; std::uint16_t mandible_r_bone_index = bone_index++; std::uint16_t scape_l_bone_index = bone_index++; std::uint16_t scape_r_bone_index = bone_index++; std::uint16_t pedicel_l_bone_index = bone_index++; std::uint16_t pedicel_r_bone_index = bone_index++; std::uint16_t petiole_bone_index = bone_index++; std::uint16_t postpetiole_bone_index = (postpetiole) ? bone_index++ : static_cast(bone_count); std::uint16_t gaster_bone_index = bone_index++; std::uint16_t sting_bone_index = (sting) ? bone_index++ : static_cast(bone_count); // Get references and pointers to bones render::bone& mesosoma_bone = skeleton.bones[mesosoma_bone_index]; render::bone& foreleg_coxa_l_bone = skeleton.bones[foreleg_coxa_l_bone_index]; render::bone& foreleg_coxa_r_bone = skeleton.bones[foreleg_coxa_r_bone_index]; render::bone& foreleg_femur_l_bone = skeleton.bones[foreleg_femur_l_bone_index]; render::bone& foreleg_femur_r_bone = skeleton.bones[foreleg_femur_r_bone_index]; render::bone& foreleg_tibia_l_bone = skeleton.bones[foreleg_tibia_l_bone_index]; render::bone& foreleg_tibia_r_bone = skeleton.bones[foreleg_tibia_r_bone_index]; render::bone& foreleg_tarsus_l_bone = skeleton.bones[foreleg_tarsus_l_bone_index]; render::bone& foreleg_tarsus_r_bone = skeleton.bones[foreleg_tarsus_r_bone_index]; render::bone& midleg_coxa_l_bone = skeleton.bones[midleg_coxa_l_bone_index]; render::bone& midleg_coxa_r_bone = skeleton.bones[midleg_coxa_r_bone_index]; render::bone& midleg_femur_l_bone = skeleton.bones[midleg_femur_l_bone_index]; render::bone& midleg_femur_r_bone = skeleton.bones[midleg_femur_r_bone_index]; render::bone& midleg_tibia_l_bone = skeleton.bones[midleg_tibia_l_bone_index]; render::bone& midleg_tibia_r_bone = skeleton.bones[midleg_tibia_r_bone_index]; render::bone& midleg_tarsus_l_bone = skeleton.bones[midleg_tarsus_l_bone_index]; render::bone& midleg_tarsus_r_bone = skeleton.bones[midleg_tarsus_r_bone_index]; render::bone& hindleg_coxa_l_bone = skeleton.bones[hindleg_coxa_l_bone_index]; render::bone& hindleg_coxa_r_bone = skeleton.bones[hindleg_coxa_r_bone_index]; render::bone& hindleg_femur_l_bone = skeleton.bones[hindleg_femur_l_bone_index]; render::bone& hindleg_femur_r_bone = skeleton.bones[hindleg_femur_r_bone_index]; render::bone& hindleg_tibia_l_bone = skeleton.bones[hindleg_tibia_l_bone_index]; render::bone& hindleg_tibia_r_bone = skeleton.bones[hindleg_tibia_r_bone_index]; render::bone& hindleg_tarsus_l_bone = skeleton.bones[hindleg_tarsus_l_bone_index]; render::bone& hindleg_tarsus_r_bone = skeleton.bones[hindleg_tarsus_r_bone_index]; render::bone& head_bone = skeleton.bones[head_bone_index]; render::bone& mandible_l_bone = skeleton.bones[mandible_l_bone_index]; render::bone& mandible_r_bone = skeleton.bones[mandible_r_bone_index]; render::bone& scape_l_bone = skeleton.bones[scape_l_bone_index]; render::bone& scape_r_bone = skeleton.bones[scape_r_bone_index]; render::bone& pedicel_l_bone = skeleton.bones[pedicel_l_bone_index]; render::bone& pedicel_r_bone = skeleton.bones[pedicel_r_bone_index]; render::bone& petiole_bone = skeleton.bones[petiole_bone_index]; render::bone* postpetiole_bone = (postpetiole) ? &skeleton.bones[postpetiole_bone_index] : nullptr; render::bone& gaster_bone = skeleton.bones[gaster_bone_index]; render::bone* sting_bone = (sting) ? &skeleton.bones[sting_bone_index] : nullptr; // Skeleton mesosoma if (auto it = mesosoma_skeleton.bone_map.find("mesosoma"); it != mesosoma_skeleton.bone_map.end()) mesosoma_bone = mesosoma_skeleton.bones[it->second]; mesosoma_bone.parent = nullptr; // Skeleton forelegs if (auto it = legs_skeleton.bone_map.find("foreleg_coxa_l"); it != legs_skeleton.bone_map.end()) foreleg_coxa_l_bone = legs_skeleton.bones[it->second]; foreleg_coxa_l_bone.parent = &mesosoma_bone; if (auto it = legs_skeleton.bone_map.find("foreleg_coxa_r"); it != legs_skeleton.bone_map.end()) foreleg_coxa_r_bone = legs_skeleton.bones[it->second]; foreleg_coxa_r_bone.parent = &mesosoma_bone; if (auto it = legs_skeleton.bone_map.find("foreleg_femur_l"); it != legs_skeleton.bone_map.end()) foreleg_femur_l_bone = legs_skeleton.bones[it->second]; foreleg_femur_l_bone.parent = &foreleg_coxa_l_bone; if (auto it = legs_skeleton.bone_map.find("foreleg_femur_r"); it != legs_skeleton.bone_map.end()) foreleg_femur_r_bone = legs_skeleton.bones[it->second]; foreleg_femur_r_bone.parent = &foreleg_coxa_r_bone; if (auto it = legs_skeleton.bone_map.find("foreleg_tibia_l"); it != legs_skeleton.bone_map.end()) foreleg_tibia_l_bone = legs_skeleton.bones[it->second]; foreleg_tibia_l_bone.parent = &foreleg_femur_l_bone; if (auto it = legs_skeleton.bone_map.find("foreleg_tibia_r"); it != legs_skeleton.bone_map.end()) foreleg_tibia_r_bone = legs_skeleton.bones[it->second]; foreleg_tibia_r_bone.parent = &foreleg_femur_r_bone; if (auto it = legs_skeleton.bone_map.find("foreleg_tarsus_l"); it != legs_skeleton.bone_map.end()) foreleg_tarsus_l_bone = legs_skeleton.bones[it->second]; foreleg_tarsus_l_bone.parent = &foreleg_tibia_l_bone; if (auto it = legs_skeleton.bone_map.find("foreleg_tarsus_r"); it != legs_skeleton.bone_map.end()) foreleg_tarsus_r_bone = legs_skeleton.bones[it->second]; foreleg_tarsus_r_bone.parent = &foreleg_tibia_r_bone; // Skeleton midlegs if (auto it = legs_skeleton.bone_map.find("midleg_coxa_l"); it != legs_skeleton.bone_map.end()) midleg_coxa_l_bone = legs_skeleton.bones[it->second]; midleg_coxa_l_bone.parent = &mesosoma_bone; if (auto it = legs_skeleton.bone_map.find("midleg_coxa_r"); it != legs_skeleton.bone_map.end()) midleg_coxa_r_bone = legs_skeleton.bones[it->second]; midleg_coxa_r_bone.parent = &mesosoma_bone; if (auto it = legs_skeleton.bone_map.find("midleg_femur_l"); it != legs_skeleton.bone_map.end()) midleg_femur_l_bone = legs_skeleton.bones[it->second]; midleg_femur_l_bone.parent = &midleg_coxa_l_bone; if (auto it = legs_skeleton.bone_map.find("midleg_femur_r"); it != legs_skeleton.bone_map.end()) midleg_femur_r_bone = legs_skeleton.bones[it->second]; midleg_femur_r_bone.parent = &midleg_coxa_r_bone; if (auto it = legs_skeleton.bone_map.find("midleg_tibia_l"); it != legs_skeleton.bone_map.end()) midleg_tibia_l_bone = legs_skeleton.bones[it->second]; midleg_tibia_l_bone.parent = &midleg_femur_l_bone; if (auto it = legs_skeleton.bone_map.find("midleg_tibia_r"); it != legs_skeleton.bone_map.end()) midleg_tibia_r_bone = legs_skeleton.bones[it->second]; midleg_tibia_r_bone.parent = &midleg_femur_r_bone; if (auto it = legs_skeleton.bone_map.find("midleg_tarsus_l"); it != legs_skeleton.bone_map.end()) midleg_tarsus_l_bone = legs_skeleton.bones[it->second]; midleg_tarsus_l_bone.parent = &midleg_tibia_l_bone; if (auto it = legs_skeleton.bone_map.find("midleg_tarsus_r"); it != legs_skeleton.bone_map.end()) midleg_tarsus_r_bone = legs_skeleton.bones[it->second]; midleg_tarsus_r_bone.parent = &midleg_tibia_r_bone; // Skeleton hindlegs if (auto it = legs_skeleton.bone_map.find("hindleg_coxa_l"); it != legs_skeleton.bone_map.end()) hindleg_coxa_l_bone = legs_skeleton.bones[it->second]; hindleg_coxa_l_bone.parent = &mesosoma_bone; if (auto it = legs_skeleton.bone_map.find("hindleg_coxa_r"); it != legs_skeleton.bone_map.end()) hindleg_coxa_r_bone = legs_skeleton.bones[it->second]; hindleg_coxa_r_bone.parent = &mesosoma_bone; if (auto it = legs_skeleton.bone_map.find("hindleg_femur_l"); it != legs_skeleton.bone_map.end()) hindleg_femur_l_bone = legs_skeleton.bones[it->second]; hindleg_femur_l_bone.parent = &hindleg_coxa_l_bone; if (auto it = legs_skeleton.bone_map.find("hindleg_femur_r"); it != legs_skeleton.bone_map.end()) hindleg_femur_r_bone = legs_skeleton.bones[it->second]; hindleg_femur_r_bone.parent = &hindleg_coxa_r_bone; if (auto it = legs_skeleton.bone_map.find("hindleg_tibia_l"); it != legs_skeleton.bone_map.end()) hindleg_tibia_l_bone = legs_skeleton.bones[it->second]; hindleg_tibia_l_bone.parent = &hindleg_femur_l_bone; if (auto it = legs_skeleton.bone_map.find("hindleg_tibia_r"); it != legs_skeleton.bone_map.end()) hindleg_tibia_r_bone = legs_skeleton.bones[it->second]; hindleg_tibia_r_bone.parent = &hindleg_femur_r_bone; if (auto it = legs_skeleton.bone_map.find("hindleg_tarsus_l"); it != legs_skeleton.bone_map.end()) hindleg_tarsus_l_bone = legs_skeleton.bones[it->second]; hindleg_tarsus_l_bone.parent = &hindleg_tibia_l_bone; if (auto it = legs_skeleton.bone_map.find("hindleg_tarsus_r"); it != legs_skeleton.bone_map.end()) hindleg_tarsus_r_bone = legs_skeleton.bones[it->second]; hindleg_tarsus_r_bone.parent = &hindleg_tibia_r_bone; // Skeleton head if (auto it = head_skeleton.bone_map.find("head"); it != head_skeleton.bone_map.end()) head_bone = head_skeleton.bones[it->second]; head_bone.parent = &mesosoma_bone; // Skeleton mandibles if (auto it = mandibles_skeleton.bone_map.find("mandible_l"); it != mandibles_skeleton.bone_map.end()) mandible_l_bone = mandibles_skeleton.bones[it->second]; mandible_l_bone.parent = &head_bone; if (auto it = mandibles_skeleton.bone_map.find("mandible_r"); it != mandibles_skeleton.bone_map.end()) mandible_r_bone = mandibles_skeleton.bones[it->second]; mandible_r_bone.parent = &head_bone; // Skeleton antennae if (auto it = antennae_skeleton.bone_map.find("scape_l"); it != antennae_skeleton.bone_map.end()) scape_l_bone = antennae_skeleton.bones[it->second]; scape_l_bone.parent = &head_bone; if (auto it = antennae_skeleton.bone_map.find("scape_r"); it != antennae_skeleton.bone_map.end()) scape_r_bone = antennae_skeleton.bones[it->second]; scape_r_bone.parent = &head_bone; if (auto it = antennae_skeleton.bone_map.find("pedicel_l"); it != antennae_skeleton.bone_map.end()) pedicel_l_bone = antennae_skeleton.bones[it->second]; pedicel_l_bone.parent = &scape_l_bone; if (auto it = antennae_skeleton.bone_map.find("pedicel_r"); it != antennae_skeleton.bone_map.end()) pedicel_r_bone = antennae_skeleton.bones[it->second]; pedicel_r_bone.parent = &scape_r_bone; // Skeleton waist if (auto it = waist_skeleton.bone_map.find("petiole"); it != waist_skeleton.bone_map.end()) petiole_bone = waist_skeleton.bones[it->second]; petiole_bone.parent = &mesosoma_bone; if (postpetiole) { if (auto it = waist_skeleton.bone_map.find("postpetiole"); it != waist_skeleton.bone_map.end()) *postpetiole_bone = waist_skeleton.bones[it->second]; postpetiole_bone->parent = &petiole_bone; } // Skeleton gaster if (auto it = gaster_skeleton.bone_map.find("gaster"); it != gaster_skeleton.bone_map.end()) gaster_bone = gaster_skeleton.bones[it->second]; gaster_bone.parent = (postpetiole) ? postpetiole_bone : &petiole_bone; // Skeleton sting if (sting) { if (auto it = sting_skeleton->bone_map.find("sting"); it != sting_skeleton->bone_map.end()) *sting_bone = sting_skeleton->bones[it->second]; sting_bone->parent = &gaster_bone; } // Get number of vertex indices for each body part std::size_t mesosoma_index_count = (*mesosoma->get_groups())[0]->get_index_count(); std::size_t legs_index_count = (*legs->get_groups())[0]->get_index_count(); std::size_t head_index_count = (*head->get_groups())[0]->get_index_count(); std::size_t mandibles_index_count = (*mandibles->get_groups())[0]->get_index_count(); std::size_t antennae_index_count = (*antennae->get_groups())[0]->get_index_count(); std::size_t waist_index_count = (*waist->get_groups())[0]->get_index_count(); std::size_t gaster_index_count = (*gaster->get_groups())[0]->get_index_count(); std::size_t sting_index_count = (sting) ? (*sting->get_groups())[0]->get_index_count() : 0; std::size_t eyes_index_count = (eyes) ? (*eyes->get_groups())[0]->get_index_count() : 0; std::size_t lateral_ocelli_index_count = (lateral_ocelli) ? (*lateral_ocelli->get_groups())[0]->get_index_count() : 0; std::size_t median_ocellus_index_count = (median_ocellus) ? (*median_ocellus->get_groups())[0]->get_index_count() : 0; std::size_t forewings_index_count = (forewings) ? (*forewings->get_groups())[0]->get_index_count() : 0; std::size_t hindwings_index_count = (hindwings) ? (*hindwings->get_groups())[0]->get_index_count() : 0; std::size_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 head space to body space math::transform head_to_body; if (auto it = mesosoma_skeleton.bone_map.find("head"); it != mesosoma_skeleton.bone_map.end()) head_to_body = mesosoma_skeleton.concatenate(it->second); // 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 + 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 mandible space to body space math::transform mandible_l_to_body; if (auto it = head_skeleton.bone_map.find("mandible_l"); it != head_skeleton.bone_map.end()) mandible_l_to_body = head_to_body * head_skeleton.concatenate(it->second); math::transform mandible_r_to_body; if (auto it = head_skeleton.bone_map.find("mandible_r"); it != head_skeleton.bone_map.end()) mandible_r_to_body = head_to_body * head_skeleton.concatenate(it->second); // 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 + 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 + 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; if (auto it = head_skeleton.bone_map.find("scape_l"); it != head_skeleton.bone_map.end()) antenna_l_to_body = head_to_body * head_skeleton.concatenate(it->second); math::transform antenna_r_to_body; if (auto it = head_skeleton.bone_map.find("scape_r"); it != head_skeleton.bone_map.end()) antenna_r_to_body = head_to_body * head_skeleton.concatenate(it->second); // Reskin scape bones std::unordered_set old_scape_l_indices; if (auto it = antennae_skeleton.bone_map.find("scape_l"); it != antennae_skeleton.bone_map.end()) old_scape_l_indices.emplace(it->second); reskin_vertices(vertex_buffer_data + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_scape_l_indices, scape_l_bone_index, antenna_l_to_body); std::unordered_set old_scape_r_indices; if (auto it = antennae_skeleton.bone_map.find("scape_r"); it != antennae_skeleton.bone_map.end()) old_scape_r_indices.emplace(it->second); reskin_vertices(vertex_buffer_data + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_scape_r_indices, scape_r_bone_index, antenna_r_to_body); // Reskin pedicel bones const std::vector pedicel_bone_names = { "pedicel", "flagellomere_1", "flagellomere_2", "flagellomere_3", "flagellomere_4", "flagellomere_5", "flagellomere_6", "flagellomere_7", "flagellomere_8", "flagellomere_9", "flagellomere_10", "flagellomere_11", "flagellomere_12" }; std::unordered_set old_pedicel_l_indices; for (const std::string& bone_name: pedicel_bone_names) if (auto it = antennae_skeleton.bone_map.find(bone_name + "_l"); it != antennae_skeleton.bone_map.end()) old_pedicel_l_indices.emplace(it->second); reskin_vertices(vertex_buffer_data + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_pedicel_l_indices, pedicel_l_bone_index, antenna_l_to_body); std::unordered_set old_pedicel_r_indices; for (const std::string& bone_name: pedicel_bone_names) if (auto it = antennae_skeleton.bone_map.find(bone_name + "_r"); it != antennae_skeleton.bone_map.end()) old_pedicel_r_indices.emplace(it->second); reskin_vertices(vertex_buffer_data + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_pedicel_r_indices, pedicel_r_bone_index, antenna_r_to_body); // Calculate transform from waist space to body space math::transform waist_to_body; if (auto it = mesosoma_skeleton.bone_map.find("petiole"); it != mesosoma_skeleton.bone_map.end()) waist_to_body = mesosoma_skeleton.concatenate(it->second); // 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 + 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) { 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 + 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; if (auto it = waist_skeleton.bone_map.find("gaster"); it != waist_skeleton.bone_map.end()) gaster_to_body = waist_to_body * waist_skeleton.concatenate(it->second); // 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 + 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) { // Calculate transform from sting space to body space math::transform sting_to_body; if (auto it = gaster_skeleton.bone_map.find("sting"); it != gaster_skeleton.bone_map.end()) sting_to_body = gaster_to_body * gaster_skeleton.concatenate(it->second); // 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 + 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); } if (eyes) { // Calculate transforms from eyes space to body space math::transform eye_l_to_body; if (auto it = head_skeleton.bone_map.find("eye_l"); it != head_skeleton.bone_map.end()) eye_l_to_body = head_to_body * head_skeleton.concatenate(it->second); math::transform eye_r_to_body; if (auto it = head_skeleton.bone_map.find("eye_r"); it != head_skeleton.bone_map.end()) eye_r_to_body = head_to_body * head_skeleton.concatenate(it->second); // 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 + 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 + 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) { // Calculate transforms from lateral ocelli space to body space math::transform ocellus_l_to_body; if (auto it = head_skeleton.bone_map.find("ocellus_l"); it != head_skeleton.bone_map.end()) ocellus_l_to_body = head_to_body * head_skeleton.concatenate(it->second); math::transform ocellus_r_to_body; if (auto it = head_skeleton.bone_map.find("ocellus_r"); it != head_skeleton.bone_map.end()) ocellus_r_to_body = head_to_body * head_skeleton.concatenate(it->second); // 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 + 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 + 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); } if (median_ocellus) { // Calculate transforms from lateral ocelli space to body space math::transform ocellus_m_to_body; if (auto it = head_skeleton.bone_map.find("ocellus_m"); it != head_skeleton.bone_map.end()) ocellus_m_to_body = head_to_body * head_skeleton.concatenate(it->second); // 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 + 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); } // Upload vertex buffer data to model VBO model->get_vertex_buffer()->repurpose(gl::buffer_usage::static_draw, vertex_buffer_size, vertex_buffer_data); // Construct exoskeleton model group render::model_group* exoskeleton_group = model->add_group("exoskeleton"); exoskeleton_group->set_material(exoskeleton_material); exoskeleton_group->set_drawing_mode(gl::drawing_mode::triangles); exoskeleton_group->set_start_index(0); exoskeleton_group->set_index_count(exoskeleton_index_count); std::size_t index_offset = exoskeleton_index_count; if (eyes) { // Construct eyes model group render::model_group* eyes_group = model->add_group("eyes"); eyes_group->set_material((*eyes->get_groups())[0]->get_material()); eyes_group->set_drawing_mode(gl::drawing_mode::triangles); eyes_group->set_start_index(index_offset); eyes_group->set_index_count(eyes_index_count); index_offset += eyes_index_count; } if (lateral_ocelli || median_ocellus) { // Construct ocelli model group render::model_group* ocelli_group = model->add_group("ocelli"); ocelli_group->set_drawing_mode(gl::drawing_mode::triangles); ocelli_group->set_start_index(index_offset); std::size_t index_count = 0; if (lateral_ocelli) { index_count += lateral_ocelli_index_count; index_offset += lateral_ocelli_index_count; ocelli_group->set_material((*lateral_ocelli->get_groups())[0]->get_material()); } if (median_ocellus) { index_count += median_ocellus_index_count; index_offset += median_ocellus_index_count; if (!lateral_ocelli) ocelli_group->set_material((*median_ocellus->get_groups())[0]->get_material()); } ocelli_group->set_index_count(index_count); } if (forewings) { // Construct forewings model group render::model_group* forewings_group = model->add_group("forewings"); forewings_group->set_material((*forewings->get_groups())[0]->get_material()); forewings_group->set_drawing_mode(gl::drawing_mode::triangles); forewings_group->set_start_index(index_offset); forewings_group->set_index_count(forewings_index_count); index_offset += forewings_index_count; } if (hindwings) { // Construct hindwings model group render::model_group* hindwings_group = model->add_group("hindwings"); hindwings_group->set_material((*hindwings->get_groups())[0]->get_material()); hindwings_group->set_drawing_mode(gl::drawing_mode::triangles); hindwings_group->set_start_index(index_offset); hindwings_group->set_index_count(hindwings_index_count); index_offset += hindwings_index_count; } // Calculate model bounding box geom::aabb bounds = calculate_bounds(vertex_buffer_data, index_offset, *position_attribute); model->set_bounds(bounds); // Free vertex buffer data delete[] vertex_buffer_data; return model; } void reskin_vertices ( std::uint8_t* 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::uint16_t new_bone_index, const math::transform& transform ) { std::uint8_t* position_data = vertex_data + position_attribute.offset; std::uint8_t* normal_data = vertex_data + normal_attribute.offset; std::uint8_t* tangent_data = vertex_data + tangent_attribute.offset; std::uint8_t* 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(std::uint8_t* vertex_data, std::size_t index_count, const gl::vertex_attribute& position_attribute) { std::uint8_t* 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 float* x = reinterpret_cast(position_data + position_attribute.stride * i); float* y = x + 1; 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; } } // namespace ant } // namespace game