Browse Source

Add skeleton and bone classes. Add support for procedurally building ant models from individual body parts

master
C. J. Howard 1 year ago
parent
commit
bd9e335a4f
12 changed files with 969 additions and 397 deletions
  1. +0
    -1
      CMakeLists.txt
  2. +730
    -32
      src/game/ant/morphogenesis.cpp
  3. +5
    -17
      src/game/ant/trait/loader/pigmentation-loader.cpp
  4. +3
    -6
      src/game/ant/trait/pigmentation.hpp
  5. +7
    -12
      src/game/state/nuptial-flight.cpp
  6. +2
    -3
      src/math/quaternion-functions.hpp
  7. +2
    -2
      src/math/transform-functions.hpp
  8. +44
    -0
      src/render/bone.hpp
  9. +16
    -5
      src/render/model.hpp
  10. +39
    -0
      src/render/skeleton.cpp
  11. +53
    -0
      src/render/skeleton.hpp
  12. +68
    -319
      src/resources/model-loader.cpp

+ 0
- 1
CMakeLists.txt View File

@ -1,6 +1,5 @@
cmake_minimum_required(VERSION 3.7)
option(VERSION_STRING "Project version string" "0.0.0")
project(antkeeper VERSION ${VERSION_STRING} LANGUAGES CXX)

+ 730
- 32
src/game/ant/morphogenesis.cpp View File

