Antkeeper  0.0.1
ant-morphogenesis.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Christopher J. Howard
3  *
4  * This file is part of Antkeeper source code.
5  *
6  * Antkeeper source code is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Antkeeper source code is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
26 #include <engine/debug/log.hpp>
29 #include <unordered_set>
30 #include <optional>
31 
32 namespace {
33 
45 void reskin_vertices
46 (
47  std::byte* vertex_data,
48  std::size_t vertex_count,
49  const gl::vertex_input_attribute& position_attribute,
50  const gl::vertex_input_attribute& normal_attribute,
51  const gl::vertex_input_attribute& tangent_attribute,
52  const gl::vertex_input_attribute& bone_index_attribute,
53  std::size_t vertex_stride,
54  const std::unordered_map<bone_index_type, std::tuple<bone_index_type, const math::transform<float>*>>& reskin_map
55 )
56 {
57  std::byte* position_data = vertex_data + position_attribute.offset;
58  std::byte* normal_data = vertex_data + normal_attribute.offset;
59  std::byte* tangent_data = vertex_data + tangent_attribute.offset;
60  std::byte* bone_index_data = vertex_data + bone_index_attribute.offset;
61 
62  for (std::size_t i = 0; i < vertex_count; ++i)
63  {
64  // Get bone index of current vertex
65  std::uint16_t& bone_index = reinterpret_cast<std::uint16_t&>(*(bone_index_data + vertex_stride * i));
66 
67  // Ignore vertices with unmapped bone indices
68  auto entry = reskin_map.find(static_cast<bone_index_type>(bone_index));
69  if (entry == reskin_map.end())
70  {
71  continue;
72  }
73 
74  const auto& [new_bone_index, transform] = entry->second;
75 
76  // Update bone index
77  bone_index = static_cast<std::uint16_t>(new_bone_index);
78 
79  // Get vertex attributes
80  float* px = reinterpret_cast<float*>(position_data + vertex_stride * i);
81  float* py = px + 1;
82  float* pz = py + 1;
83  float* nx = reinterpret_cast<float*>(normal_data + vertex_stride * i);
84  float* ny = nx + 1;
85  float* nz = ny + 1;
86  float* tx = reinterpret_cast<float*>(tangent_data + vertex_stride * i);
87  float* ty = tx + 1;
88  float* tz = ty + 1;
89 
90  // Transform vertex attributes
91  const math::fvec3 position = (*transform) * math::fvec3{*px, *py, *pz};
92  const math::fvec3 normal = math::normalize(transform->rotation * math::fvec3{*nx, *ny, *nz});
93  const math::fvec3 tangent = math::normalize(transform->rotation * math::fvec3{*tx, *ty, *tz});
94 
95  // Update vertex attributes
96  *px = position.x();
97  *py = position.y();
98  *pz = position.z();
99  *nx = normal.x();
100  *ny = normal.y();
101  *nz = normal.z();
102  *tx = tangent.x();
103  *ty = tangent.y();
104  *tz = tangent.z();
105  }
106 }
107 
115 void tag_vertices
116 (
117  std::span<std::byte> vertex_data,
118  const gl::vertex_input_attribute& bone_index_attribute,
119  std::size_t vertex_stride,
120  std::uint16_t vertex_tag
121 )
122 {
123  std::byte* bone_index_data = vertex_data.data() + bone_index_attribute.offset;
124 
125  for (std::size_t i = 0; i < vertex_data.size(); ++i)
126  {
127  // Get bone indices of current vertex
128  std::uint16_t* bone_indices = reinterpret_cast<std::uint16_t*>(bone_index_data + vertex_stride * i);
129 
130  // Tag fourth bone index
131  bone_indices[3] = vertex_tag;
132  }
133 }
134 
143 float calculate_uv_area
144 (
145  std::span<std::byte> vertex_data,
146  const gl::vertex_input_attribute& uv_attribute,
147  std::size_t vertex_stride
148 )
149 {
150  std::byte* uv_data = vertex_data.data() + uv_attribute.offset;
151 
152  float sum_area = 0.0f;
153 
154  for (std::size_t i = 0; i + 2 < vertex_data.size(); i += 3)
155  {
156  const float* uv_data_a = reinterpret_cast<const float*>(uv_data + vertex_stride * i);
157  const float* uv_data_b = reinterpret_cast<const float*>(uv_data + vertex_stride * (i + 1));
158  const float* uv_data_c = reinterpret_cast<const float*>(uv_data + vertex_stride * (i + 2));
159 
160  const math::fvec3 uva = {uv_data_a[0], uv_data_a[1], 0.0f};
161  const math::fvec3 uvb = {uv_data_b[0], uv_data_b[1], 0.0f};
162  const math::fvec3 uvc = {uv_data_c[0], uv_data_c[1], 0.0f};
163 
164  const math::fvec3 uvab = uvb - uva;
165  const math::fvec3 uvac = uvc - uva;
166 
167  sum_area += math::length(math::cross(uvab, uvac)) * 0.5f;
168  }
169 
170  return sum_area;
171 }
172 
182 [[nodiscard]] geom::box<float> calculate_bounds
183 (
184  const std::byte* vertex_data,
185  std::size_t vertex_count,
186  const gl::vertex_input_attribute& position_attribute,
187  std::size_t vertex_stride
188 )
189 {
190  const std::byte* position_data = vertex_data + position_attribute.offset;
191 
193  for (std::size_t i = 0; i < vertex_count; ++i)
194  {
195  const float* px = reinterpret_cast<const float*>(position_data + vertex_stride * i);
196  const float* py = px + 1;
197  const float* pz = py + 1;
198 
199  bounds.extend(math::fvec3{*px, *py, *pz});
200  }
201 
202  return bounds;
203 }
204 
213 [[nodiscard]] float calculate_ommatidia_scale(float eye_uv_area, float ommatidia_count)
214 {
215  // Side length of hexagon tiles generated by the eye shader
216  constexpr float source_side_length = 1.0f / math::sqrt_3<float>;
217 
218  // Side length of hexagon tiles that will fill UV area
219  const float target_side_length = std::sqrt((eye_uv_area * 2.0f) / (3.0f * math::sqrt_3<float> * ommatidia_count));
220 
221  return source_side_length / target_side_length;
222 }
223 
232 [[nodiscard]] std::unique_ptr<render::material> generate_ant_exoskeleton_material
233 (
234  const ant_phenome& phenome,
235  float eye_uv_area
236 )
237 {
238  // Allocate copy of pigmentation material
239  std::unique_ptr<render::material> exoskeleton_material = std::make_unique<render::material>(*phenome.pigmentation->material);
240 
241  // Set roughness variable
242  exoskeleton_material->set_variable("exoskeleton_roughness", std::make_shared<render::matvar_float>(1, phenome.sculpturing->roughness));
243 
244  // Set normal map variable
245  exoskeleton_material->set_variable("exoskeleton_normal_map", std::make_shared<render::matvar_texture_2d>(1, phenome.sculpturing->normal_map));
246 
247  if (phenome.eyes->present)
248  {
249  // Set ommatidia scale variable
250  const float ommatidia_scale = calculate_ommatidia_scale(eye_uv_area, static_cast<float>(phenome.eyes->ommatidia_count));
251  exoskeleton_material->set_variable("ommatidia_scale", std::make_shared<render::matvar_float>(1, ommatidia_scale));
252  }
253 
254  return exoskeleton_material;
255 }
256 
257 } // namespace
258 
259 std::unique_ptr<render::model> ant_morphogenesis(const ant_phenome& phenome)
260 {
261  // Get body part models
262  const render::model* mesosoma_model = phenome.mesosoma->model.get();
263  const render::model* legs_model = phenome.legs->model.get();
264  const render::model* head_model = phenome.head->model.get();
265  const render::model* mandibles_model = phenome.mandibles->model.get();
266  const render::model* antennae_model = phenome.antennae->model.get();
267  const render::model* waist_model = phenome.waist->model.get();
268  const render::model* gaster_model = phenome.gaster->model.get();
269  const render::model* sting_model = phenome.sting->model.get();
270  const render::model* eyes_model = phenome.eyes->model.get();
271  const render::model* ocelli_model = phenome.ocelli->model.get();
272  const render::model* wings_model = phenome.wings->model.get();
273 
274  // Check for presence of required part models
275  if (!mesosoma_model)
276  {
277  throw std::runtime_error("Ant phenome missing mesosoma model");
278  }
279  if (!legs_model)
280  {
281  throw std::runtime_error("Ant phenome missing legs model");
282  }
283  if (!head_model)
284  {
285  throw std::runtime_error("Ant phenome missing head model");
286  }
287  if (!mandibles_model)
288  {
289  throw std::runtime_error("Ant phenome missing mandibles model");
290  }
291  if (!antennae_model)
292  {
293  throw std::runtime_error("Ant phenome missing antennae model");
294  }
295  if (!waist_model)
296  {
297  throw std::runtime_error("Ant phenome missing waist model");
298  }
299  if (!gaster_model)
300  {
301  throw std::runtime_error("Ant phenome missing gaster model");
302  }
303  if (phenome.sting->present && !sting_model)
304  {
305  throw std::runtime_error("Ant phenome missing sting model");
306  }
307  if (phenome.eyes->present && !eyes_model)
308  {
309  throw std::runtime_error("Ant phenome missing eyes model");
310  }
311  if ((phenome.ocelli->lateral_ocelli_present || phenome.ocelli->median_ocellus_present) && !ocelli_model)
312  {
313  throw std::runtime_error("Ant phenome missing ocelli model");
314  }
315  if (phenome.wings->present && !wings_model)
316  {
317  throw std::runtime_error("Ant phenome missing wings model");
318  }
319 
320  // Get body part vertex buffers
321  const gl::vertex_buffer* mesosoma_vbo = mesosoma_model->get_vertex_buffer().get();
322  const gl::vertex_buffer* legs_vbo = legs_model->get_vertex_buffer().get();
323  const gl::vertex_buffer* head_vbo = head_model->get_vertex_buffer().get();
324  const gl::vertex_buffer* mandibles_vbo = mandibles_model->get_vertex_buffer().get();
325  const gl::vertex_buffer* antennae_vbo = antennae_model->get_vertex_buffer().get();
326  const gl::vertex_buffer* waist_vbo = waist_model->get_vertex_buffer().get();
327  const gl::vertex_buffer* gaster_vbo = gaster_model->get_vertex_buffer().get();
328  const gl::vertex_buffer* sting_vbo = (phenome.sting->present) ? sting_model->get_vertex_buffer().get() : nullptr;
329  const gl::vertex_buffer* eyes_vbo = (phenome.eyes->present) ? eyes_model->get_vertex_buffer().get() : nullptr;
330  const gl::vertex_buffer* ocelli_vbo = (phenome.ocelli->lateral_ocelli_present || phenome.ocelli->median_ocellus_present) ? ocelli_model->get_vertex_buffer().get() : nullptr;
331  const gl::vertex_buffer* wings_vbo = (phenome.wings->present) ? wings_model->get_vertex_buffer().get() : nullptr;
332 
333  // Determine combined size of vertex buffers and save offsets
334  std::size_t vertex_buffer_size = 0;
335  const std::size_t mesosoma_vbo_offset = vertex_buffer_size;
336  vertex_buffer_size += mesosoma_vbo->size();
337  const std::size_t legs_vbo_offset = vertex_buffer_size;
338  vertex_buffer_size += legs_vbo->size();
339  const std::size_t head_vbo_offset = vertex_buffer_size;
340  vertex_buffer_size += head_vbo->size();
341  const std::size_t mandibles_vbo_offset = vertex_buffer_size;
342  vertex_buffer_size += mandibles_vbo->size();
343  const std::size_t antennae_vbo_offset = vertex_buffer_size;
344  vertex_buffer_size += antennae_vbo->size();
345  const std::size_t waist_vbo_offset = vertex_buffer_size;
346  vertex_buffer_size += waist_vbo->size();
347  const std::size_t gaster_vbo_offset = vertex_buffer_size;
348  vertex_buffer_size += gaster_vbo->size();
349  const std::size_t sting_vbo_offset = vertex_buffer_size;
350  if (phenome.sting->present)
351  {
352  vertex_buffer_size += sting_vbo->size();
353  }
354  const std::size_t eyes_vbo_offset = vertex_buffer_size;
355  if (phenome.eyes->present)
356  {
357  vertex_buffer_size += eyes_vbo->size();
358  }
359  const std::size_t ocelli_vbo_offset = vertex_buffer_size;
361  {
362  vertex_buffer_size += ocelli_vbo->size();
363  }
364  std::size_t wings_vbo_offset = vertex_buffer_size;
365  if (phenome.wings->present)
366  {
367  vertex_buffer_size += wings_vbo->size();
368  }
369 
370  // Allocate combined vertex buffer data
371  std::vector<std::byte> vertex_buffer_data(vertex_buffer_size);
372 
373  // Read body part vertex buffer data into combined vertex buffer data
374  mesosoma_vbo->read({vertex_buffer_data.data() + mesosoma_vbo_offset, mesosoma_vbo->size()});
375  legs_vbo->read({vertex_buffer_data.data() + legs_vbo_offset, legs_vbo->size()});
376  head_vbo->read({vertex_buffer_data.data() + head_vbo_offset, head_vbo->size()});
377  mandibles_vbo->read({vertex_buffer_data.data() + mandibles_vbo_offset, mandibles_vbo->size()});
378  antennae_vbo->read({vertex_buffer_data.data() + antennae_vbo_offset, antennae_vbo->size()});
379  waist_vbo->read({vertex_buffer_data.data() + waist_vbo_offset, waist_vbo->size()});
380  gaster_vbo->read({vertex_buffer_data.data() + gaster_vbo_offset, gaster_vbo->size()});
381  if (phenome.sting->present)
382  {
383  sting_vbo->read({vertex_buffer_data.data() + sting_vbo_offset, sting_vbo->size()});
384  }
385  if (phenome.eyes->present)
386  {
387  eyes_vbo->read({vertex_buffer_data.data() + eyes_vbo_offset, eyes_vbo->size()});
388  }
390  {
391  ocelli_vbo->read({vertex_buffer_data.data() + ocelli_vbo_offset, ocelli_vbo->size()});
392  }
393  if (phenome.wings->present)
394  {
395  wings_vbo->read({vertex_buffer_data.data() + wings_vbo_offset, wings_vbo->size()});
396  }
397 
398  // Allocate model
399  std::unique_ptr<render::model> model = std::make_unique<render::model>();
400 
401  // Construct model VAO (clone mesosoma model VAO)
402  auto& model_vao = model->get_vertex_array();
403  model_vao = std::make_unique<gl::vertex_array>(mesosoma_model->get_vertex_array()->attributes());
404 
405  // Get vertex attributes
406  const gl::vertex_input_attribute* position_attribute = nullptr;
407  const gl::vertex_input_attribute* uv_attribute = nullptr;
408  const gl::vertex_input_attribute* normal_attribute = nullptr;
409  const gl::vertex_input_attribute* tangent_attribute = nullptr;
410  const gl::vertex_input_attribute* bone_index_attribute = nullptr;
411  for (const auto& attribute: model_vao->attributes())
412  {
413  switch (attribute.location)
414  {
416  position_attribute = &attribute;
417  break;
419  uv_attribute = &attribute;
420  break;
422  normal_attribute = &attribute;
423  break;
425  tangent_attribute = &attribute;
426  break;
428  bone_index_attribute = &attribute;
429  break;
430  default:
431  break;
432  }
433  }
434 
435  // Init model vertex binding
436  model->set_vertex_offset(0);
437  model->set_vertex_stride(mesosoma_model->get_vertex_stride());
438 
439  // Generate ant skeleton
440  ::skeleton& skeleton = model->get_skeleton();
441  ant_bone_set bones;
442  generate_ant_skeleton(skeleton, bones, phenome);
443  const auto& rest_pose = skeleton.get_rest_pose();
444 
445  // Get number of vertices for each body part
446  const std::uint32_t mesosoma_vertex_count = (mesosoma_model->get_groups()).front().vertex_count;
447  const std::uint32_t legs_vertex_count = (legs_model->get_groups()).front().vertex_count;
448  const std::uint32_t head_vertex_count = (head_model->get_groups()).front().vertex_count;
449  const std::uint32_t mandibles_vertex_count = (mandibles_model->get_groups()).front().vertex_count;
450  const std::uint32_t antennae_vertex_count = (antennae_model->get_groups()).front().vertex_count;
451  const std::uint32_t waist_vertex_count = (waist_model->get_groups()).front().vertex_count;
452  const std::uint32_t gaster_vertex_count = (gaster_model->get_groups()).front().vertex_count;
453  const std::uint32_t sting_vertex_count = (phenome.sting->present) ? (sting_model->get_groups()).front().vertex_count : 0;
454  const std::uint32_t eyes_vertex_count = (phenome.eyes->present) ? (eyes_model->get_groups()).front().vertex_count : 0;
455  const std::uint32_t ocelli_vertex_count = (phenome.ocelli->lateral_ocelli_present || phenome.ocelli->median_ocellus_present) ? (ocelli_model->get_groups()).front().vertex_count : 0;
456  const std::uint32_t wings_vertex_count = (phenome.wings->present) ? wings_model->get_groups().front().vertex_count : 0;
457 
458  // Get body part skeletons
459  const ::skeleton& mesosoma_skeleton = phenome.mesosoma->model->get_skeleton();
460  const ::skeleton& legs_skeleton = phenome.legs->model->get_skeleton();
461  const ::skeleton& head_skeleton = phenome.head->model->get_skeleton();
462  const ::skeleton& mandibles_skeleton = phenome.mandibles->model->get_skeleton();
463  const ::skeleton& antennae_skeleton = phenome.antennae->model->get_skeleton();
464  const ::skeleton& waist_skeleton = phenome.waist->model->get_skeleton();
465  const ::skeleton& gaster_skeleton = phenome.gaster->model->get_skeleton();
466  const ::skeleton* sting_skeleton = (phenome.sting->present) ? &phenome.sting->model->get_skeleton() : nullptr;
467  const ::skeleton* eyes_skeleton = (phenome.eyes->present) ? &phenome.eyes->model->get_skeleton() : nullptr;
468  const ::skeleton* ocelli_skeleton = (phenome.ocelli->lateral_ocelli_present || phenome.ocelli->median_ocellus_present) ? &phenome.ocelli->model->get_skeleton() : nullptr;
469  const ::skeleton* wings_skeleton = (phenome.wings->present) ? &phenome.wings->model->get_skeleton() : nullptr;
470 
471  auto get_bone_transform = [](const ::skeleton& skeleton, hash::fnv1a32_t bone_name)
472  {
474  };
475 
476  // Calculate transformations from part space to body space
477  const math::transform<float> procoxa_l_to_body = rest_pose.get_absolute_transform(bones.mesosoma) * get_bone_transform(mesosoma_skeleton, "procoxa_socket_l");
478  const math::transform<float> procoxa_r_to_body = rest_pose.get_absolute_transform(bones.mesosoma) * get_bone_transform(mesosoma_skeleton, "procoxa_socket_r");
479  const math::transform<float> mesocoxa_l_to_body = rest_pose.get_absolute_transform(bones.mesosoma) * get_bone_transform(mesosoma_skeleton, "mesocoxa_socket_l");
480  const math::transform<float> mesocoxa_r_to_body = rest_pose.get_absolute_transform(bones.mesosoma) * get_bone_transform(mesosoma_skeleton, "mesocoxa_socket_r");
481  const math::transform<float> metacoxa_l_to_body = rest_pose.get_absolute_transform(bones.mesosoma) * get_bone_transform(mesosoma_skeleton, "metacoxa_socket_l");
482  const math::transform<float> metacoxa_r_to_body = rest_pose.get_absolute_transform(bones.mesosoma) * get_bone_transform(mesosoma_skeleton, "metacoxa_socket_r");
483  const math::transform<float> head_to_body = rest_pose.get_absolute_transform(bones.mesosoma) * get_bone_transform(mesosoma_skeleton, "head_socket");
484  const math::transform<float> mandible_l_to_body = rest_pose.get_absolute_transform(bones.head) * get_bone_transform(head_skeleton, "mandible_socket_l");
485  const math::transform<float> mandible_r_to_body = rest_pose.get_absolute_transform(bones.head) * get_bone_transform(head_skeleton, "mandible_socket_r");
486  const math::transform<float> antenna_l_to_body = rest_pose.get_absolute_transform(bones.head) * get_bone_transform(head_skeleton, "antenna_socket_l");
487  const math::transform<float> antenna_r_to_body = rest_pose.get_absolute_transform(bones.head) * get_bone_transform(head_skeleton, "antenna_socket_r");
488  const math::transform<float> waist_to_body = rest_pose.get_absolute_transform(bones.mesosoma) * get_bone_transform(mesosoma_skeleton, "petiole_socket");
489 
490  math::transform<float> gaster_to_body;
491  if (phenome.waist->present)
492  {
493  if (phenome.waist->postpetiole_present)
494  {
495  gaster_to_body = rest_pose.get_absolute_transform(*bones.postpetiole) * get_bone_transform(waist_skeleton, "gaster_socket");
496  }
497  else
498  {
499  gaster_to_body = rest_pose.get_absolute_transform(*bones.petiole) * get_bone_transform(waist_skeleton, "gaster_socket");
500  }
501  }
502  else
503  {
504  gaster_to_body = waist_to_body;
505  }
506 
507  math::transform<float> sting_to_body;
508  if (phenome.sting->present)
509  {
510  sting_to_body = gaster_to_body * get_bone_transform(gaster_skeleton, "sting_socket");
511  }
512 
513  math::transform<float> eye_l_to_body;
514  math::transform<float> eye_r_to_body;
515  if (phenome.eyes->present)
516  {
517  eye_l_to_body = rest_pose.get_absolute_transform(bones.head) * get_bone_transform(head_skeleton, "eye_socket_l");
518  eye_r_to_body = rest_pose.get_absolute_transform(bones.head) * get_bone_transform(head_skeleton, "eye_socket_r");
519  }
520 
521  math::transform<float> ocellus_l_to_body;
522  math::transform<float> ocellus_r_to_body;
523  math::transform<float> ocellus_m_to_body;
525  {
526  ocellus_l_to_body = rest_pose.get_absolute_transform(bones.head) * get_bone_transform(head_skeleton, "ocellus_socket_l");
527  ocellus_r_to_body = rest_pose.get_absolute_transform(bones.head) * get_bone_transform(head_skeleton, "ocellus_socket_r");
528  ocellus_m_to_body = rest_pose.get_absolute_transform(bones.head) * get_bone_transform(head_skeleton, "ocellus_socket_m");
529  }
530 
531  // Build legs vertex reskin map
532  const std::unordered_map<bone_index_type, std::tuple<bone_index_type, const math::transform<float>*>> legs_reskin_map
533  {
534  {*legs_skeleton.get_bone_index("procoxa_l"), {bones.procoxa_l, &procoxa_l_to_body}},
535  {*legs_skeleton.get_bone_index("profemur_l"), {bones.profemur_l, &procoxa_l_to_body}},
536  {*legs_skeleton.get_bone_index("protibia_l"), {bones.protibia_l, &procoxa_l_to_body}},
537  {*legs_skeleton.get_bone_index("protarsomere1_l"), {bones.protarsomere1_l, &procoxa_l_to_body}},
538  {*legs_skeleton.get_bone_index("protarsomere2_l"), {bones.protarsomere1_l, &procoxa_l_to_body}},
539  {*legs_skeleton.get_bone_index("protarsomere3_l"), {bones.protarsomere1_l, &procoxa_l_to_body}},
540  {*legs_skeleton.get_bone_index("protarsomere4_l"), {bones.protarsomere1_l, &procoxa_l_to_body}},
541  {*legs_skeleton.get_bone_index("protarsomere5_l"), {bones.protarsomere1_l, &procoxa_l_to_body}},
542  {*legs_skeleton.get_bone_index("procoxa_r"), {bones.procoxa_r, &procoxa_r_to_body}},
543  {*legs_skeleton.get_bone_index("profemur_r"), {bones.profemur_r, &procoxa_r_to_body}},
544  {*legs_skeleton.get_bone_index("protibia_r"), {bones.protibia_r, &procoxa_r_to_body}},
545  {*legs_skeleton.get_bone_index("protarsomere1_r"), {bones.protarsomere1_r, &procoxa_r_to_body}},
546  {*legs_skeleton.get_bone_index("protarsomere2_r"), {bones.protarsomere1_r, &procoxa_r_to_body}},
547  {*legs_skeleton.get_bone_index("protarsomere3_r"), {bones.protarsomere1_r, &procoxa_r_to_body}},
548  {*legs_skeleton.get_bone_index("protarsomere4_r"), {bones.protarsomere1_r, &procoxa_r_to_body}},
549  {*legs_skeleton.get_bone_index("protarsomere5_r"), {bones.protarsomere1_r, &procoxa_r_to_body}},
550  {*legs_skeleton.get_bone_index("mesocoxa_l"), {bones.mesocoxa_l, &mesocoxa_l_to_body}},
551  {*legs_skeleton.get_bone_index("mesofemur_l"), {bones.mesofemur_l, &mesocoxa_l_to_body}},
552  {*legs_skeleton.get_bone_index("mesotibia_l"), {bones.mesotibia_l, &mesocoxa_l_to_body}},
553  {*legs_skeleton.get_bone_index("mesotarsomere1_l"), {bones.mesotarsomere1_l, &mesocoxa_l_to_body}},
554  {*legs_skeleton.get_bone_index("mesotarsomere2_l"), {bones.mesotarsomere1_l, &mesocoxa_l_to_body}},
555  {*legs_skeleton.get_bone_index("mesotarsomere3_l"), {bones.mesotarsomere1_l, &mesocoxa_l_to_body}},
556  {*legs_skeleton.get_bone_index("mesotarsomere4_l"), {bones.mesotarsomere1_l, &mesocoxa_l_to_body}},
557  {*legs_skeleton.get_bone_index("mesotarsomere5_l"), {bones.mesotarsomere1_l, &mesocoxa_l_to_body}},
558  {*legs_skeleton.get_bone_index("mesocoxa_r"), {bones.mesocoxa_r, &mesocoxa_r_to_body}},
559  {*legs_skeleton.get_bone_index("mesofemur_r"), {bones.mesofemur_r, &mesocoxa_r_to_body}},
560  {*legs_skeleton.get_bone_index("mesotibia_r"), {bones.mesotibia_r, &mesocoxa_r_to_body}},
561  {*legs_skeleton.get_bone_index("mesotarsomere1_r"), {bones.mesotarsomere1_r, &mesocoxa_r_to_body}},
562  {*legs_skeleton.get_bone_index("mesotarsomere2_r"), {bones.mesotarsomere1_r, &mesocoxa_r_to_body}},
563  {*legs_skeleton.get_bone_index("mesotarsomere3_r"), {bones.mesotarsomere1_r, &mesocoxa_r_to_body}},
564  {*legs_skeleton.get_bone_index("mesotarsomere4_r"), {bones.mesotarsomere1_r, &mesocoxa_r_to_body}},
565  {*legs_skeleton.get_bone_index("mesotarsomere5_r"), {bones.mesotarsomere1_r, &mesocoxa_r_to_body}},
566  {*legs_skeleton.get_bone_index("metacoxa_l"), {bones.metacoxa_l, &metacoxa_l_to_body}},
567  {*legs_skeleton.get_bone_index("metafemur_l"), {bones.metafemur_l, &metacoxa_l_to_body}},
568  {*legs_skeleton.get_bone_index("metatibia_l"), {bones.metatibia_l, &metacoxa_l_to_body}},
569  {*legs_skeleton.get_bone_index("metatarsomere1_l"), {bones.metatarsomere1_l, &metacoxa_l_to_body}},
570  {*legs_skeleton.get_bone_index("metatarsomere2_l"), {bones.metatarsomere1_l, &metacoxa_l_to_body}},
571  {*legs_skeleton.get_bone_index("metatarsomere3_l"), {bones.metatarsomere1_l, &metacoxa_l_to_body}},
572  {*legs_skeleton.get_bone_index("metatarsomere4_l"), {bones.metatarsomere1_l, &metacoxa_l_to_body}},
573  {*legs_skeleton.get_bone_index("metatarsomere5_l"), {bones.metatarsomere1_l, &metacoxa_l_to_body}},
574  {*legs_skeleton.get_bone_index("metacoxa_r"), {bones.metacoxa_r, &metacoxa_r_to_body}},
575  {*legs_skeleton.get_bone_index("metafemur_r"), {bones.metafemur_r, &metacoxa_r_to_body}},
576  {*legs_skeleton.get_bone_index("metatibia_r"), {bones.metatibia_r, &metacoxa_r_to_body}},
577  {*legs_skeleton.get_bone_index("metatarsomere1_r"), {bones.metatarsomere1_r, &metacoxa_r_to_body}},
578  {*legs_skeleton.get_bone_index("metatarsomere2_r"), {bones.metatarsomere1_r, &metacoxa_r_to_body}},
579  {*legs_skeleton.get_bone_index("metatarsomere3_r"), {bones.metatarsomere1_r, &metacoxa_r_to_body}},
580  {*legs_skeleton.get_bone_index("metatarsomere4_r"), {bones.metatarsomere1_r, &metacoxa_r_to_body}},
581  {*legs_skeleton.get_bone_index("metatarsomere5_r"), {bones.metatarsomere1_r, &metacoxa_r_to_body}}
582  };
583 
584  // Build head vertex reskin map
585  const std::unordered_map<bone_index_type, std::tuple<bone_index_type, const math::transform<float>*>> head_reskin_map
586  {
587  {*head_skeleton.get_bone_index("head"), {bones.head, &head_to_body}}
588  };
589 
590  // Build mandibles vertex reskin map
591  const std::unordered_map<bone_index_type, std::tuple<bone_index_type, const math::transform<float>*>> mandibles_reskin_map
592  {
593  {*mandibles_skeleton.get_bone_index("mandible_l"), {bones.mandible_l, &mandible_l_to_body}},
594  {*mandibles_skeleton.get_bone_index("mandible_r"), {bones.mandible_r, &mandible_r_to_body}}
595  };
596 
597  // Build antennae vertex reskin map
598  std::unordered_map<bone_index_type, std::tuple<bone_index_type, const math::transform<float>*>> antennae_reskin_map
599  {
600  {*antennae_skeleton.get_bone_index("antennomere1_l"), {bones.antennomere1_l, &antenna_l_to_body}},
601  {*antennae_skeleton.get_bone_index("antennomere2_l"), {bones.antennomere2_l, &antenna_l_to_body}},
602  {*antennae_skeleton.get_bone_index("antennomere1_r"), {bones.antennomere1_r, &antenna_r_to_body}},
603  {*antennae_skeleton.get_bone_index("antennomere2_r"), {bones.antennomere2_r, &antenna_r_to_body}}
604  };
605  for (std::uint8_t i = 3; i <= phenome.antennae->total_antennomere_count; ++i)
606  {
607  const std::string antennomere_l_name = std::format("antennomere{}_l", i);
608  const std::string antennomere_r_name = std::format("antennomere{}_r", i);
609 
610  const hash::fnv1a32_t antennomere_l_key = hash::fnv1a32<char>(antennomere_l_name);
611  const hash::fnv1a32_t antennomere_r_key = hash::fnv1a32<char>(antennomere_r_name);
612 
613  antennae_reskin_map.emplace(*antennae_skeleton.get_bone_index(antennomere_l_key), std::tuple(bones.antennomere2_l, &antenna_l_to_body));
614  antennae_reskin_map.emplace(*antennae_skeleton.get_bone_index(antennomere_r_key), std::tuple(bones.antennomere2_r, &antenna_r_to_body));
615  }
616 
617  // Build waist vertex reskin map
618  std::unordered_map<bone_index_type, std::tuple<bone_index_type, const math::transform<float>*>> waist_reskin_map;
619  if (phenome.waist->present)
620  {
621  waist_reskin_map.emplace(*waist_skeleton.get_bone_index("petiole"), std::tuple(*bones.petiole, &waist_to_body));
622 
623  if (phenome.waist->postpetiole_present)
624  {
625  waist_reskin_map.emplace(*waist_skeleton.get_bone_index("postpetiole"), std::tuple(*bones.postpetiole, &waist_to_body));
626  }
627  }
628 
629  // Build gaster vertex reskin map
630  const std::unordered_map<bone_index_type, std::tuple<bone_index_type, const math::transform<float>*>> gaster_reskin_map
631  {
632  {*gaster_skeleton.get_bone_index("gaster"), {bones.gaster, &gaster_to_body}}
633  };
634 
635  // Build sting vertex reskin map
636  std::unordered_map<bone_index_type, std::tuple<bone_index_type, const math::transform<float>*>> sting_reskin_map;
637  if (phenome.sting->present)
638  {
639  sting_reskin_map.emplace(*sting_skeleton->get_bone_index("sting"), std::tuple(*bones.sting, &sting_to_body));
640  }
641 
642  // Build eyes vertex reskin map
643  std::unordered_map<bone_index_type, std::tuple<bone_index_type, const math::transform<float>*>> eyes_reskin_map;
644  if (phenome.eyes->present)
645  {
646  eyes_reskin_map.emplace(*eyes_skeleton->get_bone_index("eye_l"), std::tuple(bones.head, &eye_l_to_body));
647  eyes_reskin_map.emplace(*eyes_skeleton->get_bone_index("eye_r"), std::tuple(bones.head, &eye_r_to_body));
648  }
649 
650  // Build ocelli vertex reskin map
651  std::unordered_map<bone_index_type, std::tuple<bone_index_type, const math::transform<float>*>> ocelli_reskin_map;
652  if (phenome.ocelli->lateral_ocelli_present)
653  {
654  ocelli_reskin_map.emplace(*ocelli_skeleton->get_bone_index("ocellus_l"), std::tuple(bones.head, &ocellus_l_to_body));
655  ocelli_reskin_map.emplace(*ocelli_skeleton->get_bone_index("ocellus_r"), std::tuple(bones.head, &ocellus_r_to_body));
656  ocelli_reskin_map.emplace(*ocelli_skeleton->get_bone_index("ocellus_m"), std::tuple(bones.head, &ocellus_m_to_body));
657  }
658 
659  // Reskin legs vertices
660  reskin_vertices(vertex_buffer_data.data() + legs_vbo_offset, legs_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, model->get_vertex_stride(), legs_reskin_map);
661 
662  // Reskin head vertices
663  reskin_vertices(vertex_buffer_data.data() + head_vbo_offset, head_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, model->get_vertex_stride(), head_reskin_map);
664 
665  // Reskin mandibles vertices
666  reskin_vertices(vertex_buffer_data.data() + mandibles_vbo_offset, mandibles_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, model->get_vertex_stride(), mandibles_reskin_map);
667 
668  // Reskin antennae vertices
669  reskin_vertices(vertex_buffer_data.data() + antennae_vbo_offset, antennae_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, model->get_vertex_stride(), antennae_reskin_map);
670 
671  // Reskin waist vertices
672  if (phenome.waist->present)
673  {
674  reskin_vertices(vertex_buffer_data.data() + waist_vbo_offset, waist_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, model->get_vertex_stride(), waist_reskin_map);
675  }
676 
677  // Reskin gaster vertices
678  reskin_vertices(vertex_buffer_data.data() + gaster_vbo_offset, gaster_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, model->get_vertex_stride(), gaster_reskin_map);
679 
680  // Reskin sting vertices
681  if (phenome.sting->present)
682  {
683  reskin_vertices(vertex_buffer_data.data() + sting_vbo_offset, sting_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, model->get_vertex_stride(), sting_reskin_map);
684  }
685 
686  // Reskin eyes vertices
687  if (phenome.eyes->present)
688  {
689  reskin_vertices(vertex_buffer_data.data() + eyes_vbo_offset, eyes_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, model->get_vertex_stride(), eyes_reskin_map);
690  }
691 
692  // Reskin ocelli vertices
694  {
695  reskin_vertices(vertex_buffer_data.data() + ocelli_vbo_offset, ocelli_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, model->get_vertex_stride(), ocelli_reskin_map);
696  }
697 
698  // Reskin wings vertices
699  if (phenome.wings->present)
700  {
701  const auto forewing_l_to_body = rest_pose.get_absolute_transform(bones.mesosoma) * get_bone_transform(mesosoma_skeleton, "forewing_socket_l");
702  const auto forewing_r_to_body = rest_pose.get_absolute_transform(bones.mesosoma) * get_bone_transform(mesosoma_skeleton, "forewing_socket_r");
703  const auto hindwing_l_to_body = rest_pose.get_absolute_transform(bones.mesosoma) * get_bone_transform(mesosoma_skeleton, "hindwing_socket_l");
704  const auto hindwing_r_to_body = rest_pose.get_absolute_transform(bones.mesosoma) * get_bone_transform(mesosoma_skeleton, "hindwing_socket_r");
705 
706  std::unordered_map<bone_index_type, std::tuple<bone_index_type, const math::transform<float>*>> wings_reskin_map;
707  wings_reskin_map.emplace(*wings_skeleton->get_bone_index("forewing_l"), std::tuple(*bones.forewing_l, &forewing_l_to_body));
708  wings_reskin_map.emplace(*wings_skeleton->get_bone_index("forewing_r"), std::tuple(*bones.forewing_r, &forewing_r_to_body));
709  wings_reskin_map.emplace(*wings_skeleton->get_bone_index("hindwing_l"), std::tuple(*bones.hindwing_l, &hindwing_l_to_body));
710  wings_reskin_map.emplace(*wings_skeleton->get_bone_index("hindwing_r"), std::tuple(*bones.hindwing_r, &hindwing_r_to_body));
711 
712  reskin_vertices(vertex_buffer_data.data() + wings_vbo_offset, wings_vertex_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, model->get_vertex_stride(), wings_reskin_map);
713  }
714 
715  // Tag eye vertices
716  if (phenome.eyes->present)
717  {
718  tag_vertices({vertex_buffer_data.data() + eyes_vbo_offset, vertex_buffer_data.data() + eyes_vbo_offset + eyes_vertex_count}, *bone_index_attribute, model->get_vertex_stride(), 1);
719  }
720 
721  // Construct model VBO
722  auto& model_vbo = model->get_vertex_buffer();
723  model_vbo = std::make_unique<gl::vertex_buffer>(gl::buffer_usage::static_draw, vertex_buffer_data);
724 
725  // Allocate model groups
726  model->get_groups().resize(phenome.wings->present ? 2 : 1);
727 
728  // Calculate UV area of a single eye
729  float eye_uv_area = 0.0f;
730  if (phenome.eyes->present)
731  {
732  eye_uv_area = calculate_uv_area({vertex_buffer_data.data() + eyes_vbo_offset, vertex_buffer_data.data() + eyes_vbo_offset + eyes_vertex_count / 2}, *uv_attribute, model->get_vertex_stride());
733  }
734 
735  // Generate exoskeleton material
736  std::shared_ptr<render::material> exoskeleton_material = generate_ant_exoskeleton_material(phenome, eye_uv_area);
737 
738  // Construct model group
739  render::model_group& model_group = model->get_groups()[0];
740  model_group.id = "exoskeleton";
741  model_group.material = exoskeleton_material;
743  model_group.first_vertex = 0;
744  model_group.vertex_count = mesosoma_vertex_count +
745  legs_vertex_count +
746  head_vertex_count +
747  mandibles_vertex_count +
748  antennae_vertex_count +
749  waist_vertex_count +
750  gaster_vertex_count +
751  sting_vertex_count +
752  eyes_vertex_count +
753  ocelli_vertex_count;
754 
755  if (phenome.wings->present)
756  {
757  // Construct wings model group
758  render::model_group& wings_group = model->get_groups()[1];
759  wings_group.id = "wings";
760  wings_group.material = wings_model->get_groups().front().material;
762  wings_group.first_vertex = model_group.vertex_count;
763  wings_group.vertex_count = wings_vertex_count;
764  }
765 
766  // Calculate model bounding box
767  model->get_bounds() = calculate_bounds(vertex_buffer_data.data(), model_group.vertex_count, *position_attribute, model->get_vertex_stride());
768 
769  return model;
770 }
std::unique_ptr< render::model > ant_morphogenesis(const ant_phenome &phenome)
Generates a 3D model of an ant given its phenome.
void generate_ant_skeleton(skeleton &skeleton, ant_bone_set &bones, const ant_phenome &phenome)
Generates a skeleton for an ant model.
std::uint16_t bone_index_type
Bone index type.
Definition: bone.hpp:31
Vertex buffer object (VBO).
constexpr std::size_t size() const noexcept
Returns the size of the buffer's data, in bytes.
void read(std::size_t offset, std::span< std::byte > data) const
Reads a subset of the buffer's data from the GL and returns it to the application.
const bone_transform_type & get_relative_transform(bone_index_type index) const
Returns the relative transform describing a bone pose.
Definition: pose.hpp:116
const bone_transform_type & get_absolute_transform(bone_index_type index) const
Returns the absolute transform describing a bone pose.
Definition: pose.hpp:128
const std::shared_ptr< gl::vertex_buffer > & get_vertex_buffer() const noexcept
Returns the vertex buffer associated with this model.
Definition: model.hpp:96
constexpr std::size_t get_vertex_stride() const noexcept
Returns the byte stride between consecutive elements within the vertex buffer.
Definition: model.hpp:113
const std::vector< model_group > & get_groups() const noexcept
Returns the model's model groups.
Definition: model.hpp:136
const std::shared_ptr< gl::vertex_array > & get_vertex_array() const noexcept
Returns the vertex array associated with this model.
Definition: model.hpp:82
Skeleton rest pose.
Definition: rest-pose.hpp:29
Skeletal animation skeleton.
Definition: skeleton.hpp:35
std::optional< bone_index_type > get_bone_index(hash::fnv1a32_t name) const
Finds the index of a bone from the bone's name.
Definition: skeleton.cpp:113
const rest_pose & get_rest_pose() const noexcept
Returns the skeleton's rest pose.
Definition: skeleton.hpp:187
format
Image and vertex formats.
Definition: format.hpp:29
@ static_draw
Data will be modified once, by the application, and used many times, for drawing commands.
@ triangle_list
Separate triangle primitives.
quaternion< T > normalize(const quaternion< T > &q)
Normalizes a quaternion.
Definition: quaternion.hpp:679
T length(const quaternion< T > &q)
Calculates the length of a quaternion.
Definition: quaternion.hpp:602
constexpr vector< T, 3 > cross(const vector< T, 3 > &x, const vector< T, 3 > &y) noexcept
Calculates the cross product of two vectors.
Definition: vector.hpp:1095
vector< T, N > sqrt(const vector< T, N > &x)
Takes the square root of each element.
@ uv
Vertex UV texture coordinates (vec2)
std::uint8_t total_antennomere_count
Total number of antennal segments per antenna.
std::shared_ptr< render::model > model
3D model of the antennae.
Set of bone indices for all possible bones in an ant skeleotn.
bone_index_type head
bone_index_type mandible_r
bone_index_type antennomere2_l
bone_index_type procoxa_r
bone_index_type protarsomere1_l
bone_index_type mesosoma
bone_index_type mesotarsomere1_l
std::optional< bone_index_type > hindwing_l
std::optional< bone_index_type > postpetiole
bone_index_type mesofemur_l
bone_index_type antennomere1_l
bone_index_type protibia_r
bone_index_type profemur_l
bone_index_type profemur_r
bone_index_type metatibia_r
bone_index_type mesotarsomere1_r
bone_index_type mesocoxa_l
std::optional< bone_index_type > forewing_l
bone_index_type mesotibia_r
bone_index_type mandible_l
std::optional< bone_index_type > sting
bone_index_type metafemur_l
std::optional< bone_index_type > forewing_r
bone_index_type mesocoxa_r
bone_index_type protarsomere1_r
bone_index_type mesofemur_r
bone_index_type metatibia_l
std::optional< bone_index_type > petiole
bone_index_type gaster
bone_index_type metacoxa_r
bone_index_type metatarsomere1_l
bone_index_type protibia_l
std::optional< bone_index_type > hindwing_r
bone_index_type antennomere2_r
bone_index_type metacoxa_l
bone_index_type procoxa_l
bone_index_type mesotibia_l
bone_index_type antennomere1_r
bone_index_type metafemur_r
bone_index_type metatarsomere1_r
std::shared_ptr< render::model > model
3D model of the eyes, if present.
std::uint32_t ommatidia_count
Number of ommatidia.
bool present
Indicates whether eyes are present.
std::shared_ptr< render::model > model
3D model of the gaster.
std::shared_ptr< render::model > model
3D model of the head.
std::shared_ptr< render::model > model
3D model of the legs.
std::shared_ptr< render::model > model
3D model of the mandibles.
std::shared_ptr< render::model > model
3D model of the mesosoma.
bool lateral_ocelli_present
Lateral ocelli present.
bool median_ocellus_present
Median ocellus present.
std::shared_ptr< render::model > model
3D model of the ocelli, if present.
Complete set of ant phenes.
Definition: ant-phenome.hpp:52
const ant_sting_phene * sting
Definition: ant-phenome.hpp:83
const ant_sculpturing_phene * sculpturing
Definition: ant-phenome.hpp:82
const ant_mandibles_phene * mandibles
Definition: ant-phenome.hpp:76
const ant_legs_phene * legs
Definition: ant-phenome.hpp:75
const ant_pigmentation_phene * pigmentation
Definition: ant-phenome.hpp:80
const ant_ocelli_phene * ocelli
Definition: ant-phenome.hpp:79
const ant_antennae_phene * antennae
Definition: ant-phenome.hpp:64
const ant_wings_phene * wings
Definition: ant-phenome.hpp:85
const ant_waist_phene * waist
Definition: ant-phenome.hpp:84
const ant_head_phene * head
Definition: ant-phenome.hpp:73
const ant_eyes_phene * eyes
Definition: ant-phenome.hpp:69
const ant_gaster_phene * gaster
Definition: ant-phenome.hpp:72
const ant_mesosoma_phene * mesosoma
Definition: ant-phenome.hpp:77
std::shared_ptr< render::material > material
Pigmentation material.
float roughness
Surface roughness.
std::shared_ptr< gl::texture_2d > normal_map
Surface culpturing normal map.
bool present
Indicates whether a sting present or not.
std::shared_ptr< render::model > model
3D model of the sting.
bool postpetiole_present
Postpetiole presence.
std::shared_ptr< render::model > model
3D model of the waist.
std::shared_ptr< render::model > model
3D model of the wings.
bool present
Wings presence.
n-dimensional axis-aligned rectangle.
void extend(const vector_type &point) noexcept
Extends the hyperrectangle to include a point.
std::uint32_t offset
Byte offset of this attribute relative to the start of an element in the vertex input binding.
32-bit FNV-1a hash value.
Definition: fnv1a.hpp:117
n-dimensional vector.
Definition: vector.hpp:44
static constexpr vector infinity() noexcept
Returns a vector of infinities, where every element is equal to infinity.
Definition: vector.hpp:342
Part of a model which is associated with exactly one material.
Definition: model.hpp:41
std::shared_ptr< render::material > material
Definition: model.hpp:46
gl::primitive_topology primitive_topology
Definition: model.hpp:43
std::uint32_t vertex_count
Definition: model.hpp:45
std::uint32_t first_vertex
Definition: model.hpp:44
hash::fnv1a32_t id
Definition: model.hpp:42