4#include "glm/gtc/epsilon.hpp"
10#include <meshoptimizer/src/meshoptimizer.h>
28auto hf_height_at(hpp::span<const float> heights, uint32_t vx, int32_t sx, int32_t sz, int32_t ix, int32_t iz) ->
double
30 ix = std::clamp(ix, 0, sx);
31 iz = std::clamp(iz, 0, sz);
32 const size_t idx =
static_cast<size_t>(iz) * vx +
static_cast<size_t>(ix);
33 return static_cast<double>(heights[idx]);
36auto hf_dh_dt0(hpp::span<const float> heights, uint32_t vx, int32_t sx, int32_t sz, int32_t ix, int32_t iz) ->
double
44 return static_cast<double>(sx) * (hf_height_at(heights, vx, sx, sz, 1, iz) - hf_height_at(heights, vx, sx, sz, 0, iz));
48 return static_cast<double>(sx) *
49 (hf_height_at(heights, vx, sx, sz, sx, iz) - hf_height_at(heights, vx, sx, sz, sx - 1, iz));
51 return static_cast<double>(sx) * 0.5 *
52 (hf_height_at(heights, vx, sx, sz, ix + 1, iz) - hf_height_at(heights, vx, sx, sz, ix - 1, iz));
55auto hf_dh_dt1(hpp::span<const float> heights, uint32_t vx, int32_t sx, int32_t sz, int32_t ix, int32_t iz) ->
double
63 return static_cast<double>(sz) * (hf_height_at(heights, vx, sx, sz, ix, 1) - hf_height_at(heights, vx, sx, sz, ix, 0));
67 return static_cast<double>(sz) *
68 (hf_height_at(heights, vx, sx, sz, ix, sz) - hf_height_at(heights, vx, sx, sz, ix, sz - 1));
70 return static_cast<double>(sz) * 0.5 *
71 (hf_height_at(heights, vx, sx, sz, ix, iz + 1) - hf_height_at(heights, vx, sx, sz, ix, iz - 1));
76 mesh::preparation_data& data,
80 bool has_position =
format.has(gfx::attribute::Position);
81 bool has_texcoord0 =
format.has(gfx::attribute::TexCoord0);
82 bool has_normals =
format.has(gfx::attribute::Normal);
83 bool has_tangents =
format.has(gfx::attribute::Tangent);
84 bool has_bitangents =
format.has(gfx::attribute::Bitangent);
85 uint16_t vertex_stride =
format.getStride();
89 data.triangle_count = uint32_t(triangle_count);
90 data.vertex_count = uint32_t(vertex_count);
93 data.vertex_data.resize(data.vertex_count * vertex_stride);
94 data.vertex_flags.resize(data.vertex_count);
95 data.triangle_data.resize(data.triangle_count);
96 mesh::submesh submesh;
97 submesh.data_group_id = 0;
98 submesh.face_count = data.triangle_count;
99 submesh.face_start = 0;
100 submesh.vertex_count = data.vertex_count;
101 submesh.vertex_start = 0;
103 uint8_t* current_vertex_ptr = data.vertex_data.data();
108 math::vec4
normal = math::vec4(
v.normal, 0.0f);
109 math::vec2 texcoords0 =
v.tex_coord;
114 gfx::attribute::Position,
121 gfx::attribute::Normal,
128 gfx::attribute::TexCoord0,
138 for(
const auto& triangle : mesh.
triangles())
140 const auto&
indices = triangle.vertices;
141 auto& tri = data.triangle_data[tri_idx];
142 tri.indices[0] = uint32_t(
indices[0]);
143 tri.indices[1] = uint32_t(
indices[1]);
144 tri.indices[2] = uint32_t(
indices[2]);
150 data.compute_binormals = has_bitangents;
151 data.compute_tangents = has_tangents;
153 data.submeshes.emplace_back(submesh);
204 for(
auto& lod :
lods_)
207 for(
auto*
submesh : lod.submeshes_)
211 lod.submeshes_.clear();
214 lod.hardware_ib_.reset();
259 for(
size_t i = 0; i <
lods_.size(); ++i)
261 const auto& lod =
lods_[i];
263 .triangles = lod.face_count_,
264 .percent = (
static_cast<float>(lod.face_count_) /
static_cast<float>(
face_count_)) * 100.0f,
304 APPLOG_ERROR(
"Attempting to set a mesh vertex source without first calling "
305 "'prepareMesh' is not allowed.\n");
311 if(preparation_data_.owns_source)
315 preparation_data_.vertex_source =
nullptr;
316 preparation_data_.source_format = {};
317 preparation_data_.owns_source =
false;
318 preparation_data_.vertex_records.clear();
321 if(vertex_count == 0)
329 preparation_data_.source_format = source_format;
330 if(source_format.m_hash == vertex_format_.m_hash)
332 preparation_data_.vertex_source =
reinterpret_cast<uint8_t*
>(source.data());
337 preparation_data_.vertex_source =
new uint8_t[vertex_count * vertex_format_.getStride()];
338 preparation_data_.owns_source =
true;
340 preparation_data_.vertex_source,
342 reinterpret_cast<uint8_t*
>(source.data()),
349 if(!source_format.has(gfx::attribute::Normal) && vertex_format_.has(gfx::attribute::Normal))
351 preparation_data_.compute_normals =
true;
353 if(!source_format.has(gfx::attribute::Bitangent) && vertex_format_.has(gfx::attribute::Bitangent))
355 preparation_data_.compute_binormals =
true;
357 if(!source_format.has(gfx::attribute::Tangent) && vertex_format_.has(gfx::attribute::Tangent))
359 preparation_data_.compute_tangents =
true;
363 math::vec4 tangent{};
364 math::vec4 bitangent{};
365 gfx::vertex_unpack(math::value_ptr(
normal), gfx::attribute::Normal, vertex_format_, preparation_data_.vertex_source, 0);
366 gfx::vertex_unpack(math::value_ptr(tangent), gfx::attribute::Tangent, vertex_format_, preparation_data_.vertex_source, 0);
367 gfx::vertex_unpack(math::value_ptr(bitangent), gfx::attribute::Bitangent, vertex_format_, preparation_data_.vertex_source, 0);
368 if(math::epsilonEqual(math::length(
normal), 0.0f, math::epsilon<float>()))
370 preparation_data_.compute_normals =
true;
372 if(math::epsilonEqual(math::length(tangent), 0.0f, math::epsilon<float>()))
374 preparation_data_.compute_tangents =
true;
376 if(math::epsilonEqual(math::length(bitangent), 0.0f, math::epsilon<float>()))
378 preparation_data_.compute_binormals =
true;
381#ifdef SET_VERTICES_WHEN_SETTING_PRIMITIVES
383 preparation_data_.vertex_records.clear();
384 preparation_data_.vertex_records.resize(vertex_count);
389 memset(preparation_data_.vertex_records.data(), 0xFF, vertex_count *
sizeof(uint32_t));
391 preparation_data_.vertex_data = std::move(source);
392 preparation_data_.vertex_count = vertex_count;
413 APPLOG_ERROR(
"Attempting to add primitives to a mesh without first calling "
414 "'prepareMesh' is not allowed.\n");
419 preparation_data_.submeshes = submeshes;
431 APPLOG_ERROR(
"Attempting to add primitives to a mesh without first calling "
432 "'prepareMesh' is not allowed.\n");
437#ifdef SET_VERTICES_WHEN_SETTING_PRIMITIVES
439 preparation_data_.triangle_count = 0;
440 preparation_data_.triangle_data.clear();
443 bool has_position = vertex_format_.has(gfx::attribute::Position);
444 bool has_normal = vertex_format_.has(gfx::attribute::Normal);
445 uint16_t vertex_stride = vertex_format_.getStride();
450 bool source_has_normals = preparation_data_.source_format.has(gfx::attribute::Normal);
451 bool source_has_binormal = preparation_data_.source_format.has(gfx::attribute::Bitangent);
452 bool source_has_tangent = preparation_data_.source_format.has(gfx::attribute::Tangent);
456 uint8_t vertex_flags = 0;
457 if(source_has_normals)
459 vertex_flags |= preparation_data::source_contains_normal;
461 if(source_has_binormal)
463 vertex_flags |= preparation_data::source_contains_binormal;
465 if(source_has_tangent)
467 vertex_flags |= preparation_data::source_contains_tangent;
471 uint8_t* src_vertices_ptr = preparation_data_.vertex_source;
473 for(
const auto& src_tri : triangles)
477 if(preparation_data_.check_for_degenerates)
483 gfx::vertex_unpack(vf1, gfx::attribute::Position, vertex_format_, src_vertices_ptr, src_tri.indices[0]);
486 gfx::vertex_unpack(vf2, gfx::attribute::Position, vertex_format_, src_vertices_ptr, src_tri.indices[1]);
489 gfx::vertex_unpack(vf3, gfx::attribute::Position, vertex_format_, src_vertices_ptr, src_tri.indices[2]);
490 std::memcpy(&v1[0], vf1, 3 *
sizeof(
float));
491 std::memcpy(&v2[0], vf2, 3 *
sizeof(
float));
492 std::memcpy(&v3[0], vf3, 3 *
sizeof(
float));
495 if(math::all(math::equal(v1, v2, math::epsilon<float>())) ||
496 math::all(math::equal(v1, v3, math::epsilon<float>())) ||
497 math::all(math::equal(v2, v3, math::epsilon<float>())))
504 preparation_data_.triangle_count++;
505 preparation_data_.triangle_data.resize(preparation_data_.triangle_count);
506 triangle& triangle_data = preparation_data_.triangle_data[preparation_data_.triangle_count - 1];
512 for(uint32_t j = 0; j < 3; ++j)
515 uint32_t orig_index = src_tri.indices[j];
518 uint32_t
index = preparation_data_.vertex_records[orig_index];
521 if(
index == 0xFFFFFFFF)
525 index = preparation_data_.vertex_count++;
526 preparation_data_.vertex_records[orig_index] =
index;
529 size_t initial_size = preparation_data_.vertex_data.size();
530 preparation_data_.vertex_data.resize(initial_size + vertex_stride);
533 uint8_t* src_ptr = src_vertices_ptr + (orig_index * vertex_stride);
534 uint8_t* dst_ptr = &preparation_data_.vertex_data[initial_size];
535 std::memcpy(dst_ptr, src_ptr, vertex_stride);
538 preparation_data_.vertex_flags.push_back(vertex_flags);
557 bbox_.add_point(math::vec3(fpos[0], fpos[1], fpos[2]));
570 preparation_data_.triangle_count = triangles.size();
571 preparation_data_.triangle_data = std::move(triangles);
583 if(!bind_data.has_bones())
594 skin_bind_data_.clear();
595 skin_bind_data_ = bind_data;
598 skin_bind_data_.build_vertex_table(preparation_data_.vertex_count, preparation_data_.vertex_records, vertex_table);
599 skin_bind_data_.clear_vertex_influences();
604 bone_palettes_.clear();
608 bone_palettes_.reserve(preparation_data_.submeshes.size());
610 for(
size_t palette_id = 0; palette_id < preparation_data_.submeshes.size(); ++palette_id)
612 auto&
submesh = preparation_data_.submeshes[palette_id];
616 std::vector<uint32_t> faces;
623 for(uint32_t vertex_index : tri_data[i].
indices)
625 const auto& data = vertex_table[vertex_index];
626 for(
const auto& influence : data.influences)
629 used_bones[
static_cast<uint32_t
>(influence)] =
true;
641 bone_palettes_.push_back(new_palette);
647 for(uint32_t i = face_start; i < face_end; ++i)
649 for(uint32_t k = 0; k < 3; ++k)
651 uint32_t vertex_index = tri_data[i].indices[k];
652 auto& data = vertex_table[vertex_index];
655 if(data.palette == -1)
657 data.palette =
static_cast<int32_t
>(palette_id);
669 else if(data.palette !=
static_cast<int32_t
>(palette_id))
672 uint32_t new_index =
static_cast<uint32_t
>(vertex_table.size());
677 new_vertex.
palette =
static_cast<int32_t
>(palette_id);
678 vertex_table.push_back(new_vertex);
691 tri_data[i].indices[k] = new_index;
701 bool has_weights = new_format.has(gfx::attribute::Weight);
702 bool has_indices = new_format.has(gfx::attribute::Indices);
703 if(!has_weights || !has_indices)
705 new_format.m_hash = 0;
708 new_format.add(gfx::attribute::Weight, 4, gfx::attribute_type::Float);
712 new_format.add(gfx::attribute::Indices, 4, gfx::attribute_type::Float,
false,
true);
717 vertex_format_ = new_format;
721 uint16_t vertex_stride = vertex_format_.getStride();
724 uint32_t original_vertex_count = preparation_data_.vertex_count;
725 if(vertex_format_.m_hash != original_format.m_hash)
728 byte_array_t original_buffer(preparation_data_.vertex_data);
729 preparation_data_.vertex_data.clear();
730 preparation_data_.vertex_data.resize(vertex_table.size() * vertex_stride);
731 preparation_data_.vertex_flags.resize(vertex_table.size());
734 preparation_data_.vertex_data.data(),
736 original_buffer.data(),
737 original_vertex_count);
742 preparation_data_.vertex_data.resize(vertex_table.size() * vertex_stride);
743 preparation_data_.vertex_flags.resize(vertex_table.size());
747 uint8_t* src_vertices_ptr = preparation_data_.vertex_data.data();
748 for(
size_t i = 0; i < vertex_table.size(); ++i)
750 auto& data = vertex_table[i];
753 int32_t palette_id = data.palette;
761 const auto& palette = bone_palettes_[
static_cast<size_t>(palette_id)];
764 if(i >= original_vertex_count)
766 std::memcpy(src_vertices_ptr + (i * vertex_stride),
767 src_vertices_ptr + (data.original_vertex * vertex_stride),
771 preparation_data_.vertex_flags[i] = preparation_data_.vertex_flags[data.original_vertex];
774 uint32_t max_bones = std::min<uint32_t>(4, uint32_t(data.influences.size()));
780 math::vec4 blend_weights(0.0f, 0.0f, 0.0f, 0.0f);
781 math::vec4 blend_indices(0.0f, 0.0f, 0.0f, 0.0f);
783 for(uint32_t j = 0; j < max_bones; ++j)
786 uint32_t palette_bone_index =
787 palette.translate_bone_to_palette(
static_cast<uint32_t
>(data.influences[j]));
789 blend_indices[
static_cast<math::vec4::length_type
>(j)] =
static_cast<float>(palette_bone_index);
790 blend_weights[
static_cast<math::vec4::length_type
>(j)] = data.weights[j];
795 gfx::attribute::Weight,
802 gfx::attribute::Indices,
810 preparation_data_.vertex_count =
static_cast<uint32_t
>(vertex_table.size());
820 root_ = std::move(root);
828 default_material_uids_ = std::move(data.default_material_uids);
830 const bool has_skin_data = data.skin_data.has_bones();
831 const bool skin_is_prepared = data.skin_is_prepared;
832 auto bone_palette_bones = std::move(data.bone_palette_bones);
835 result &= prepare_mesh(data.vertex_format);
836 result &= set_bounding_box(data.bbox);
837 result &= set_vertex_source(std::move(data.vertex_data), data.vertex_count, data.vertex_format);
838 result &= set_primitives(std::move(data.triangle_data));
839 result &= set_submeshes(data.submeshes);
840 if(has_skin_data && skin_is_prepared)
842 skin_bind_data_.clear();
843 skin_bind_data_ = data.skin_data;
847 result &= bind_skin(data.skin_data);
849 result &= bind_armature(data.root_node);
850 result &= end_prepare();
853 if(result && has_skin_data && skin_is_prepared)
855 bone_palettes_.clear();
857 bone_palettes_.reserve(data.submeshes.size());
858 for(
size_t palette_id = 0; palette_id < data.submeshes.size(); ++palette_id)
862 if(palette_id < bone_palette_bones.size())
866 bone_palettes_.push_back(std::move(palette));
873 result &= restore_lods_from_load_data(data);
882 uint32_t width_segments,
883 uint32_t height_segments,
885 bool hardware_copy ) ->
bool
892 math::quat rot1(math::vec3(math::radians(-90.0f), 0.f, 0.0f));
893 math::quat rot2(math::vec3(math::radians(90.0f), 0.f, 0.0f));
895 auto plane1 = rotate_mesh(plane, rot1);
896 auto plane2 = rotate_mesh(plane, rot2);
897 auto mesh = merge_mesh(plane1, plane2);
899 create_mesh(vertex_format_,
mesh, preparation_data_, bbox_);
901 return end_prepare(hardware_copy);
905 hpp::span<const float> heights,
912 bool hardware_copy) ->
bool
915 const uint32_t sx = segments_x;
916 const uint32_t sz = segments_z;
917 if(sx < 1u || sz < 1u)
921 const uint32_t vx = sx + 1u;
922 if(heights.size() <
static_cast<size_t>(vx) *
static_cast<size_t>(sz + 1u))
929 const double hx =
static_cast<double>(half_extent_x);
930 const double hz =
static_cast<double>(half_extent_z);
931 const double hs =
static_cast<double>(height_scale);
932 const int32_t isx =
static_cast<int32_t
>(sx);
933 const int32_t isz =
static_cast<int32_t
>(sz);
937 const double fx = t[0] *
static_cast<double>(sx);
938 const double fz = t[1] *
static_cast<double>(sz);
939 int32_t ix =
static_cast<int32_t
>(std::lround(fx));
940 int32_t iz =
static_cast<int32_t
>(std::lround(fz));
941 ix = std::clamp(ix, 0, isx);
942 iz = std::clamp(iz, 0, isz);
946 v.position[1] = hs * hf_height_at(heights, vx, isx, isz, ix, iz);
947 v.position[2] = (t[1] - 0.5) * 2.0 * hz;
948 v.tex_coord[0] = t[0];
949 v.tex_coord[1] = t[1];
951 const gml::dvec3 dr_dt0{2.0 * hx, hs * hf_dh_dt0(heights, vx, isx, isz, ix, iz), 0.0};
952 const gml::dvec3 dr_dt1{0.0, hs * hf_dh_dt1(heights, vx, isx, isz, ix, iz), 2.0 * hz};
953 v.normal = -gml::normalize(gml::cross(dr_dt1, dr_dt0));
959 math::quat rot(math::vec3(math::radians(-180.0f), 0.f, 0.0f));
960 auto mesh = rotate_mesh(hf_mesh, rot);
961 create_mesh(vertex_format_,
mesh, preparation_data_, bbox_);
963 return end_prepare(hardware_copy);
970 uint32_t width_segments,
971 uint32_t height_segments,
972 uint32_t depth_segments,
974 bool hardware_copy ) ->
bool
981 math::quat rot(math::vec3(math::radians(-90.0f), 0.f, 0.0f));
982 auto mesh = rotate_mesh(
box, rot);
984 create_mesh(vertex_format_,
mesh, preparation_data_, bbox_);
986 return end_prepare(hardware_copy);
994 uint32_t width_segments,
995 uint32_t height_segments,
996 uint32_t depth_segments,
998 bool hardware_copy ) ->
bool
1005 math::quat rot(math::vec3(math::radians(-90.0f), 0.f, 0.0f));
1006 auto mesh = rotate_mesh(rounded_box, rot);
1008 create_mesh(vertex_format_,
mesh, preparation_data_, bbox_);
1010 return end_prepare(hardware_copy);
1018 bool hardware_copy ) ->
bool
1025 math::quat rot(math::vec3(math::radians(-90.0f), 0.f, 0.0f));
1028 create_mesh(vertex_format_,
mesh, preparation_data_, bbox_);
1030 return end_prepare(hardware_copy);
1039 bool hardware_copy ) ->
bool
1050 math::quat rot(math::vec3(math::radians(-90.0f), 0.f, 0.0f));
1051 auto mesh = rotate_mesh(cylinder, rot);
1053 create_mesh(vertex_format_,
mesh, preparation_data_, bbox_);
1055 return end_prepare(hardware_copy);
1064 bool hardware_copy ) ->
bool
1070 capsule_mesh_t capsule(radius,
height * 0.5,
static_cast<int>(slices),
static_cast<int>(stacks));
1071 math::quat rot(math::vec3(math::radians(-90.0f), 0.f, 0.0f));
1072 auto mesh = rotate_mesh(capsule, rot);
1074 create_mesh(vertex_format_,
mesh, preparation_data_, bbox_);
1076 return end_prepare(hardware_copy);
1086 bool hardware_copy ) ->
bool
1092 capped_cone_mesh_t cone(radius, 1.0,
static_cast<int>(stacks),
static_cast<int>(slices));
1093 math::quat rot(math::vec3(math::radians(-90.0f), 0.f, 0.0f));
1094 auto mesh = rotate_mesh(cone, rot);
1096 create_mesh(vertex_format_,
mesh, preparation_data_, bbox_);
1098 return end_prepare(hardware_copy);
1107 bool hardware_copy ) ->
bool
1113 torus_mesh_t torus(inner_radius, outer_radius,
static_cast<int>(sides),
static_cast<int>(bands));
1114 math::quat rot(math::vec3(math::radians(-90.0f), 0.f, 0.0f));
1115 auto mesh = rotate_mesh(torus, rot);
1117 create_mesh(vertex_format_,
mesh, preparation_data_, bbox_);
1119 return end_prepare(hardware_copy);
1129 math::quat rot(math::vec3(math::radians(-90.0f), 0.f, 0.0f));
1130 auto mesh = rotate_mesh(teapot, rot);
1132 create_mesh(vertex_format_,
mesh, preparation_data_, bbox_);
1134 return end_prepare(hardware_copy);
1144 math::quat rot(math::vec3(math::radians(-90.0f), 0.f, 0.0f));
1145 auto mesh = rotate_mesh(icosahedron, rot);
1147 create_mesh(vertex_format_,
mesh, preparation_data_, bbox_);
1149 return end_prepare(hardware_copy);
1159 math::quat rot(math::vec3(math::radians(-90.0f), 0.f, 0.0f));
1160 auto mesh = rotate_mesh(dodecahedron, rot);
1162 create_mesh(vertex_format_,
mesh, preparation_data_, bbox_);
1164 return end_prepare(hardware_copy);
1175 math::quat rot(math::vec3(math::radians(-90.0f), 0.f, 0.0f));
1176 auto mesh = rotate_mesh(icosphere, rot);
1178 create_mesh(vertex_format_,
mesh, preparation_data_, bbox_);
1180 return end_prepare(hardware_copy);
1186 uint16_t position_offset =
vertex_format_.getOffset(gfx::attribute::Position);
1204 std::memcpy(&v1[0], vf1, 3 *
sizeof(
float));
1205 std::memcpy(&v2[0], vf2, 3 *
sizeof(
float));
1206 std::memcpy(&v3[0], vf3, 3 *
sizeof(
float));
1208 math::vec3 c = math::cross(v2 - v1, v3 - v1);
1209 if(math::length2(c) < (4.0f * 0.000001f * 0.000001f))
1225 APPLOG_ERROR(
"Attempting to call 'end_prepare' on a mesh without first "
1226 "calling 'prepare_mesh' is not "
1233 check_for_degenerates();
1238 if(!generate_vertex_components(weld))
1244 vertex_count_ = preparation_data_.vertex_count;
1245 system_vb_ =
new uint8_t[vertex_count_ * vertex_format_.getStride()];
1248 std::memcpy(system_vb_, preparation_data_.vertex_data.data(), vertex_count_ * vertex_format_.getStride());
1249 preparation_data_.vertex_data.clear();
1250 preparation_data_.vertex_flags.clear();
1251 preparation_data_.vertex_count = 0;
1256 build_vb(hardware_copy);
1260 face_count_ = preparation_data_.triangle_count;
1261 system_ib_ =
new uint32_t[face_count_ * 3];
1265 if(!sort_mesh_data())
1273 build_ib(hardware_copy);
1276 if(preparation_data_.owns_source)
1280 preparation_data_.vertex_source =
nullptr;
1284 hardware_mesh_ = hardware_copy;
1285 optimize_mesh_ = optimize;
1301 const uint16_t vb_flags =
1302 BGFX_BUFFER_COMPUTE_READ | BGFX_BUFFER_COMPUTE_FORMAT_32X1 | BGFX_BUFFER_COMPUTE_TYPE_FLOAT;
1316 auto buffer_size =
static_cast<uint32_t
>(size_t(
face_count_ * 3) *
sizeof(uint32_t));
1320 const uint16_t ib_flags = BGFX_BUFFER_INDEX32
1321 | BGFX_BUFFER_COMPUTE_READ
1322 | BGFX_BUFFER_COMPUTE_FORMAT_32X1
1323 | BGFX_BUFFER_COMPUTE_TYPE_UINT;
1329 hardware_ib_ = std::make_shared<gfx::index_buffer>(mem, ib_flags);
1333 auto ib = std::static_pointer_cast<gfx::index_buffer>(
hardware_ib_);
1337 hardware_ib_ = std::make_shared<gfx::index_buffer>(mem, ib_flags);
1346 std::map<adjacent_edge_key, uint32_t> edge_tree;
1347 std::map<adjacent_edge_key, uint32_t>::iterator it_edge;
1353 if(preparation_data_.triangle_count == 0)
1359 uint16_t position_offset = vertex_format_.getOffset(gfx::attribute::Position);
1360 uint16_t vertex_stride = vertex_format_.getStride();
1363 uint8_t* src_vertices_ptr = preparation_data_.vertex_data.data() + position_offset;
1364 for(uint32_t i = 0; i < preparation_data_.triangle_count; ++i)
1369 const triangle& tri = preparation_data_.triangle_data[i];
1374 const math::vec3* v1 =
1375 reinterpret_cast<const math::vec3*
>(src_vertices_ptr + (tri.
indices[0] * vertex_stride));
1376 const math::vec3* v2 =
1377 reinterpret_cast<const math::vec3*
>(src_vertices_ptr + (tri.
indices[1] * vertex_stride));
1378 const math::vec3* v3 =
1379 reinterpret_cast<const math::vec3*
>(src_vertices_ptr + (tri.
indices[2] * vertex_stride));
1384 edge_tree[edge] = i;
1389 edge_tree[edge] = i;
1394 edge_tree[edge] = i;
1399 adjacency.resize(preparation_data_.triangle_count * 3, 0xFFFFFFFF);
1402 for(uint32_t i = 0; i < preparation_data_.triangle_count; ++i)
1407 const triangle& tri = preparation_data_.triangle_data[i];
1414 const math::vec3* v1 =
1415 reinterpret_cast<const math::vec3*
>(src_vertices_ptr + (tri.
indices[0] * vertex_stride));
1416 const math::vec3* v2 =
1417 reinterpret_cast<const math::vec3*
>(src_vertices_ptr + (tri.
indices[1] * vertex_stride));
1418 const math::vec3* v3 =
1419 reinterpret_cast<const math::vec3*
>(src_vertices_ptr + (tri.
indices[2] * vertex_stride));
1431 it_edge = edge_tree.find(edge);
1432 if(it_edge != edge_tree.end())
1434 adjacency[(i * 3)] = it_edge->second;
1442 it_edge = edge_tree.find(edge);
1443 if(it_edge != edge_tree.end())
1445 adjacency[(i * 3) + 1] = it_edge->second;
1453 it_edge = edge_tree.find(edge);
1454 if(it_edge != edge_tree.end())
1456 adjacency[(i * 3) + 2] = it_edge->second;
1465 if(face_count_ == 0)
1471 uint16_t position_offset = vertex_format_.getOffset(gfx::attribute::Position);
1472 uint16_t vertex_stride = vertex_format_.getStride();
1475 uint8_t* src_vertices_ptr = system_vb_ + position_offset;
1476 uint32_t* src_indices_ptr = system_ib_;
1477 for(uint32_t i = 0; i < face_count_; ++i, src_indices_ptr += 3)
1483 reinterpret_cast<const math::vec3*
>(src_vertices_ptr + (src_indices_ptr[0] * vertex_stride));
1485 reinterpret_cast<const math::vec3*
>(src_vertices_ptr + (src_indices_ptr[1] * vertex_stride));
1487 reinterpret_cast<const math::vec3*
>(src_vertices_ptr + (src_indices_ptr[2] * vertex_stride));
1492 edge_tree[edge] = i;
1497 edge_tree[edge] = i;
1502 edge_tree[edge] = i;
1507 adjacency.resize(face_count_ * 3, 0xFFFFFFFF);
1510 src_indices_ptr = system_ib_;
1511 for(uint32_t i = 0; i < face_count_; ++i, src_indices_ptr += 3)
1516 const math::vec3* v1 =
1517 reinterpret_cast<const math::vec3*
>(src_vertices_ptr + (src_indices_ptr[0] * vertex_stride));
1518 const math::vec3* v2 =
1519 reinterpret_cast<const math::vec3*
>(src_vertices_ptr + (src_indices_ptr[1] * vertex_stride));
1520 const math::vec3* v3 =
1521 reinterpret_cast<const math::vec3*
>(src_vertices_ptr + (src_indices_ptr[2] * vertex_stride));
1533 it_edge = edge_tree.find(edge);
1534 if(it_edge != edge_tree.end())
1536 adjacency[(i * 3)] = it_edge->second;
1544 it_edge = edge_tree.find(edge);
1545 if(it_edge != edge_tree.end())
1547 adjacency[(i * 3) + 1] = it_edge->second;
1555 it_edge = edge_tree.find(edge);
1556 if(it_edge != edge_tree.end())
1558 adjacency[(i * 3) + 2] = it_edge->second;
1614 return std::static_pointer_cast<gfx::vertex_buffer>(
hardware_vb_);
1619 if(lod_index == 0 || lods_.empty())
1621 return std::static_pointer_cast<gfx::index_buffer>(hardware_ib_);
1624 if(lod_index <= lods_.size())
1626 return std::static_pointer_cast<gfx::index_buffer>(lods_[lod_index - 1].hardware_ib_);
1629 return std::static_pointer_cast<gfx::index_buffer>(hardware_ib_);
1649void accumulate_submesh_node_transforms(
const std::unique_ptr<mesh::armature_node>& node,
1651 std::vector<math::transform>& out)
1658 for(
auto submesh_index : node->submeshes)
1660 if(submesh_index < out.size())
1662 out[submesh_index] =
global;
1665 for(
const auto& child : node->children)
1667 accumulate_submesh_node_transforms(child,
global, out);
1674 const size_t submesh_count = get_submeshes_count(lod_index);
1675 std::vector<math::transform> transforms(submesh_count);
1676 accumulate_submesh_node_transforms(root_,
math::transform{}, transforms);
1683 math::vec3 cen = bounds.get_center();
1684 math::vec3 ext = bounds.get_extents();
1686 const auto view_proj = cam.get_view_projection();
1687 const auto& viewport_size = cam.get_viewport_size();
1688 const auto& viewport_pos = cam.get_viewport_pos();
1689 const float near_plane_epsilon = 0.001f;
1691 std::array<math::vec3, 8> corners = {{
1692 math::vec3(cen.x - ext.x, cen.y - ext.y, cen.z - ext.z),
1693 math::vec3(cen.x + ext.x, cen.y - ext.y, cen.z - ext.z),
1694 math::vec3(cen.x - ext.x, cen.y - ext.y, cen.z + ext.z),
1695 math::vec3(cen.x + ext.x, cen.y - ext.y, cen.z + ext.z),
1696 math::vec3(cen.x - ext.x, cen.y + ext.y, cen.z - ext.z),
1697 math::vec3(cen.x + ext.x, cen.y + ext.y, cen.z - ext.z),
1698 math::vec3(cen.x - ext.x, cen.y + ext.y, cen.z + ext.z),
1699 math::vec3(cen.x + ext.x, cen.y + ext.y, cen.z + ext.z),
1703 constexpr std::array<std::pair<int, int>, 12> edges = {{
1704 {0, 1}, {2, 3}, {4, 5}, {6, 7},
1705 {0, 2}, {1, 3}, {4, 6}, {5, 7},
1706 {0, 4}, {1, 5}, {2, 6}, {3, 7}
1709 math::vec2 min = math::vec2(std::numeric_limits<float>::max());
1710 math::vec2 max = math::vec2(std::numeric_limits<float>::lowest());
1711 bool has_valid_point =
false;
1714 std::array<math::vec4, 8> clip_coords;
1715 std::array<bool, 8> is_visible;
1717 for(
int i = 0; i < 8; ++i)
1719 clip_coords[i] = view_proj * math::vec4{corners[i].x, corners[i].y, corners[i].z, 1.0f};
1720 is_visible[i] = clip_coords[i].w > near_plane_epsilon;
1724 const float recip_w = 1.0f / clip_coords[i].w;
1725 const float ndc_x = clip_coords[i].x * recip_w;
1726 const float ndc_y = clip_coords[i].y * recip_w;
1728 math::vec2 screen_point;
1729 screen_point.x = ((ndc_x * 0.5f) + 0.5f) *
float(viewport_size.width) + float(viewport_pos.x);
1730 screen_point.y = ((ndc_y * -0.5f) + 0.5f) *
float(viewport_size.height) + float(viewport_pos.y);
1732 min = math::min(min, screen_point);
1733 max = math::max(max, screen_point);
1734 has_valid_point =
true;
1739 for(
const auto& edge : edges)
1741 const int idx0 = edge.first;
1742 const int idx1 = edge.second;
1744 const bool v0_visible = is_visible[idx0];
1745 const bool v1_visible = is_visible[idx1];
1747 if(v0_visible == v1_visible)
1752 const math::vec4& clip0 = clip_coords[idx0];
1753 const math::vec4& clip1 = clip_coords[idx1];
1755 const float w0 = clip0.w;
1756 const float w1 = clip1.w;
1758 const float t = (near_plane_epsilon - w0) / (w1 - w0);
1760 if(t >= 0.0f && t <= 1.0f)
1762 const math::vec4 clipped_clip = clip0 + t * (clip1 - clip0);
1764 const float recip_w = 1.0f / clipped_clip.w;
1765 const float ndc_x = clipped_clip.x * recip_w;
1766 const float ndc_y = clipped_clip.y * recip_w;
1768 math::vec2 screen_point;
1769 screen_point.x = ((ndc_x * 0.5f) + 0.5f) *
float(viewport_size.width) + float(viewport_pos.x);
1770 screen_point.y = ((ndc_y * -0.5f) + 0.5f) *
float(viewport_size.height) + float(viewport_pos.y);
1772 min = math::min(min, screen_point);
1773 max = math::max(max, screen_point);
1774 has_valid_point =
true;
1778 if(!has_valid_point)
1780 min = math::vec2(
float(viewport_pos.x),
float(viewport_pos.y));
1781 max = math::vec2(
float(viewport_pos.x + viewport_size.width),
float(viewport_pos.y + viewport_size.height));
1793 math::vec3 cen = bounds.get_center();
1794 math::vec3 ext = bounds.get_extents();
1796 const auto view_proj = cam.get_view_projection();
1797 const auto& viewport_size = cam.get_viewport_size();
1798 const auto& viewport_pos = cam.get_viewport_pos();
1800 std::array<math::vec3, 8> corners = {{
1801 math::vec3(cen.x - ext.x, cen.y - ext.y, cen.z - ext.z),
1802 math::vec3(cen.x + ext.x, cen.y - ext.y, cen.z - ext.z),
1803 math::vec3(cen.x - ext.x, cen.y - ext.y, cen.z + ext.z),
1804 math::vec3(cen.x + ext.x, cen.y - ext.y, cen.z + ext.z),
1805 math::vec3(cen.x - ext.x, cen.y + ext.y, cen.z - ext.z),
1806 math::vec3(cen.x + ext.x, cen.y + ext.y, cen.z - ext.z),
1807 math::vec3(cen.x - ext.x, cen.y + ext.y, cen.z + ext.z),
1808 math::vec3(cen.x + ext.x, cen.y + ext.y, cen.z + ext.z),
1811 math::vec2 min = math::vec2(std::numeric_limits<float>::max());
1812 math::vec2 max = math::vec2(std::numeric_limits<float>::lowest());
1813 int valid_count = 0;
1814 int behind_count = 0;
1816 for(
const auto& corner : corners)
1818 math::vec4 clip = view_proj * math::vec4{corner.x, corner.y, corner.z, 1.0f};
1821 if(clip.w <= 0.001f)
1828 const float recip_w = 1.0f / clip.w;
1829 const float ndc_x = clip.x * recip_w;
1830 const float ndc_y = clip.y * recip_w;
1833 math::vec2 screen_point;
1834 screen_point.x = ((ndc_x * 0.5f) + 0.5f) *
float(viewport_size.width) + float(viewport_pos.x);
1835 screen_point.y = ((ndc_y * -0.5f) + 0.5f) *
float(viewport_size.height) + float(viewport_pos.y);
1837 min = math::min(min, screen_point);
1838 max = math::max(max, screen_point);
1844 if(behind_count > 0 && valid_count > 0)
1846 min.x = float(viewport_pos.x);
1847 min.y = float(viewport_pos.y);
1848 max.x = float(viewport_pos.x + viewport_size.width);
1849 max.y = float(viewport_pos.y + viewport_size.height);
1851 else if(valid_count == 0)
1854 min = math::vec2(
float(viewport_pos.x),
float(viewport_pos.y));
1855 max = math::vec2(
float(viewport_pos.x + viewport_size.width),
float(viewport_pos.y + viewport_size.height));
1868 return mesh_submeshes_;
1871 if(lod_index > 0 && lod_index <= lods_.size())
1873 return lods_[lod_index - 1].submeshes_;
1877 return mesh_submeshes_;
1884 return mesh_submeshes_.size();
1887 if(lod_index > 0 && lod_index <= lods_.size())
1889 return lods_[lod_index - 1].submeshes_.size();
1893 return mesh_submeshes_.size();
1898 const auto& submeshes = get_submeshes(lod_index);
1899 if(submesh_index < submeshes.size())
1901 return submeshes[submesh_index];
1908 const auto& submeshes = get_submeshes(lod_index);
1910 for(
const auto&
submesh : submeshes)
1929 const auto& submeshes = get_submeshes(lod_index);
1930 for(
size_t i = 0; i < submeshes.size(); ++i)
1932 if(submeshes[i] !=
nullptr && submeshes[i]->stable_id == stable_id)
1934 return static_cast<int>(i);
1951 return &mesh_submeshes_;
1954 if(lod_index > 0 && lod_index <= lods_.size())
1956 return &lods_[lod_index - 1].submeshes_;
1969 if(lod_index > 0 && lod_index <= lods_.size())
1971 return lods_[lod_index - 1].face_count_;
1982 if(!system_ib_ || face_count_ == 0)
1986 out_indices.resize(face_count_ * 3);
1987 std::memcpy(out_indices.data(), system_ib_, face_count_ * 3 *
sizeof(uint32_t));
1992 if(lod_index > 0 && lod_index <= lods_.size())
1994 const auto& lod = lods_[lod_index - 1];
1995 if(!lod.system_ib_ || lod.face_count_ == 0)
1999 out_indices.resize(lod.face_count_ * 3);
2000 std::memcpy(out_indices.data(), lod.system_ib_, lod.face_count_ * 3 *
sizeof(uint32_t));
2001 out_error = lod.simplification_error_;
2010 return 1 + get_max_generated_lod_count();
2020 std::vector<std::pair<size_t, float>> lod_configs;
2022 auto max_lod_count = get_max_generated_lod_count();
2023 for(uint32_t i = 1; i <= max_lod_count; ++i)
2026 size_t base_tri_count = data.triangle_count;
2027 lod_configs.push_back({base_tri_count / (1 << i), target_error * i});
2035 if(!data.skin_data.has_bones())
2042 data.skin_data.build_vertex_table(data.vertex_count, {}, vertex_table);
2045 std::vector<bone_palette> bone_palettes;
2046 bone_palettes.reserve(data.submeshes.size());
2049 for(
size_t palette_id = 0; palette_id < data.submeshes.size(); ++palette_id)
2051 auto&
submesh = data.submeshes[palette_id];
2054 std::vector<uint32_t> faces;
2061 for(uint32_t vertex_index : data.triangle_data[i].indices)
2063 const auto& vdata = vertex_table[vertex_index];
2064 for(
const auto& influence : vdata.influences)
2066 used_bones[
static_cast<uint32_t
>(influence)] =
true;
2075 bone_palettes.push_back(new_palette);
2080 for(uint32_t i = face_start; i < face_end; ++i)
2082 for(uint32_t k = 0; k < 3; ++k)
2084 uint32_t vertex_index = data.triangle_data[i].indices[k];
2085 auto& vdata = vertex_table[vertex_index];
2087 if(vdata.palette == -1)
2089 vdata.palette =
static_cast<int32_t
>(palette_id);
2099 else if(vdata.palette !=
static_cast<int32_t
>(palette_id))
2102 uint32_t new_index =
static_cast<uint32_t
>(vertex_table.size());
2106 new_vertex.
palette =
static_cast<int32_t
>(palette_id);
2107 vertex_table.push_back(new_vertex);
2119 data.triangle_data[i].indices[k] = new_index;
2128 bool has_weights = new_format.has(gfx::attribute::Weight);
2129 bool has_indices = new_format.has(gfx::attribute::Indices);
2131 if(!has_weights || !has_indices)
2133 new_format.m_hash = 0;
2136 new_format.add(gfx::attribute::Weight, 4, gfx::attribute_type::Float);
2140 new_format.add(gfx::attribute::Indices, 4, gfx::attribute_type::Float,
false,
true);
2143 data.vertex_format = new_format;
2146 uint16_t vertex_stride = data.vertex_format.getStride();
2147 uint32_t original_vertex_count = data.vertex_count;
2150 if(data.vertex_format.m_hash != original_format.m_hash)
2154 data.vertex_data.clear();
2155 data.vertex_data.resize(vertex_table.size() * vertex_stride);
2158 data.vertex_data.data(),
2160 original_buffer.data(),
2161 original_vertex_count);
2166 data.vertex_data.resize(vertex_table.size() * vertex_stride);
2170 uint8_t* src_vertices_ptr = data.vertex_data.data();
2171 for(
size_t i = 0; i < vertex_table.size(); ++i)
2173 auto& vdata = vertex_table[i];
2174 int32_t palette_id = vdata.palette;
2181 const auto& palette = bone_palettes[
static_cast<size_t>(palette_id)];
2184 if(i >= original_vertex_count)
2186 std::memcpy(src_vertices_ptr + (i * vertex_stride),
2187 src_vertices_ptr + (vdata.original_vertex * vertex_stride),
2191 uint32_t max_bones = std::min<uint32_t>(4, uint32_t(vdata.influences.size()));
2194 math::vec4 blend_weights(0.0f, 0.0f, 0.0f, 0.0f);
2195 math::vec4 blend_indices(0.0f, 0.0f, 0.0f, 0.0f);
2197 for(uint32_t j = 0; j < max_bones; ++j)
2199 uint32_t palette_bone_index =
2200 palette.translate_bone_to_palette(
static_cast<uint32_t
>(vdata.influences[j]));
2202 blend_indices[
static_cast<math::vec4::length_type
>(j)] =
static_cast<float>(palette_bone_index);
2203 blend_weights[
static_cast<math::vec4::length_type
>(j)] = vdata.weights[j];
2208 gfx::attribute::Weight,
2215 gfx::attribute::Indices,
2223 data.vertex_count =
static_cast<uint32_t
>(vertex_table.size());
2226 data.skin_is_prepared =
true;
2227 data.bone_palette_bones.clear();
2228 data.bone_palette_bones.reserve(bone_palettes.size());
2229 for(
const auto& palette : bone_palettes)
2231 data.bone_palette_bones.push_back(palette.get_bones());
2238 if(data.vertex_data.empty() || data.triangle_count == 0 || data.vertex_count == 0)
2240 APPLOG_ERROR(
"Cannot generate LODs for empty mesh data\n");
2244 if(!data.vertex_format.has(gfx::attribute::Position))
2246 APPLOG_ERROR(
"Mesh must have position data to generate LODs\n");
2250 if(data.submeshes.empty())
2252 APPLOG_ERROR(
"Cannot generate LODs for mesh with no submeshes\n");
2257 uint16_t position_offset = data.vertex_format.getOffset(gfx::attribute::Position);
2258 uint16_t vertex_stride = data.vertex_format.getStride();
2259 const uint8_t* vertex_data_ptr = data.vertex_data.data();
2262 const float* vertex_positions =
reinterpret_cast<const float*
>(vertex_data_ptr + position_offset);
2265 std::vector<uint32_t> base_indices(data.triangle_count * 3);
2266 for(uint32_t i = 0; i < data.triangle_count; ++i)
2268 base_indices[i * 3 + 0] = data.triangle_data[i].indices[0];
2269 base_indices[i * 3 + 1] = data.triangle_data[i].indices[1];
2270 base_indices[i * 3 + 2] = data.triangle_data[i].indices[2];
2277 uint32_t previous_face_count = data.triangle_count;
2278 constexpr float MIN_FACE_COUNT_DIFFERENCE_RATIO = 0.02f;
2281 bool use_attribute_simplify =
false;
2282 std::vector<float> packed_attributes;
2283 const float* attribute_ptr =
nullptr;
2284 size_t attribute_stride = 0;
2285 std::vector<float> attribute_weights;
2286 uint32_t attribute_component_count = 0;
2287 uint32_t total_components = 0;
2290 bool has_normal = data.vertex_format.has(gfx::attribute::Normal);
2291 bool has_texcoord = data.vertex_format.has(gfx::attribute::TexCoord0);
2292 bool has_tangent = data.vertex_format.has(gfx::attribute::Tangent);
2294 if(has_normal || has_texcoord || has_tangent)
2297 if(has_normal) total_components += 3;
2298 if(has_texcoord) total_components += 2;
2299 if(has_tangent) total_components += 3;
2301 packed_attributes.resize(data.vertex_count * total_components);
2302 attribute_weights.resize(total_components);
2305 uint32_t weight_offset = 0;
2308 attribute_weights[weight_offset + 0] = 1.5f;
2309 attribute_weights[weight_offset + 1] = 1.5f;
2310 attribute_weights[weight_offset + 2] = 1.5f;
2315 attribute_weights[weight_offset + 0] = 1.0f;
2316 attribute_weights[weight_offset + 1] = 1.0f;
2321 attribute_weights[weight_offset + 0] = 0.75f;
2322 attribute_weights[weight_offset + 1] = 0.75f;
2323 attribute_weights[weight_offset + 2] = 0.75f;
2329 for(uint32_t i = 0; i < data.vertex_count; ++i)
2331 float* dst = &packed_attributes[i * total_components];
2332 uint32_t component_offset = 0;
2337 gfx::vertex_unpack(attr, gfx::attribute::Normal, data.vertex_format, vertex_data_ptr, i);
2338 dst[component_offset + 0] = attr[0];
2339 dst[component_offset + 1] = attr[1];
2340 dst[component_offset + 2] = attr[2];
2341 component_offset += 3;
2347 gfx::vertex_unpack(attr, gfx::attribute::TexCoord0, data.vertex_format, vertex_data_ptr, i);
2348 dst[component_offset + 0] = attr[0];
2349 dst[component_offset + 1] = attr[1];
2350 component_offset += 2;
2356 gfx::vertex_unpack(attr, gfx::attribute::Tangent, data.vertex_format, vertex_data_ptr, i);
2357 dst[component_offset + 0] = attr[0];
2358 dst[component_offset + 1] = attr[1];
2359 dst[component_offset + 2] = attr[2];
2360 component_offset += 3;
2366 if(total_components > 0)
2368 attribute_ptr = packed_attributes.data();
2369 attribute_stride = total_components *
sizeof(float);
2370 attribute_component_count = total_components;
2371 use_attribute_simplify =
true;
2378 auto compute_bbox_from_indices = [&](
const uint32_t*
indices,
size_t index_count) ->
math::bbox
2381 for(
size_t i = 0; i < index_count; ++i)
2384 reinterpret_cast<const float*
>(vertex_data_ptr +
indices[i] * vertex_stride + position_offset);
2385 box.add_point(math::vec3(pos[0], pos[1], pos[2]));
2391 for(
const auto& config : lod_configs)
2393 size_t target_tri_count = config.first;
2394 float target_error = config.second;
2397 target_tri_count = std::min(target_tri_count,
static_cast<size_t>(data.triangle_count));
2398 if(target_tri_count < 1)
2406 lod_data.simplification_error = 0.0f;
2408 uint32_t current_face_start = 0;
2411 unsigned int options = meshopt_SimplifyLockBorder;
2413 if(data.submeshes.size() > 1)
2415 options |= meshopt_SimplifySparse;
2420 for(
const auto& base_submesh : data.submeshes)
2423 if(base_submesh.face_count == 0)
2425 submesh lod_submesh = base_submesh;
2426 lod_submesh.
face_start =
static_cast<int32_t
>(current_face_start);
2427 lod_data.submeshes.push_back(lod_submesh);
2432 float submesh_ratio =
static_cast<float>(base_submesh.face_count) /
static_cast<float>(data.triangle_count);
2433 size_t submesh_target_tri_count =
static_cast<size_t>(std::max(1.0f,
static_cast<float>(target_tri_count) * submesh_ratio));
2434 submesh_target_tri_count = std::min(submesh_target_tri_count,
static_cast<size_t>(base_submesh.face_count));
2437 std::vector<uint32_t> submesh_indices(base_submesh.face_count * 3);
2438 for(uint32_t i = 0; i < base_submesh.face_count; ++i)
2440 uint32_t tri_idx = base_submesh.face_start + i;
2441 submesh_indices[i * 3 + 0] = base_indices[tri_idx * 3 + 0];
2442 submesh_indices[i * 3 + 1] = base_indices[tri_idx * 3 + 1];
2443 submesh_indices[i * 3 + 2] = base_indices[tri_idx * 3 + 2];
2447 size_t submesh_target_index_count = submesh_target_tri_count * 3;
2450 std::vector<uint32_t> submesh_lod_indices(base_submesh.face_count * 3);
2453 float submesh_result_error = 0.0f;
2454 size_t submesh_lod_index_count = 0;
2458 if(use_attribute_simplify)
2460 submesh_lod_index_count = meshopt_simplifyWithAttributes(
2461 submesh_lod_indices.data(),
2462 submesh_indices.data(),
2463 base_submesh.face_count * 3,
2469 attribute_weights.data(),
2470 attribute_component_count,
2472 submesh_target_index_count,
2475 &submesh_result_error);
2479 submesh_lod_index_count = meshopt_simplify(
2480 submesh_lod_indices.data(),
2481 submesh_indices.data(),
2482 base_submesh.face_count * 3,
2486 submesh_target_index_count,
2489 &submesh_result_error);
2493 size_t submesh_lod_tri_count = submesh_lod_index_count / 3;
2494 if(submesh_lod_tri_count == 0)
2496 APPLOG_WARNING(
"Failed to generate LOD for submesh with target {} triangles (error: {})\n", submesh_target_tri_count, target_error);
2498 submesh lod_submesh = base_submesh;
2499 lod_submesh.
face_start =
static_cast<int32_t
>(current_face_start);
2500 lod_data.submeshes.push_back(lod_submesh);
2503 for(uint32_t i = 0; i < base_submesh.face_count * 3; ++i)
2505 lod_data.index_data.push_back(submesh_indices[i]);
2507 current_face_start += base_submesh.face_count;
2508 lod_data.face_count += base_submesh.face_count;
2513 submesh lod_submesh = base_submesh;
2514 lod_submesh.
face_count =
static_cast<uint32_t
>(submesh_lod_tri_count);
2515 lod_submesh.
face_start =
static_cast<int32_t
>(current_face_start);
2516 const math::bbox lod_bbox = compute_bbox_from_indices(submesh_lod_indices.data(), submesh_lod_index_count);
2519 lod_submesh.
bbox = lod_bbox;
2521 lod_data.submeshes.push_back(lod_submesh);
2524 for(
size_t i = 0; i < submesh_lod_index_count; ++i)
2526 lod_data.index_data.push_back(submesh_lod_indices[i]);
2530 current_face_start +=
static_cast<uint32_t
>(submesh_lod_tri_count);
2531 lod_data.face_count +=
static_cast<uint32_t
>(submesh_lod_tri_count);
2532 lod_data.simplification_error = std::max(
lod_data.simplification_error, submesh_result_error);
2538 APPLOG_WARNING(
"Failed to generate LOD with target {} triangles (error: {})\n", target_tri_count, target_error);
2542 if(
lod_data.face_count >= data.triangle_count)
2544 APPLOG_WARNING(
"Could not simplify mesh. Not enough triangles to simplify.\n");
2549 if(previous_face_count > 0)
2551 float face_count_difference =
static_cast<float>(previous_face_count -
lod_data.face_count);
2552 float difference_ratio = face_count_difference /
static_cast<float>(previous_face_count);
2554 if(difference_ratio < MIN_FACE_COUNT_DIFFERENCE_RATIO)
2556 APPLOG_INFO(
"Stopping LOD generation: new LOD face count ({}) is not significantly different from previous ({}, {:.2f}% difference)\n",
2557 lod_data.face_count, previous_face_count, difference_ratio * 100.0f);
2562 data.lods.push_back(std::move(
lod_data));
2563 previous_face_count =
lod_data.face_count;
2566 APPLOG_INFO(
"Generated {} LOD levels\n", data.lods.size());
2572 if(data.lods.empty())
2578 for(
auto& lod : lods_)
2580 for(
auto*
submesh : lod.submeshes_)
2584 lod.submeshes_.clear();
2586 lod.hardware_ib_.reset();
2592 for(
const auto&
lod_data : data.lods)
2606 for(
size_t i = 0; i <
lod_data.submeshes.size(); ++i)
2608 const auto& submesh_data =
lod_data.submeshes[i];
2609 auto* lod_submesh =
new submesh(submesh_data);
2613 if(lod_submesh->skinned)
2626 auto buffer_size =
static_cast<uint32_t
>(lod.
face_count_ * 3 *
sizeof(uint32_t));
2630 const uint16_t ib_flags = BGFX_BUFFER_INDEX32
2631 | BGFX_BUFFER_COMPUTE_READ
2632 | BGFX_BUFFER_COMPUTE_FORMAT_32X1
2633 | BGFX_BUFFER_COMPUTE_TYPE_UINT;
2634 lod.
hardware_ib_ = std::make_shared<gfx::index_buffer>(mem, ib_flags);
2637 lods_.push_back(std::move(lod));
2646 const auto& submeshes = get_submeshes(lod_index);
2648 for(
const auto*
submesh : submeshes)
2661 if(lod_index == 0 || lods_.empty())
2663 auto it = skinned_submesh_indices_.find(data_group_id);
2664 if(it != skinned_submesh_indices_.end())
2673 if(lod_index > 0 && lod_index <= lods_.size())
2675 const auto& lod = lods_[lod_index - 1];
2676 auto it = lod.skinned_submesh_indices_.find(data_group_id);
2677 if(it != lod.skinned_submesh_indices_.end())
2689 const auto& submeshes = get_submeshes(lod_index);
2691 for(
const auto*
submesh : submeshes)
2704 if(lod_index == 0 || lods_.empty())
2706 auto it = non_skinned_submesh_indices_.find(data_group_id);
2707 if(it != non_skinned_submesh_indices_.end())
2716 if(lod_index > 0 && lod_index <= lods_.size())
2718 const auto& lod = lods_[lod_index - 1];
2719 auto it = lod.non_skinned_submesh_indices_.find(data_group_id);
2720 if(it != lod.non_skinned_submesh_indices_.end())
2748 uint32_t groups_count = 0;
2751 groups_count = std::max(groups_count, sub.data_group_id + 1);
2753 return groups_count;
2769 std::vector<asset_handle<material>> imported_materials;
2772 auto mat = am.get_asset<
material>(uid);
2773 imported_materials.push_back(mat);
2775 return imported_materials;
2782 if(math::epsilonNotEqual(key1.vertex1->x, key2.vertex1->x, math::epsilon<float>()))
2784 return (key2.vertex1->x < key1.vertex1->x);
2786 if(math::epsilonNotEqual(key1.vertex1->y, key2.vertex1->y, math::epsilon<float>()))
2788 return (key2.vertex1->y < key1.vertex1->y);
2790 if(math::epsilonNotEqual(key1.vertex1->z, key2.vertex1->z, math::epsilon<float>()))
2792 return (key2.vertex1->z < key1.vertex1->z);
2795 if(math::epsilonNotEqual(key1.vertex2->x, key2.vertex2->x, math::epsilon<float>()))
2797 return (key2.vertex2->x < key1.vertex2->x);
2799 if(math::epsilonNotEqual(key1.vertex2->y, key2.vertex2->y, math::epsilon<float>()))
2801 return (key2.vertex2->y < key1.vertex2->y);
2803 if(math::epsilonNotEqual(key1.vertex2->z, key2.vertex2->z, math::epsilon<float>()))
2805 return (key2.vertex2->z < key1.vertex2->z);
2814 return key1.data_group_id < key2.data_group_id;
2819 auto vertex_compare =
2820 [](
const uint8_t* pVtx1,
const uint8_t* pVtx2,
const gfx::vertex_layout& layout,
float tolerance) ->
int
2825 for(uint16_t i = 0; i < gfx::attribute::Count; ++i)
2833 uint16_t offset = layout.getOffset(
static_cast<gfx::attribute>(i));
2836 const uint8_t* p1 = pVtx1 + offset;
2837 const uint8_t* p2 = pVtx2 + offset;
2840 uint8_t num_components{};
2841 bgfx::AttribType::Enum
type{};
2842 bool normalized{}, as_int{};
2843 layout.decode(
static_cast<gfx::attribute>(i), num_components,
type, normalized, as_int);
2848 case bgfx::AttribType::Float:
2850 for(uint8_t j = 0; j < num_components; ++j)
2852 diff = ((
float*)p1)[j] - ((
float*)p2)[j];
2853 if(fabsf(diff) > tolerance)
2854 return (diff < 0) ? -1 : 1;
2859 case bgfx::AttribType::Uint8:
2860 case bgfx::AttribType::Int16:
2864 ndifference = memcmp(p1, p2, num_components * (
type == bgfx::AttribType::Uint8 ? 1 : 2));
2865 if(ndifference != 0)
2867 return (ndifference < 0) ? -1 : 1;
2872 for(uint8_t j = 0; j < num_components; ++j)
2875 if(
type == bgfx::AttribType::Uint8)
2877 f1 = normalized ? ((float)p1[j] / 255.0f) : (float)p1[j];
2878 f2 = normalized ? ((float)p2[j] / 255.0f) : (float)p2[j];
2882 f1 = normalized ? ((float)((int16_t*)p1)[j] / 32767.0f) : (float)((int16_t*)p1)[j];
2883 f2 = normalized ? ((float)((int16_t*)p2)[j] / 32767.0f) : (float)((int16_t*)p2)[j];
2886 if(fabsf(diff) > tolerance)
2888 return (diff < 0) ? -1 : 1;
2905 int ndifference = vertex_compare(key1.vertex, key2.vertex, key1.format, key1.tolerance);
2906 if(ndifference != 0)
2908 return (ndifference < 0);
2918 if(key1.data_group_id != key2.data_group_id)
2920 return key1.data_group_id < key2.data_group_id;
2933 auto it_bone1 = p1->
bones.begin();
2934 auto it_bone2 = p2->
bones.begin();
2935 for(; it_bone1 != p1->
bones.end() && it_bone2 != p2->
bones.end(); ++it_bone1, ++it_bone2)
2937 if(it_bone1->first != it_bone2->first)
2939 return it_bone1->first < it_bone2->first;
2951 if(force_normal_generation_ || preparation_data_.compute_normals)
2954 std::vector<uint32_t> adjacency;
2955 if(!generate_adjacency(adjacency))
2957 APPLOG_ERROR(
"Failed to generate adjacency buffer mesh containing {0} faces.\n",
2958 preparation_data_.triangle_count);
2962 if(force_barycentric_generation_ || preparation_data_.compute_barycentric)
2965 if(!generate_vertex_barycentrics(&adjacency.front()))
2967 APPLOG_ERROR(
"Failed to generate vertex barycentric coords for mesh "
2968 "containing {0} faces.\n",
2969 preparation_data_.triangle_count);
2977 if(!generate_vertex_normals(&adjacency.front()))
2979 APPLOG_ERROR(
"Failed to generate vertex normals for mesh containing {0} faces.\n",
2980 preparation_data_.triangle_count);
2990 if(!weld_vertices())
2992 APPLOG_ERROR(
"Failed to weld vertices for mesh containing {0} faces.\n", preparation_data_.triangle_count);
3001 if(force_tangent_generation_ || preparation_data_.compute_binormals || preparation_data_.compute_tangents)
3004 if(vertex_format_.has(gfx::attribute::Normal))
3007 if(!generate_vertex_tangents())
3009 APPLOG_ERROR(
"Failed to generate vertex tangents for mesh containing "
3011 preparation_data_.triangle_count);
3027 uint32_t start_tri, previous_tri, current_tri;
3028 math::vec3 vec_edge1, vec_edge2, vec_normal;
3029 uint32_t i, j, k,
index;
3032 uint16_t position_offset = vertex_format_.getOffset(gfx::attribute::Position);
3033 bool has_normals = vertex_format_.has(gfx::attribute::Normal);
3034 uint16_t vertex_stride = vertex_format_.getStride();
3043 uint32_t original_vertex_count = preparation_data_.vertex_count;
3046 remap_array_ptr->resize(preparation_data_.vertex_count);
3047 for(i = 0; i < preparation_data_.vertex_count; ++i)
3049 (*remap_array_ptr)[i] = i;
3055 uint8_t* src_vertices_ptr = preparation_data_.vertex_data.data();
3056 auto* normals_ptr =
new math::vec3[preparation_data_.triangle_count];
3057 memset(normals_ptr, 0, preparation_data_.triangle_count *
sizeof(math::vec3));
3058 for(i = 0; i < preparation_data_.triangle_count; ++i)
3061 const triangle& tri = preparation_data_.triangle_data[i];
3063 reinterpret_cast<const math::vec3*
>(src_vertices_ptr + (tri.
indices[0] * vertex_stride) + position_offset);
3065 reinterpret_cast<const math::vec3*
>(src_vertices_ptr + (tri.
indices[1] * vertex_stride) + position_offset);
3067 reinterpret_cast<const math::vec3*
>(src_vertices_ptr + (tri.
indices[2] * vertex_stride) + position_offset);
3071 vec_edge1 = math::normalize(*v2 - *v1);
3072 vec_edge2 = math::normalize(*v3 - *v1);
3075 vec_normal = math::cross(vec_edge1, vec_edge2);
3076 normals_ptr[i] = math::normalize(vec_normal);
3081 for(i = 0; i < preparation_data_.triangle_count; ++i)
3083 triangle& tri = preparation_data_.triangle_data[i];
3090 for(j = 0; j < 3; ++j)
3096 if(!force_normal_generation_ &&
3097 (preparation_data_.vertex_flags[
index] & preparation_data::source_contains_normal))
3121 current_tri = adjacency_ptr[(i * 3) + ((j + 2) % 3)];
3126 if(current_tri == start_tri || current_tri == 0xFFFFFFFF)
3132 for(k = 0; k < 3; ++k)
3134 if(adjacency_ptr[(current_tri * 3) + k] == previous_tri)
3145 previous_tri = current_tri;
3146 current_tri = adjacency_ptr[(current_tri * 3) + ((k + 2) % 3)];
3161 if(current_tri != 0xFFFFFFFF)
3163 for(k = 0; k < 3; ++k)
3165 if(adjacency_ptr[(current_tri * 3) + k] == previous_tri)
3181 start_tri = current_tri;
3182 previous_tri = current_tri;
3183 current_tri = adjacency_ptr[(current_tri * 3) + k];
3184 vec_normal = normals_ptr[start_tri];
3189 if(current_tri == start_tri || current_tri == 0xFFFFFFFF)
3195 vec_normal += normals_ptr[current_tri];
3198 for(k = 0; k < 3; ++k)
3200 if(adjacency_ptr[(current_tri * 3) + k] == previous_tri)
3211 previous_tri = current_tri;
3212 current_tri = adjacency_ptr[(current_tri * 3) + ((k + 1) % 3)];
3226 vec_normal = math::normalize(vec_normal);
3235 math::vec3 ref_normal;
3236 ref_normal[0] = fn[0];
3237 ref_normal[1] = fn[1];
3238 ref_normal[2] = fn[2];
3239 if(ref_normal.x == 0.0f && ref_normal.y == 0.0f && ref_normal.z == 0.0f)
3246 if(math::abs(ref_normal.x - vec_normal.x) >= 1e-3f || math::abs(ref_normal.y - vec_normal.y) >= 1e-3f ||
3247 math::abs(ref_normal.z - vec_normal.z) >= 1e-3f)
3250 preparation_data_.vertex_data.resize(preparation_data_.vertex_data.size() + vertex_stride);
3257 src_vertices_ptr = preparation_data_.vertex_data.data();
3260 std::memcpy(src_vertices_ptr + (preparation_data_.vertex_count * vertex_stride),
3261 src_vertices_ptr + (
index * vertex_stride),
3265 preparation_data_.vertex_flags.push_back(preparation_data_.vertex_flags[
index]);
3270 (*remap_array_ptr)[
index] = preparation_data_.vertex_count;
3275 index = preparation_data_.vertex_count++;
3276 math::vec4 norm(vec_normal, 0.0f);
3279 gfx::attribute::Normal,
3300 if(remap_array_ptr && original_vertex_count == preparation_data_.vertex_count)
3302 remap_array_ptr->clear();
3317 math::vec3 *tangents =
nullptr, *bitangents =
nullptr;
3318 uint32_t i, i1, i2, i3, num_faces, num_verts;
3319 math::vec3
P, Q, T, B, cross_vec, normal_vec;
3322 uint16_t vertex_stride = vertex_format_.getStride();
3324 bool has_normals = vertex_format_.has(gfx::attribute::Normal);
3332 bool requires_tangents = vertex_format_.has(gfx::attribute::Tangent);
3333 bool requires_bitangents = vertex_format_.has(gfx::attribute::Bitangent);
3334 if(!force_tangent_generation_ && !requires_bitangents && !requires_tangents)
3341 num_faces = preparation_data_.triangle_count;
3342 num_verts = preparation_data_.vertex_count;
3343 tangents =
new math::vec3[num_verts];
3344 bitangents =
new math::vec3[num_verts];
3345 memset(tangents, 0,
sizeof(math::vec3) * num_verts);
3346 memset(bitangents, 0,
sizeof(math::vec3) * num_verts);
3349 uint8_t* src_vertices_ptr = preparation_data_.vertex_data.data();
3350 for(i = 0; i < num_faces; ++i)
3352 triangle& tri = preparation_data_.triangle_data[i];
3363 gfx::vertex_unpack(fE, gfx::attribute::Position, vertex_format_, src_vertices_ptr, i1);
3366 gfx::vertex_unpack(fF, gfx::attribute::Position, vertex_format_, src_vertices_ptr, i2);
3369 gfx::vertex_unpack(fG, gfx::attribute::Position, vertex_format_, src_vertices_ptr, i3);
3370 std::memcpy(&E[0], fE, 3 *
sizeof(
float));
3371 std::memcpy(&F[0], fF, 3 *
sizeof(
float));
3372 std::memcpy(&G[0], fG, 3 *
sizeof(
float));
3379 gfx::vertex_unpack(&fEt[0], gfx::attribute::TexCoord0, vertex_format_, src_vertices_ptr, i1);
3382 gfx::vertex_unpack(&fFt[0], gfx::attribute::TexCoord0, vertex_format_, src_vertices_ptr, i2);
3385 gfx::vertex_unpack(&fGt[0], gfx::attribute::TexCoord0, vertex_format_, src_vertices_ptr, i3);
3386 std::memcpy(&Et[0], fEt, 2 *
sizeof(
float));
3387 std::memcpy(&Ft[0], fFt, 2 *
sizeof(
float));
3388 std::memcpy(&Gt[0], fGt, 2 *
sizeof(
float));
3399 float s1 = Ft.x - Et.x;
3400 float t1 = Ft.y - Et.y;
3401 float s2 = Gt.x - Et.x;
3402 float t2 = Gt.y - Et.y;
3408 float r = (s1 * t2 - s2 * t1);
3409 if(math::abs(r) < math::epsilon<float>())
3418 T.x = r * (t2 *
P.x - t1 * Q.x);
3419 T.y = r * (t2 *
P.y - t1 * Q.y);
3420 T.z = r * (t2 *
P.z - t1 * Q.z);
3421 B.x = r * (s1 * Q.x - s2 *
P.x);
3422 B.y = r * (s1 * Q.y - s2 *
P.y);
3423 B.z = r * (s1 * Q.z - s2 *
P.z);
3430 bitangents[i1] += B;
3431 bitangents[i2] += B;
3432 bitangents[i3] += B;
3437 for(i = 0; i < num_verts; i++, src_vertices_ptr += vertex_stride)
3441 bool has_bitangent =
false;
3442 bool has_tangent =
false;
3444 if(!preparation_data_.vertex_flags.empty())
3446 has_bitangent = ((preparation_data_.vertex_flags[i] & preparation_data::source_contains_binormal) != 0);
3447 has_tangent = ((preparation_data_.vertex_flags[i] & preparation_data::source_contains_tangent) != 0);
3449 if(!force_tangent_generation_ && has_bitangent && has_tangent)
3458 std::memcpy(&normal_vec[0],
normal, 3 *
sizeof(
float));
3463 T = T - (normal_vec * math::dot(normal_vec, T));
3466 float length_sq = math::dot(T, T);
3467 if(length_sq < 1e-6f)
3472 if(std::abs(normal_vec.x) < std::abs(normal_vec.y) && std::abs(normal_vec.x) < std::abs(normal_vec.z))
3474 axis = math::vec3(1.0f, 0.0f, 0.0f);
3476 else if(std::abs(normal_vec.y) < std::abs(normal_vec.z))
3478 axis = math::vec3(0.0f, 1.0f, 0.0f);
3482 axis = math::vec3(0.0f, 0.0f, 1.0f);
3484 T = math::cross(normal_vec, axis);
3487 T = math::normalize(T);
3490 if(force_tangent_generation_ || (!has_tangent && requires_tangents))
3492 math::vec4 t(T, 1.0f);
3493 gfx::vertex_pack(math::value_ptr(t),
true, gfx::attribute::Tangent, vertex_format_, src_vertices_ptr);
3497 if(force_tangent_generation_ || (!has_bitangent && requires_bitangents))
3500 B = math::cross(normal_vec, T);
3501 B = math::normalize(B);
3506 cross_vec = math::cross(normal_vec, T);
3507 if(math::dot(cross_vec, bitangents[i]) < 0.0f)
3515 math::vec4
b(B, 1.0f);
3516 gfx::vertex_pack(math::value_ptr(
b),
true, gfx::attribute::Bitangent, vertex_format_, src_vertices_ptr);
3533 std::map<weld_key, uint32_t> vertex_tree;
3534 std::map<weld_key, uint32_t>::const_iterator it_key;
3536 uint32_t new_vertex_count = 0;
3539 if(vertex_remap_ptr)
3541 vertex_remap_ptr->resize(preparation_data_.vertex_count);
3543 auto collapse_map =
new uint32_t[preparation_data_.vertex_count];
3546 uint16_t vertex_stride = vertex_format_.getStride();
3549 for(uint32_t i = 0; i < preparation_data_.vertex_count; ++i)
3552 key.vertex = (&preparation_data_.vertex_data[0]) + (i * vertex_stride);
3553 key.format = vertex_format_;
3554 key.tolerance = tolerance;
3557 it_key = vertex_tree.find(key);
3558 if(it_key == vertex_tree.end())
3561 vertex_tree[key] = new_vertex_count;
3562 collapse_map[i] = new_vertex_count;
3563 if(vertex_remap_ptr)
3565 (*vertex_remap_ptr)[i] = new_vertex_count;
3569 new_vertex_data.resize((new_vertex_count + 1) * vertex_stride);
3570 std::memcpy(&new_vertex_data[new_vertex_count * vertex_stride], key.vertex, vertex_stride);
3571 new_vertex_flags.push_back(preparation_data_.vertex_flags[i]);
3579 collapse_map[i] = it_key->second;
3580 if(vertex_remap_ptr)
3582 (*vertex_remap_ptr)[i] = 0xFFFFFFFF;
3590 if(preparation_data_.vertex_count == new_vertex_count)
3594 if(vertex_remap_ptr)
3596 vertex_remap_ptr->clear();
3603 preparation_data_.vertex_data.clear();
3604 preparation_data_.vertex_data.resize(new_vertex_data.size());
3605 std::memcpy(preparation_data_.vertex_data.data(), new_vertex_data.data(), new_vertex_data.size());
3606 preparation_data_.vertex_flags.clear();
3607 preparation_data_.vertex_flags.resize(new_vertex_flags.size());
3608 std::memcpy(preparation_data_.vertex_flags.data(), new_vertex_flags.data(), new_vertex_flags.size());
3609 preparation_data_.vertex_count = new_vertex_count;
3612 for(uint32_t i = 0; i < preparation_data_.triangle_count; ++i)
3614 triangle& tri = preparation_data_.triangle_data[i];
3633 bones_.push_back(bone);
3638 for(
size_t i = 0; i < bones_.size();)
3640 if(bones_[i].influences.empty())
3642 bones_.erase(bones_.begin() +
static_cast<int>(i));
3655 for(
auto& bone : bones_)
3657 bone.influences.clear();
3669 for(
auto& bone : bones_)
3673 new_influences.reserve(influences.size());
3674 for(
auto& influence : influences)
3676 uint32_t new_index = remap[influence.vertex_index];
3677 if(new_index != 0xFFFFFFFF)
3684 if(new_index >= remap.size())
3686 new_influences.push_back(
vertex_influence{influence.vertex_index, influence.weight});
3692 bone.influences = new_influences;
3698 const std::vector<uint32_t>& vertex_remap,
3704 table.reserve(vertex_count);
3705 for(vertex = 0; vertex < vertex_count; ++vertex)
3710 table.push_back(data);
3715 for(
size_t i = 0; i < bones_.size(); ++i)
3718 for(
auto& influence : influences)
3721 if(!vertex_remap.empty())
3723 vertex = vertex_remap[influence.vertex_index];
3724 if(vertex == 0xFFFFFFFF)
3728 auto& data = table[vertex];
3730 data.influences.push_back(
static_cast<int32_t
>(i));
3731 data.weights.push_back(influence.weight);
3735 auto& data = table[influence.vertex_index];
3737 data.influences.push_back(
static_cast<int32_t
>(i));
3738 data.weights.push_back(influence.weight);
3764 auto it = std::find_if(std::begin(bones_),
3766 [
name](
const auto& bone)
3768 return name == bone.bone_id;
3770 if(it != std::end(bones_))
3772 query.bone = &(*it);
3773 query.index = std::distance(std::begin(bones_), it);
3790 , maximum_size_(palette_size)
3791 , maximum_blend_index_(-1)
3796 const skin_bind_data& bind_data)
const ->
const std::vector<math::mat4>&
3800 const auto& bind_list = bind_data.get_bones();
3803 const size_t bones_size = bones_.size();
3804 const size_t count = std::min(bones_size, node_transforms.size());
3806 thread_local static std::vector<math::mat4> skinning_transforms_;
3807 skinning_transforms_.resize(bones_size, math::identity<math::mat4>());
3810 const size_t bind_list_size = bind_list.size();
3812 for(
size_t i = 0; i <
count; ++i)
3814 const auto bone = bones_[i];
3817 if(bone >= node_transforms.size() || bone >= bind_list_size)
3822 const auto& bone_transform = node_transforms[bone];
3823 const auto& bone_data = bind_list[bone];
3826 const auto& bind_pose_matrix = bone_data.bind_pose_transform.get_matrix();
3827 skinning_transforms_[i] = bone_transform.get_matrix() * bind_pose_matrix;
3830 return skinning_transforms_;
3834 const skin_bind_data& bind_data)
const ->
const std::vector<math::mat4>&
3838 const auto& bind_list = bind_data.get_bones();
3841 const size_t bones_size = bones_.size();
3842 const size_t count = std::min(bones_size, node_transforms.size());
3844 thread_local static std::vector<math::mat4> skinning_transforms_;
3845 skinning_transforms_.resize(bones_size, math::identity<math::mat4>());
3848 const size_t bind_list_size = bind_list.size();
3850 for(
size_t i = 0; i <
count; ++i)
3852 const auto bone = bones_[i];
3855 if(bone >= node_transforms.size() || bone >= bind_list_size)
3860 const auto& bone_transform = node_transforms[bone];
3861 const auto& bone_data = bind_list[bone];
3864 const auto& bind_pose_matrix = bone_data.bind_pose_transform.get_matrix();
3865 skinning_transforms_[i] = bone_transform * bind_pose_matrix;
3868 return skinning_transforms_;
3873 bone_index_map_t::iterator it_bone, it_bone2;
3877 for(it_bone = bones.begin(); it_bone != bones.end(); ++it_bone)
3883 bones_.push_back(it_bone->first);
3893 std::memcpy(
faces_.data(), faces.data(), faces.size() *
sizeof(uint32_t));
3898 bone_index_map_t::iterator it_bone, it_bone2;
3903 for(
size_t i = 0, j = bones.size(); i < j; ++i)
3924 std::memcpy(
faces_.data(), faces.data(), faces.size() *
sizeof(uint32_t));
3929 bone_index_map_t::iterator it_bone;
3937 for(
size_t i = 0; i < bones.size(); ++i)
3943 bones_.push_back(bones[i]);
3951 int32_t& current_space,
3952 int32_t& common_bones,
3953 int32_t& additional_bones)
3956 current_space =
static_cast<int32_t
>(
maximum_size_ -
static_cast<uint32_t
>(
bones_.size()));
3958 additional_bones = 0;
3963 additional_bones =
static_cast<int32_t
>(
input.size());
3967 else if(
input.size() == 0)
3975 bone_index_map_t::iterator it_bone, it_bone2;
3976 for(it_bone =
input.begin(); it_bone !=
input.end(); ++it_bone)
3989 auto it_bone = bones_lut_.find(bone_index);
3990 if(it_bone == bones_lut_.end())
3992 return it_bone->second;
4040 if(preparation_data_.compute_per_triangle_material_data)
4045 triangle_data_.resize(face_count_);
4047 uint32_t* dst_indices_ptr = system_ib_;
4048 for(uint32_t i = 0; i < face_count_; ++i)
4051 const triangle& tri_in = preparation_data_.triangle_data[i];
4052 *dst_indices_ptr++ = tri_in.
indices[0];
4053 *dst_indices_ptr++ = tri_in.
indices[1];
4054 *dst_indices_ptr++ = tri_in.
indices[2];
4056 if(preparation_data_.compute_per_triangle_material_data)
4065 preparation_data_.triangle_count = 0;
4066 preparation_data_.triangle_data.clear();
4071 data_groups_.clear();
4073 for(
auto submesh : mesh_submeshes_)
4077 mesh_submeshes_.clear();
4079 skinned_submesh_indices_.clear();
4080 skinned_submesh_count_ = {};
4082 non_skinned_submesh_indices_.clear();
4083 non_skinned_submesh_count_ = {};
4085 for(
size_t i = 0; i < preparation_data_.submeshes.size(); ++i)
4087 const auto& s = preparation_data_.submeshes[i];
4092 skinned_submesh_count_++;
4093 skinned_submesh_indices_[sub->data_group_id].emplace_back(i);
4097 non_skinned_submesh_count_++;
4098 non_skinned_submesh_indices_[sub->data_group_id].emplace_back(i);
4101 mesh_submeshes_.emplace_back(sub);
4102 data_groups_[sub->data_group_id].emplace_back(sub);
4105 preparation_data_.submeshes.clear();
4113 uint32_t* ib_data =
nullptr;
4114 std::shared_ptr<void> ib_hardware =
nullptr;
4115 uint32_t lod_face_count = 0;
4124 else if(lod_index > 0 && lod_index <=
lods_.size())
4127 const auto& lod =
lods_[lod_index - 1];
4128 ib_data = lod.system_ib_;
4129 ib_hardware = lod.hardware_ib_;
4130 lod_face_count = lod.face_count_;
4147 auto vb = std::static_pointer_cast<gfx::vertex_buffer>(
hardware_vb_);
4148 auto ib = std::static_pointer_cast<gfx::index_buffer>(ib_hardware);
4160 std::memcpy(vb.data,
4170 std::memcpy(ib.data,
4171 ib_data + index_start,
4172 index_count *
sizeof(uint32_t));
gfx::texture_format format
void checked_array_delete(T *&x)
void checked_delete(T *&x)
A type erasing container that can store any mesh.
any_generator< mesh_vertex_t > vertices() const noexcept
any_generator< triangle_t > triangles() const noexcept
A mesh with values evaluated using a callback function.
Manages assets, including loading, unloading, and storage.
Outlines a collection of bones that influence a given set of faces/vertices in the mesh.
std::map< uint32_t, uint32_t > bone_index_map_t
auto get_bones() const -> const std::vector< uint32_t > &
Retrieves the indices of the bones referenced by this palette.
auto get_maximum_size() const -> uint32_t
Retrieves the maximum size of the palette.
bone_index_map_t bones_lut_
< Sorted list of bones in this palette.
void clear_influenced_faces()
Clears out the temporary face influences array.
uint32_t data_group_id_
The maximum size of the palette.
void assign_bones(bone_index_map_t &bones, std::vector< uint32_t > &faces)
Assigns the specified bones (and faces) to this bone palette.
std::vector< uint32_t > faces_
The data group identifier used to separate the mesh data into submeshes relevant tothis bone palette.
auto get_maximum_blend_index() const -> int32_t
Retrieves the maximum vertex blend index for this palette.
auto get_influenced_faces() -> std::vector< uint32_t > &
Retrieves the list of faces assigned to this palette.
auto get_skinning_matrices(const std::vector< math::transform > &node_transforms, const skin_bind_data &bind_data) const -> const std::vector< math::mat4 > &
Gathers the bone/palette information and matrices ready for drawing the skinned mesh.
uint32_t maximum_size_
The maximum vertex blend index for this palette.
void compute_palette_fit(bone_index_map_t &input, int32_t ¤t_space, int32_t &common_base, int32_t &additional_bones)
Determines the relevant "fit" information that can be used to discover if and how the specified combi...
void set_data_group(uint32_t group)
Sets the identifier of the data group assigned to the submesh of the mesh reserved for this bone pale...
void set_maximum_blend_index(int index)
Sets the maximum vertex blend index for this palette.
int32_t maximum_blend_index_
auto translate_bone_to_palette(uint32_t bone_index) const -> uint32_t
Translates the specified bone index into its associated position in the palette.
bone_palette(uint32_t paletteSize)
Constructs a bone palette with the given size.
auto get_data_group() const -> uint32_t
Retrieves the identifier of the data group assigned to the submesh of the mesh reserved for this bone...
std::vector< uint32_t > bones_
List of faces assigned to this palette.
Class representing a camera. Contains functionality for manipulating and updating a camera....
Base class for materials used in rendering.
Main class representing a 3D mesh with support for different LODs, submeshes, and skinning.
static auto apply_skin_to_load_data(load_data &data) -> bool
Applies skinning vertex duplication to load_data (for offline LOD generation).
gfx::vertex_layout vertex_format_
The final system memory copy of the index buffer (LOD 0 - base mesh).
auto get_lod_index_data(uint32_t lod_index, std::vector< uint32_t > &out_indices, float &out_error) const -> bool
Gets index buffer data for a specific LOD level (for serialization).
auto generate_vertex_normals(uint32_t *adjacency_ptr, std::vector< uint32_t > *remap_array_ptr=nullptr) -> bool
Generates vertex normals for the mesh.
auto create_dodecahedron(const gfx::vertex_layout &format, bool hardware_copy=true) -> bool
Creates a dodecahedron geometry.
auto create_plane(const gfx::vertex_layout &format, float width, float height, uint32_t width_segments, uint32_t height_segments, mesh_create_origin origin, bool hardware_copy=true) -> bool
Creates a plane geometry.
auto restore_lods_from_load_data(const load_data &data) -> bool
Restores LOD levels from load_data into the mesh.
static auto generate_lods_for_load_data(load_data &data, const std::vector< std::pair< size_t, float > > &lod_configs) -> bool
Generates LOD levels directly in load_data without creating GPU buffers.
bone_palette_array_t bone_palettes_
List of armature nodes.
std::shared_ptr< void > hardware_vb_
The actual hardware index buffer resource (LOD 0 - base mesh).
auto set_vertex_source(void *source, uint32_t vertex_count, const gfx::vertex_layout &source_format) -> bool
Sets the source of the vertex buffer to pull data from while preparing the mesh.
std::unique_ptr< armature_node > root_
UIDs of default materials generated during import, indexed by data_group_id.
auto create_sphere(const gfx::vertex_layout &format, float radius, uint32_t stacks, uint32_t slices, mesh_create_origin origin, bool hardware_copy=true) -> bool
Creates a sphere geometry.
auto create_capsule(const gfx::vertex_layout &format, float radius, float height, uint32_t stacks, uint32_t slices, mesh_create_origin origin, bool hardware_copy=true) -> bool
Creates a capsule geometry.
submesh_array_t mesh_submeshes_
Indices in the subset array which are skinned.
auto get_bone_palettes() const -> const bone_palette_array_t &
Retrieves the compiled bone combination palette data if this mesh has been bound as a skin.
auto get_lod_count() const -> uint32_t
Gets the number of LOD levels available.
void check_for_degenerates()
auto create_icosahedron(const gfx::vertex_layout &format, bool hardware_copy=true) -> bool
Creates an icosahedron geometry.
auto set_submeshes(const std::vector< submesh > &submeshes) -> bool
auto generate_vertex_barycentrics(uint32_t *adjacency) -> bool
Generates vertex barycentric coordinates for the mesh.
auto get_imported_materials() const -> std::vector< asset_handle< material > >
Gets the imported materials generated during import.
uint32_t face_count_
Total number of vertices in the prepared mesh.
std::vector< uint8_t > byte_array_t
auto get_info() const -> info
auto get_default_material_uids() const -> const std::vector< hpp::uuid > &
Gets the default material UIDs generated during import.
auto get_system_ib() -> uint32_t *
Retrieves the underlying index data from the mesh.
auto calculate_screen_rect(const math::transform &world, const camera &cam) const -> irect32_t
Calculates the screen rectangle of the mesh based on its world transform and the camera....
auto get_vertex_count() const -> uint32_t
Determines the number of vertices stored in the mesh.
auto get_hardware_vb() const -> std::shared_ptr< gfx::vertex_buffer >
Retrieves the hardware vertex buffer for the mesh (shared across all LODs).
auto set_primitives(triangle_array_t &&triangles) -> bool
Adds primitives (triangles) to the mesh.
auto create_teapot(const gfx::vertex_layout &format, bool hardware_copy=true) -> bool
Creates a teapot geometry.
auto end_prepare(bool hardware_copy=true, bool build_buffers=true, bool weld=false, bool optimize=false) -> bool
Ends the preparation of the mesh and builds the render data.
void bind_render_buffers_for_submesh(const submesh *submesh, uint32_t lod_index=0)
Binds the mesh data for rendering the selected batch of primitives.
auto get_bounds() const -> const math::bbox &
Gets the local bounding box for this mesh.
auto create_cone(const gfx::vertex_layout &format, float radius, float radius_tip, float height, uint32_t stacks, uint32_t slices, mesh_create_origin origin, bool hardware_copy=true) -> bool
Creates a cone geometry.
bool force_normal_generation_
Whether to force the generation of vertex barycentric coordinates.
std::vector< hpp::uuid > default_material_uids_
static auto generate_default_lod_configs(const load_data &data, float target_error=0.01f) -> std::vector< std::pair< size_t, float > >
Generates default LOD configurations based on mesh triangle count.
math::bbox bbox_
Total number of faces in the prepared mesh.
auto get_submeshes_count(uint32_t lod_index=0) const -> size_t
Gets the number of submeshes for this mesh.
data_group_submesh_map_t data_groups_
Whether the mesh uses a hardware vertex/index buffer.
auto sort_mesh_data() -> bool
Sorts the data in the mesh into material and data group order.
auto generate_vertex_tangents() -> bool
Generates vertex tangents for the mesh.
auto generate_adjacency(std::vector< uint32_t > &adjacency) -> bool
Generates edge-triangle adjacency information for the mesh data.
auto get_lod_submeshes(uint32_t lod_index) const -> const submesh_array_t *
Gets submeshes for a specific LOD level.
std::shared_ptr< void > hardware_ib_
Additional LOD levels (LOD 1, 2, 3, ...) with simplified index buffers.
uint8_t * system_vb_
The vertex format used for the mesh internal vertex data.
auto create_rounded_cube(const gfx::vertex_layout &format, float width, float height, float depth, uint32_t width_segments, uint32_t height_segments, uint32_t depth_segments, mesh_create_origin origin, bool hardware_copy=true) -> bool
auto get_vertex_format() const -> const gfx::vertex_layout &
Retrieves the format of the underlying mesh vertex data.
auto get_skinned_submeshes_count(uint32_t lod_index=0) const -> size_t
Gets the number of skinned submeshes for this mesh.
bool hardware_mesh_
Whether the mesh was optimized when it was prepared.
auto bind_skin(const skin_bind_data &bind_data) -> bool
Binds the mesh as a skin with the specified skin binding data.
auto get_non_skinned_submeshes_count(uint32_t lod_index=0) const -> size_t
Gets the number of non-skinned submeshes for this mesh.
bool force_tangent_generation_
< Whether to force the generation of tangent space vectors.
auto get_status() const -> mesh_status
Gets the preparation status for this mesh.
auto create_icosphere(const gfx::vertex_layout &format, int tesselation_level, bool hardware_copy=true) -> bool
Creates an icosphere geometry.
uint32_t vertex_count_
Preparation status of the mesh.
auto load_mesh(load_data &&data) -> bool
void dispose()
Clears out all the mesh data.
std::vector< lod_level > lods_
Current LOD count (including base LOD 0).
auto find_submesh_index_by_stable_id(uint32_t stable_id, uint32_t lod_index=0) const -> int
Finds the submesh array index matching an import-stable submesh identifier.
void build_vb(bool hardware_copy=true)
Builds the internal vertex buffer.
auto generate_vertex_components(bool weld) -> bool
Generates any vertex components that may be missing, such as normals, tangents, or binormals.
auto get_lod_face_count(uint32_t lod_index) const -> uint32_t
Gets the face count for a specific LOD level.
auto create_cube(const gfx::vertex_layout &format, float width, float height, float depth, uint32_t width_segments, uint32_t height_segments, uint32_t depth_segments, mesh_create_origin origin, bool hardware_copy=true) -> bool
Creates a cube geometry.
auto get_skinned_submeshes_indices(uint32_t data_group_id, uint32_t lod_index=0) const -> const submesh_array_indices_t &
Gets the indices of skinned submeshes for a specific data group.
auto weld_vertices(float tolerance=0.000001f, std::vector< uint32_t > *vertex_remap_ptr=nullptr) -> bool
Welds the vertices together that can be combined.
auto bind_armature(std::unique_ptr< armature_node > &root) -> bool
Binds the armature tree.
auto get_armature() const -> const std::unique_ptr< armature_node > &
Retrieves the armature tree of the mesh.
bool force_barycentric_generation_
Whether to disable the automatic re-sort operation.
skin_bind_data skin_bind_data_
List of unique combinations of bones to use during rendering.
auto get_submeshes(uint32_t lod_index=0) const -> const submesh_array_t &
Retrieves information about the submesh of the mesh associated with the specified data group identifi...
auto create_cylinder(const gfx::vertex_layout &format, float radius, float height, uint32_t stacks, uint32_t slices, mesh_create_origin origin, bool hardware_copy=true) -> bool
Creates a cylinder geometry.
preparation_data preparation_data_
Data describing how the mesh should be bound as a skin with supplied bone matrices.
auto get_skin_bind_data() const -> const skin_bind_data &
Retrieves the skin bind data if this mesh has been bound as a skin.
std::vector< size_t > submesh_array_indices_t
auto get_hardware_ib(uint32_t lod_index=0) const -> std::shared_ptr< gfx::index_buffer >
Retrieves the hardware index buffer for a given LOD.
auto prepare_mesh(const gfx::vertex_layout &vertex_format) -> bool
Prepares the mesh with the specified vertex format.
auto set_bounding_box(const math::bbox &box) -> bool
auto get_submesh_node_transforms(uint32_t lod_index=0) const -> std::vector< math::transform >
Computes the accumulated node transform for each submesh.
static auto get_max_lod_count() -> uint32_t
Gets the maximum number of LODs that can be used for this mesh including base LOD.
mesh()
Constructs a mesh object.
uint32_t * system_ib_
Material and data group information for each triangle.
auto get_submesh(uint32_t submesh_index=0, uint32_t lod_index=0) const -> const submesh *
auto calculate_screen_rect_precise(const math::transform &world, const camera &cam) const -> irect32_t
Calculates the screen rectangle of the mesh with precise near-plane clipping. This version properly c...
std::vector< bone_palette > bone_palette_array_t
auto get_non_skinned_submeshes_indices(uint32_t data_group_id, uint32_t lod_index=0) const -> const submesh_array_indices_t &
Gets the indices of non-skinned submeshes for a specific data group.
std::vector< submesh * > submesh_array_t
auto get_submesh_index(const submesh *s, uint32_t lod_index=0) const -> int
Gets the index of a submesh within the submesh array.
auto get_data_groups_count() const -> size_t
Gets the number of data groups(materials) for this mesh.
void build_ib(bool hardware_copy=true)
Builds the internal index buffer.
std::vector< triangle > triangle_array_t
auto create_torus(const gfx::vertex_layout &format, float outer_radius, float inner_radius, uint32_t bands, uint32_t sides, mesh_create_origin origin, bool hardware_copy=true) -> bool
Creates a torus geometry.
mesh_status prepare_status_
Input data used for constructing the final mesh.
auto get_face_count() const -> uint32_t
Determines the number of faces stored in the mesh.
auto create_heightfield(const gfx::vertex_layout &format, hpp::span< const float > heights, uint32_t segments_x, uint32_t segments_z, float half_extent_x, float half_extent_z, float height_scale, mesh_create_origin origin, bool hardware_copy=true) -> bool
Creates a heightfield on the XZ plane (Y up): vertex grid (segments_x+1)*(segments_z+1).
submesh_key_array_t triangle_data_
The actual hardware vertex buffer resource (shared by all LODs).
auto get_system_vb() -> uint8_t *
Retrieves the underlying vertex data from the mesh.
static auto get_max_generated_lod_count() -> uint32_t
Gets the maximum number of LODs that can be generated for a mesh.
uint32_t lod_count_
The actual list of submeshes maintained by this mesh.
Structure describing how a skinned mesh should be bound to any bones that influence its vertices.
void build_vertex_table(uint32_t vertex_count, const std::vector< uint32_t > &vertex_remap, vertex_data_array_t &table)
Constructs a list of bone influences and weights for each vertex based on the binding data provided.
void remap_vertices(const std::vector< uint32_t > &remap)
Remaps the vertex references stored in the binding based on the supplied remap array.
auto get_bones() const -> const bone_influence_array_t &
Retrieves a list of all bones that influence the skin in some way.
std::vector< vertex_data > vertex_data_array_t
auto find_bone_by_id(const std::string &id) const -> bone_query
Finds a bone by its unique identifier.
void clear()
Clears out the bone information stored in this object.
auto has_bones() const -> bool
Checks whether the skin data has any bones.
void remove_empty_bones()
Removes any bones that do not contain any influences.
std::vector< vertex_influence > vertex_influence_array_t
void clear_vertex_influences()
Releases memory allocated for vertex influences in each stored bone.
void add_bone(const bone_influence &bone)
Adds influence information for a specific bone.
rect< std::int32_t > irect32_t
#define APPLOG_WARNING(...)
#define APPLOG_ERROR(...)
int count(const generator_t &generator) noexcept
Counts the number of steps left in the generator.
void alloc_transient_index_buffer(transient_index_buffer *_tib, uint32_t _num, bool _index32)
void alloc_transient_vertex_buffer(transient_vertex_buffer *_tvb, uint32_t _num, const vertex_layout &_decl)
void vertex_convert(const vertex_layout &_destDecl, void *_destData, const vertex_layout &_srcDecl, const void *_srcData, uint32_t _num)
bgfx::VertexLayout vertex_layout
uint32_t get_avail_transient_index_buffer(uint32_t _num, bool _index32)
bgfx::Attrib::Enum attribute
const memory_view * make_ref(const void *_data, uint32_t _size, release_fn _releaseFn, void *_userData)
bgfx::TransientVertexBuffer transient_vertex_buffer
void vertex_pack(const float _input[4], bool _inputNormalized, attribute _attr, const vertex_layout &_decl, void *_data, uint32_t _index)
bgfx::TransientIndexBuffer transient_index_buffer
void set_vertex_buffer(uint8_t _stream, vertex_buffer_handle _handle)
auto get_max_blend_transforms() -> uint32_t
void set_index_buffer(index_buffer_handle _handle)
uint32_t get_avail_transient_vertex_buffer(uint32_t _num, const vertex_layout &_decl)
void vertex_unpack(float _output[4], attribute _attr, const vertex_layout &_decl, const void *_data, uint32_t _index)
Hash specialization for batch_key to enable use in std::unordered_map.
auto operator<(const mesh::adjacent_edge_key &key1, const mesh::adjacent_edge_key &key2) -> bool
@ sphere
Sphere type reflection probe.
@ box
Box type reflection probe.
std::vector< uint32_t > indices
Thread-safe handle to an asset.
Storage for box vector values and wraps up common functionality.
bbox & add_point(const vec3 &point)
Grows the bounding box based on the point passed.
void reset()
Resets the bounding box values.
bbox & mul(const transform &t)
Transforms an axis aligned bounding box by the specified matrix.
bool is_populated() const
Checks if the bounding box is populated.
static auto context() -> rtti::context &
Contains level of detail (LOD) data for an entity per view. Uses distance-based hysteresis for stable...
const math::vec3 * vertex2
const math::vec3 * vertex1
< Pointer to the first vertex in the edge.
bone_palette::bone_index_map_t bones
< List of unique bones that influence a given number of faces.
< Structure describing LOD level information. Total number of vertices.
Struct used for mesh construction.
Structure describing a LOD level with its own index buffer and submeshes.
uint32_t face_count_
Submeshes for this LOD (face ranges may differ from base LOD)
std::shared_ptr< void > hardware_ib_
Number of faces in this LOD.
uint32_t * system_ib_
< System memory index buffer for this LOD
submesh_array_t submeshes_
Cached skinned submesh indices per data group.
submesh_array_map_t non_skinned_submesh_indices_
Error metric for this LOD (from meshoptimizer)
submesh_array_map_t skinned_submesh_indices_
Cached non-skinned submesh indices per data group.
float simplification_error_
Structure describing a LOD level in load_data (for serialization).
uint32_t data_group_id
< The data group identifier for this submesh.
bool owns_source
The format of the vertex data currently being used to prepare the mesh.
uint32_t vertex_count
Prepared substs information.
std::vector< uint32_t > vertex_records
Final vertex buffer currently being prepared.
uint32_t triangle_count
Total number of vertices currently stored.
bool compute_binormals
Whether to compute vertex tangents.
triangle_array_t triangle_data
Total number of triangles currently stored.
bool check_for_degenerates
gfx::vertex_layout source_format
Records the location in the vertex buffer that each vertex has been placed during data insertion.
bool compute_normals
Whether to compute vertex binormals.
uint8_t * vertex_source
Whether the source data is owned by this object.
bool compute_tangents
Whether to compute vertex barycentric coordinates.
std::vector< submesh > submeshes
Whether to compute vertex normals.
byte_array_t vertex_data
Additional descriptive information about the vertices.
byte_array_t vertex_flags
Stores the current face/triangle data.
Structure describing an individual "piece" of the mesh, often grouped by material,...
uint32_t vertex_count
The initial face, from the index buffer, to render in this batch.
int32_t vertex_start
Number of vertices included in this batch.
int32_t face_start
Number of faces to render in this batch.
uint32_t data_group_id
< The unique user assigned "data group" that can be used to separate submeshes.
Structure describing data for a single triangle in the mesh.
std::array< uint32_t, 3 > indices
Flags for this triangle.
uint32_t data_group_id
< Data group identifier for this triangle.
Describes the vertices that are connected to the referenced bone and how much influence it has on the...
Contains per-vertex influence and weight information.
int32_t palette
The index of the original vertex.
Describes how a bone influences a specific vertex.