@ -19,6 +19,9 @@
#include "game/ant/morphogenesis.hpp"
#include "render/material.hpp"
#include "render/vertex-attribute.hpp"
#include <unordered_set>
#include <iostream>
namespace game {
namespace ant {
@ -27,22 +30,39 @@ 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_material(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<std::uint16_t>& old_bone_indices,
std::uint16_t new_bone_index,
const math::transform<float>& transform
);
static geom::aabb<float> 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,
render::model* antennae,
render::model* eyes,
render::model* forewings,
render::model* gaster,
render::model* head,
render::model* hindwings,
render::model* legs,
render::model* mandibles,
render::model* mesosoma,
render::model* ocelli,
render::model* sting,
render::model* waist
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* ocelli,
const render::model* sting,
const render::model* waist
);
render::model* morphogenesis(const ant::breed& breed, ant::caste caste)
@ -72,8 +92,8 @@ render::model* generate_worker(const ant::breed& breed)
// Get material parameters
// Build material
render::material* material = build_material(breed);
// 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;
@ -89,7 +109,7 @@ render::model* generate_worker(const ant::breed& breed)
// Build worker model
render::model* model = build_model
(
material,
exoskeleton_material,
antennae_model,
eyes_model,
nullptr,
@ -118,31 +138,709 @@ render::model* generate_male(const ant::breed& breed)
return nullptr;
}
render::material* build_material(const ant::breed& breed)
render::material* build_exoskeleton_material
(
const ant::trait::pigmentation& pigmentation,
const ant::trait::sculpturing& sculpturing
)
{
return nullptr;
// 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<render::material_property<float>*>(property)->set_value(sculpturing.roughness);
}
else
{
exoskeleton_material->add_property<float>("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<render::material_property<const gl::texture_2d*>*>(property)->set_value(sculpturing.normal_map);
}
else
{
exoskeleton_material->add_property<const gl::texture_2d*>("normal_map")->set_value(sculpturing.normal_map);
}
return exoskeleton_material;
}
render::model* build_model
(
render::material* material,
render::model* antennae,
render::model* eyes,
render::model* forewings,
render::model* gaster,
render::model* head,
render::model* hindwings,
render::model* legs,
render::model* mandibles,
render::model* mesosoma,
render::model* ocelli,
render::model* sting,
render::model* waist
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* ocelli,
const render::model* sting,
const render::model* waist
)
{
return nullptr;
// 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* ocelli_vbo = (ocelli) ? ocelli->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 ocelli_vbo_offset = vertex_buffer_size;
if (ocelli)
vertex_buffer_size += ocelli_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 (ocelli)
ocelli_vbo->read(0, ocelli_vbo->get_size(), vertex_buffer_data + ocelli_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* ocelli_skeleton = (ocelli) ? &ocelli->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<std::uint16_t>(bone_count);
std::uint16_t gaster_bone_index = bone_index++;
std::uint16_t sting_bone_index = (sting) ? bone_index++ : static_cast<std::uint16_t>(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 ocelli_index_count = (ocelli) ? (*ocelli->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<float> 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<std::uint16_t> 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<float> 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<float> 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<std::uint16_t> 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<std::uint16_t> 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<float> 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<float> 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<std::uint16_t> 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<std::uint16_t> 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<std::string> 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<std::uint16_t> 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<std::uint16_t> 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<float> 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<std::uint16_t> 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<std::uint16_t> 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<float> 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<std::uint16_t> 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<float> 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<std::uint16_t> 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<float> 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<float> 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<std::uint16_t> 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<std::uint16_t> 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);
}
// 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 (ocelli)
{
// Construct ocelli model group
render::model_group* ocelli_group = model->add_group("ocelli");
ocelli_group->set_material((*ocelli->get_groups())[0]->get_material());
ocelli_group->set_drawing_mode(gl::drawing_mode::triangles);
ocelli_group->set_start_index(index_offset);
ocelli_group->set_index_count(ocelli_index_count);
index_offset += ocelli_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<float> 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<std::uint16_t>& old_bone_indices,
std::uint16_t new_bone_index,
const math::transform<float>& 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<float*>(bone_index_data + bone_index_attribute.stride * i);
// Skip irrelevant bones
if (!old_bone_indices.count(static_cast<std::uint16_t>(*bone_index + 0.5f)))
continue;
// Get vertex position
float* x = reinterpret_cast<float*>(position_data + position_attribute.stride * i);
float* y = x + 1;
float* z = y + 1;
// Get vertex normal
float* nx = reinterpret_cast<float*>(normal_data + normal_attribute.stride * i);
float* ny = nx + 1;
float* nz = ny + 1;
// Get vertex tangent
float* tx = reinterpret_cast<float*>(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<float>(new_bone_index);
}
}
geom::aabb<float> 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<float> bounds;
bounds.min_point.x = std::numeric_limits<float>::infinity();
bounds.min_point.y = std::numeric_limits<float>::infinity();
bounds.min_point.z = std::numeric_limits<float>::infinity();
bounds.max_point.x = -std::numeric_limits<float>::infinity();
bounds.max_point.y = -std::numeric_limits<float>::infinity();
bounds.max_point.z = -std::numeric_limits<float>::infinity();
for (std::size_t i = 0; i < index_count; ++i)
{
// Get vertex position
float* x = reinterpret_cast<float*>(position_data + position_attribute.stride * i);
float* y = x + 1;
float* z = y + 1;
bounds.min_point.x = std::min<float>(*x, bounds.min_point.x);
bounds.min_point.y = std::min<float>(*y, bounds.min_point.y);
bounds.min_point.z = std::min<float>(*z, bounds.min_point.z);
bounds.max_point.x = std::max<float>(*x, bounds.max_point.x);
bounds.max_point.y = std::max<float>(*y, bounds.max_point.y);
bounds.max_point.z = std::max<float>(*z, bounds.max_point.z);
}
return bounds;
}
} // namespace ant
} // namespace game

+ 5
- 17
src/game/ant/trait/loader/pigmentation-loader.cpp View File

@ -39,23 +39,11 @@ trait::pigmentation* resource_loader::load(resource_manager
// Allocate pigmentation trait
trait::pigmentation* pigmentation = new trait::pigmentation();
// Parse pigmentation primary albedo
pigmentation->primary_albedo = {0.0, 0.0, 0.0};
if (auto primary_albedo_element = pigmentation_element->find("primary_albedo"); primary_albedo_element != pigmentation_element->end())
{
pigmentation->primary_albedo.x = (*primary_albedo_element)[0].get<float>();
pigmentation->primary_albedo.y = (*primary_albedo_element)[1].get<float>();
pigmentation->primary_albedo.z = (*primary_albedo_element)[2].get<float>();
}
// Parse pigmentation secondary albedo
pigmentation->secondary_albedo = {0.0, 0.0, 0.0};
if (auto secondary_albedo_element = pigmentation_element->find("secondary_albedo"); secondary_albedo_element != pigmentation_element->end())
{
pigmentation->secondary_albedo.x = (*secondary_albedo_element)[0].get<float>();
pigmentation->secondary_albedo.y = (*secondary_albedo_element)[1].get<float>();
pigmentation->secondary_albedo.z = (*secondary_albedo_element)[2].get<float>();
}
// Load pigmentation material
auto material_element = pigmentation_element->find("material");
if (material_element == pigmentation_element->end())
throw std::runtime_error("Pigmentation trait doesn't specify pigmentation material.");
pigmentation->material = resource_manager->load<render::material>(material_element->get<std::string>());
// Free JSON data
delete data;

+ 3
- 6
src/game/ant/trait/pigmentation.hpp View File

@ -20,7 +20,7 @@
#ifndef ANTKEEPER_GAME_ANT_TRAIT_PIGMENTATION_HPP
#define ANTKEEPER_GAME_ANT_TRAIT_PIGMENTATION_HPP
#include "utility/fundamental-types.hpp"
#include "render/material.hpp"
namespace game {
namespace ant {
@ -31,11 +31,8 @@ namespace trait {
*/
struct pigmentation
{
/// Primary albedo color.
float3 primary_albedo;
/// Secondary albedo color.
float3 secondary_albedo;
/// Pigmentation material
render::material* material;
};
} // namespace trait

+ 7
- 12
src/game/state/nuptial-flight.cpp View File

@ -58,18 +58,18 @@ nuptial_flight::nuptial_flight(game::context& ctx):
ant::breed breed;
// Load morphological traits
breed.head = ctx.resource_manager->load<ant::trait::head>("collared-harvester-head.dna");
breed.head = ctx.resource_manager->load<ant::trait::head>("square-harvester-head.dna");
breed.mandibles = ctx.resource_manager->load<ant::trait::mandibles>("harvester-mandibles.dna");
breed.antennae = ctx.resource_manager->load<ant::trait::antennae>("slender-antennae.dna");
breed.eyes = ctx.resource_manager->load<ant::trait::eyes>("vestigial-eyes.dna");
breed.eyes = ctx.resource_manager->load<ant::trait::eyes>("oval-eyes.dna");
breed.mesosoma = ctx.resource_manager->load<ant::trait::mesosoma>("humpback-mesosoma.dna");
breed.legs = ctx.resource_manager->load<ant::trait::legs>("trekking-legs.dna");
breed.waist = ctx.resource_manager->load<ant::trait::waist>("harvester-waist.dna");
breed.gaster = ctx.resource_manager->load<ant::trait::gaster>("ovoid-gaster.dna");
breed.ocelli = ctx.resource_manager->load<ant::trait::ocelli>("absent-ocelli.dna");
breed.sting = ctx.resource_manager->load<ant::trait::sting>("bullet-sting.dna");
breed.sting = ctx.resource_manager->load<ant::trait::sting>("sting-absent.dna");
breed.sculpturing = ctx.resource_manager->load<ant::trait::sculpturing>("politus-sculpturing.dna");
breed.pigmentation = ctx.resource_manager->load<ant::trait::pigmentation>("onyx-pigmentation.dna");
breed.pigmentation = ctx.resource_manager->load<ant::trait::pigmentation>("rust-pigmentation.dna");
breed.egg = ctx.resource_manager->load<ant::trait::egg>("ellipsoid-egg.dna");
breed.larva = ctx.resource_manager->load<ant::trait::larva>("old-larva.dna");
breed.cocoon = ctx.resource_manager->load<ant::trait::cocoon>("cocoon-present.dna");
@ -124,13 +124,6 @@ nuptial_flight::nuptial_flight(game::context& ctx):
entity::command::warp_to(*ctx.entity_registry, ruler_10cm_eid, {0, 0, 10});
}
// Create ant_test
{
entity::archetype* ant_test_10cm_archetype = ctx.resource_manager->load<entity::archetype>("ant-test.ent");
auto ant_test_eid = ant_test_10cm_archetype->create(*ctx.entity_registry);
entity::command::warp_to(*ctx.entity_registry, ant_test_eid, {10, 0, 0});
}
// Create keeper if not yet created
if (ctx.entities.find("keeper") == ctx.entities.end())
{
@ -144,7 +137,7 @@ nuptial_flight::nuptial_flight(game::context& ctx):
auto boid_eid = ctx.entity_registry->create();
entity::component::model model;
model.render_model = ctx.resource_manager->load<render::model>("ant-test.mdl");
model.render_model = worker_model;//ctx.resource_manager->load<render::model>("ant-test.mdl");
model.instance_count = 0;
model.layers = 1;
ctx.entity_registry->assign<entity::component::model>(boid_eid, model);
@ -158,6 +151,8 @@ nuptial_flight::nuptial_flight(game::context& ctx):
entity::component::locomotion locomotion;
locomotion.yaw = 0.0f;
ctx.entity_registry->assign<entity::component::locomotion>(boid_eid, locomotion);
entity::command::warp_to(*ctx.entity_registry, boid_eid, {0, 2, 0});
// Set target ant
ctx.entities["ant"] = boid_eid;

+ 2
- 3
src/math/quaternion-functions.hpp View File

@ -336,9 +336,8 @@ inline quaternion mul(const quaternion& q, T s)
template <class T>
vector<T, 3> mul(const quaternion<T>& q, const vector<T, 3>& v)
{
const T r = q.w; // Real part
const vector<T, 3>& i = reinterpret_cast<const vector<T, 3>&>(q.x); // Imaginary part
return i * dot(i, v) * T(2) + v * (r * r - dot(i, i)) + cross(i, v) * r * T(2);
const vector<T, 3>& i = reinterpret_cast<const vector<T, 3>&>(q.x);
return add(add(mul(i, dot(i, v) * T(2)), mul(v, (q.w * q.w - dot(i, i)))), mul(cross(i, v), q.w * T(2)));
}
template <class T>

+ 2
- 2
src/math/transform-functions.hpp View File

@ -89,14 +89,14 @@ transform mul(const transform& x, const transform& y)
{
mul(x, y.translation),
normalize(mul(x.rotation, y.rotation)),
x.scale * y.scale
mul(x.scale, y.scale)
};
}
template <class T>
vector<T, 3> mul(const transform<T>& t, const vector<T, 3>& v)
{
return t.translation + (t.rotation * (v * t.scale));
return add(t.translation, (mul(t.rotation, mul(v, t.scale))));
}
} // namespace math

+ 44
- 0
src/render/bone.hpp View File

@ -0,0 +1,44 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_RENDER_BONE_HPP
#define ANTKEEPER_RENDER_BONE_HPP
#include "math/transform-type.hpp"
namespace render {
/**
* Skeletal animation bone.
*/
struct bone
{
/// Pointer to the parent bone.
bone* parent;
/// Local transform, relative to the parent bone.
math::transform<float> transform;
/// Length of the bone.
float length;
};
} // namespace render
#endif // ANTKEEPER_RENDER_BONE_HPP

+ 16
- 5
src/render/model.hpp View File

@ -20,20 +20,18 @@
#ifndef ANTKEEPER_RENDER_MODEL_HPP
#define ANTKEEPER_RENDER_MODEL_HPP
#include "render/skeleton.hpp"
#include "gl/vertex-array.hpp"
#include "gl/vertex-buffer.hpp"
#include "gl/drawing-mode.hpp"
#include "render/material.hpp"
#include "geom/aabb.hpp"
#include <unordered_map>
#include <string>
#include <vector>
class skeleton;
namespace render {
class material;
/**
* Part of a model which is associated with exactly one material.
*/
@ -149,6 +147,9 @@ public:
const gl::vertex_buffer* get_vertex_buffer() const;
gl::vertex_buffer* get_vertex_buffer();
const skeleton& get_skeleton() const;
skeleton& get_skeleton();
private:
aabb_type bounds;
@ -156,7 +157,7 @@ private:
std::unordered_map<std::string, model_group*> group_map;
gl::vertex_array vao;
gl::vertex_buffer vbo;
skeleton* skeleton;
render::skeleton skeleton;
};
inline void model::set_bounds(const aabb_type& bounds)
@ -194,6 +195,16 @@ inline gl::vertex_buffer* model::get_vertex_buffer()
return &vbo;
}
inline const skeleton& model::get_skeleton() const
{
return skeleton;
}
inline skeleton& model::get_skeleton()
{
return skeleton;
}
} // namespace render
#endif // ANTKEEPER_RENDER_MODEL_HPP

+ 39
- 0
src/render/skeleton.cpp View File

@ -0,0 +1,39 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "render/skeleton.hpp"
#include "math/transform-operators.hpp"
namespace render {
math::transform<float> skeleton::concatenate(std::uint16_t index) const
{
const bone* bone = &bones[index];
math::transform<float> transform = bone->transform;
while (bone->parent)
{
transform = bone->parent->transform * transform;
bone = bone->parent;
}
return transform;
}
} // namespace render

+ 53
- 0
src/render/skeleton.hpp View File

@ -0,0 +1,53 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_RENDER_SKELETON_HPP
#define ANTKEEPER_RENDER_SKELETON_HPP
#include "render/bone.hpp"
#include <cstdint>
#include <string>
#include <unordered_map>
#include <vector>
namespace render {
/**
* Skeletal animation skeleton.
*/
struct skeleton
{
/// Collection of bones.
std::vector<bone> bones;
/// Maps bone names to bone indices.
std::unordered_map<std::string, std::uint16_t> bone_map;
/**
* Calculates the global transform of a bone.
*
* @param index Index of the bone.
* @return Global transform of the bone.
*/
math::transform<float> concatenate(std::uint16_t index) const;
};
} // namespace render
#endif // ANTKEEPER_RENDER_SKELETON_HPP

+ 68
- 319
src/resources/model-loader.cpp View File

@ -24,11 +24,11 @@
#include "gl/vertex-attribute.hpp"
#include "gl/drawing-mode.hpp"
#include "utility/fundamental-types.hpp"
#include "math/constants.hpp"
#include <sstream>
#include <stdexcept>
#include <limits>
#include <physfs.h>
#include <iostream>
#include <nlohmann/json.hpp>
static const float3 barycentric_coords[3] =
@ -38,324 +38,6 @@ static const float3 barycentric_coords[3] =
float3{0, 0, 1}
};
/*
template <>
model* resource_loader<model>::load(resource_manager* resource_manager, PHYSFS_File* file)
{
std::string line;
std::vector<float3> positions;
std::vector<float2> uvs;
std::vector<float3> normals;
std::vector<float3> tangents;
std::vector<float3> bitangents;
std::vector<std::vector<std::size_t>> faces;
std::vector<material_group> material_groups;
material_group* current_material_group = nullptr;
aabb<float> bounds =
{
{std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()},
{-std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity(), -std::numeric_limits<float>::infinity()}
};
while (!PHYSFS_eof(file))
{
// Read line
physfs_getline(file, line);
// Tokenize line
std::vector<std::string> tokens;
std::string token;
std::istringstream linestream(line);
while (linestream >> token)
tokens.push_back(token);
// Skip empty lines and comments
if (tokens.empty() || tokens[0][0] == '#')
continue;
if (tokens[0] == "v")
{
if (tokens.size() != 4)
{
std::stringstream stream;
stream << "resource_loader<mesh>::load(): Invalid line \"" << line << "\"" << std::endl;
throw std::runtime_error(stream.str());
}
float3 position;
std::stringstream(tokens[1]) >> position[0];
std::stringstream(tokens[2]) >> position[1];
std::stringstream(tokens[3]) >> position[2];
positions.push_back(position);
// Add position to bounds
for (int i = 0; i < 3; ++i)
{
bounds.min_point[i] = std::min<float>(bounds.min_point[i], position[i]);
bounds.max_point[i] = std::max<float>(bounds.max_point[i], position[i]);
}
}
else if (tokens[0] == "vt")
{
if (tokens.size() != 3)
{
std::stringstream stream;
stream << "resource_loader<mesh>::load(): Invalid line \"" << line << "\"" << std::endl;
throw std::runtime_error(stream.str());
}
float2 uv;
std::stringstream(tokens[1]) >> uv[0];
std::stringstream(tokens[2]) >> uv[1];
uvs.push_back(uv);
}
else if (tokens[0] == "vn")
{
if (tokens.size() != 4)
{
std::stringstream stream;
stream << "resource_loader<mesh>::load(): Invalid line \"" << line << "\"" << std::endl;
throw std::runtime_error(stream.str());
}
float3 normal;
std::stringstream(tokens[1]) >> normal[0];
std::stringstream(tokens[2]) >> normal[1];
std::stringstream(tokens[3]) >> normal[2];
normals.push_back(normal);
}
else if (tokens[0] == "f")
{
if (tokens.size() != 4)
{
std::stringstream stream;
stream << "resource_loader<mesh>::load(): Invalid line \"" << line << "\"" << std::endl;
throw std::runtime_error(stream.str());
}
std::vector<std::size_t> face;
for (std::size_t i = 0; i < 3; ++i)
{
std::stringstream ss(tokens[i + 1]);
while (ss.good())
{
std::string substring;
std::getline(ss, substring, '/');
if (!substring.empty())
{
std::size_t index = std::stoul(substring) - 1;
face.push_back(index);
}
}
}
faces.push_back(face);
}
else if (tokens[0] == "usemtl")
{
if (tokens.size() != 2)
{
std::stringstream stream;
stream << "resource_loader<mesh>::load(): Invalid line \"" << line << "\"" << std::endl;
throw std::runtime_error(stream.str());
}
if (current_material_group)
{
current_material_group->index_count = faces.size() * 3 - current_material_group->start_index;
}
material_groups.push_back(material_group());
current_material_group = &material_groups.back();
current_material_group->name = tokens[1];
current_material_group->start_index = faces.size() * 3;
current_material_group->index_count = 0;
}
}
if (current_material_group)
{
current_material_group->index_count = faces.size() * 3 - current_material_group->start_index;
}
// Load material group materials
for (material_group& material_group: material_groups)
{
material_group.material = resource_manager->load<material>(material_group.name + ".mtl");
}
bool has_uvs = (!uvs.empty());
bool has_normals = (!normals.empty());
bool has_tangents = (has_uvs && has_normals);
bool has_barycentric = false;
has_barycentric = true;
// Calculate faceted tangents and bitangents
if (has_tangents)
{
tangents.resize(positions.size());
bitangents.resize(positions.size());
for (std::size_t i = 0; i < positions.size(); ++i)
{
tangents[i] = {0.0f, 0.0f, 0.0f};
bitangents[i] = {0.0f, 0.0f, 0.0f};
}
for (std::size_t i = 0; i < faces.size(); ++i)
{
const std::vector<std::size_t>& face = faces[i];
std::size_t ia = face[0];
std::size_t ib = face[3];
std::size_t ic = face[6];
const float3& a = positions[ia];
const float3& b = positions[ib];
const float3& c = positions[ic];
const float2& uva = uvs[face[1]];
const float2& uvb = uvs[face[4]];
const float2& uvc = uvs[face[7]];
float3 ba = b - a;
float3 ca = c - a;
float2 uvba = uvb - uva;
float2 uvca = uvc - uva;
float f = 1.0f / (uvba.x * uvca.y - uvca.x * uvba.y);
float3 tangent = (ba * uvca.y - ca * uvba.y) * f;
float3 bitangent = (ba * -uvca.x + ca * uvba.x) * f;
tangents[ia] += tangent;
tangents[ib] += tangent;
tangents[ic] += tangent;
bitangents[ia] += bitangent;
bitangents[ib] += bitangent;
bitangents[ic] += bitangent;
}
}
std::size_t vertex_size = 3;
if (has_uvs)
vertex_size += 2;
if (has_normals)
vertex_size += 3;
if (has_tangents)
vertex_size += 4;
if (has_barycentric)
vertex_size += 3;
std::size_t vertex_stride = sizeof(float) * vertex_size;
// Generate vertex buffer
float* vertex_data = new float[vertex_size * faces.size() * 3];
float* v = &vertex_data[0];
for (std::size_t i = 0; i < faces.size(); ++i)
{
const std::vector<std::size_t>& face = faces[i];
std::size_t k = 0;
for (std::size_t j = 0; j < 3; ++j)
{
const float3& position = positions[face[k++]];
*(v++) = position.x;
*(v++) = position.y;
*(v++) = position.z;
if (has_uvs)
{
const float2& uv = uvs[face[k++]];
*(v++) = uv.x;
*(v++) = uv.y;
}
if (has_normals)
{
const float3& normal = normals[face[k++]];
*(v++) = normal.x;
*(v++) = normal.y;
*(v++) = normal.z;
}
if (has_tangents)
{
const float3& n = normals[face[k - 1]];
const float3& t = tangents[face[k - 3]];
const float3& b = bitangents[face[k - 3]];
// Gram-Schmidt orthogonalize tangent and bitangent
float3 tangent = math::normalize(t - n * dot(n, t));
float bitangent_sign = (math::dot(math::cross(n, t), b) < 0.0f) ? -1.0f : 1.0f;
*(v++) = tangent.x;
*(v++) = tangent.y;
*(v++) = tangent.z;
*(v++) = bitangent_sign;
}
if (has_barycentric)
{
*(v++) = barycentric_coords[j].x;
*(v++) = barycentric_coords[j].y;
*(v++) = barycentric_coords[j].z;
}
}
}
// Allocate a model
model* model = new ::model();
model->set_bounds(bounds);
vertex_buffer* vbo = model->get_vertex_buffer();
vertex_array* vao = model->get_vertex_array();
vbo->resize(sizeof(float) * vertex_size * faces.size() * 3, vertex_data);
std::size_t offset = 0;
vao->bind_attribute(VERTEX_POSITION_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, 0);
offset += 3;
if (has_uvs)
{
vao->bind_attribute(VERTEX_TEXCOORD_LOCATION, *vbo, 2, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset);
offset += 2;
}
if (has_normals)
{
vao->bind_attribute(VERTEX_NORMAL_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset);
offset += 3;
}
if (has_tangents)
{
vao->bind_attribute(VERTEX_TANGENT_LOCATION, *vbo, 4, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset);
offset += 4;
}
if (has_barycentric)
{
vao->bind_attribute(VERTEX_BARYCENTRIC_LOCATION, *vbo, 3, vertex_attribute_type::float_32, vertex_stride, sizeof(float) * offset);
offset += 3;
}
// Add model groups for each material
for (const material_group& material_group: material_groups)
{
model_group* model_group = model->add_group(material_group.name);
model_group->set_material(material_group.material);
model_group->set_drawing_mode(drawing_mode::triangles);
model_group->set_start_index(material_group.start_index);
model_group->set_index_count(material_group.index_count);
}
// Deallocate vertex data
delete[] vertex_data;
return model;
}
*/
template <>
render::model* resource_loader<render::model>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{
@ -536,5 +218,72 @@ render::model* resource_loader::load(resource_manager* resource_m
}
}
// Build skeleton
if (auto skeleton_node = json.find("skeleton"); skeleton_node != json.end())
{
if (auto bones_node = skeleton_node->find("bones"); bones_node != skeleton_node->end())
{
render::skeleton& skeleton = model->get_skeleton();
skeleton.bones.resize(bones_node->size());
std::size_t bone_index = 0;
for (const auto& bone_node: bones_node.value())
{
render::bone& bone = skeleton.bones[bone_index];
// Find bone name
if (auto name_node = bone_node.find("name"); name_node != bone_node.end())
{
// Add bone to bone map
skeleton.bone_map[name_node->get<std::string>()] = bone_index;
}
// Find parent bone
bone.parent = nullptr;
if (auto parent_node = bone_node.find("parent"); parent_node != bone_node.end())
{
// Link bone to parent bone (if any)
if (!parent_node->is_null())
bone.parent = &skeleton.bones[parent_node->get<std::uint16_t>()];
}
// Clear bone transform
bone.transform = math::identity_transform<float>;
// Find translation
if (auto translation_node = bone_node.find("translation"); translation_node != bone_node.end())
{
if (translation_node->size() == 3)
{
bone.transform.translation.x = (*translation_node)[0].get<float>();
bone.transform.translation.y = (*translation_node)[1].get<float>();
bone.transform.translation.z = (*translation_node)[2].get<float>();
}
}
// Find rotation
if (auto rotation_node = bone_node.find("rotation"); rotation_node != bone_node.end())
{
if (rotation_node->size() == 4)
{
bone.transform.rotation.w = (*rotation_node)[0].get<float>();
bone.transform.rotation.x = (*rotation_node)[1].get<float>();
bone.transform.rotation.y = (*rotation_node)[2].get<float>();
bone.transform.rotation.z = (*rotation_node)[3].get<float>();
}
}
// Find length
if (auto length_node = bone_node.find("length"); length_node != bone_node.end())
bone.length = length_node->get<float>();
else
bone.length = 0.0f;
++bone_index;
}
}
}
return model;
}

Loading…
Cancel
Save