24auto get_cached_submesh_bounds(
const model_submit_extras& extras,
25 uint32_t submesh_index,
26 size_t instance_index,
29 if(extras.proxies ==
nullptr)
33 return skinned ? extras.proxies->get_skinned_bounds(submesh_index)
34 : extras.proxies->get_instance_bounds(submesh_index, instance_index);
44 const model_submit_extras& extras,
45 uint32_t submesh_index,
46 size_t instance_index,
49 const auto* bounds = get_cached_submesh_bounds(extras, submesh_index, instance_index, skinned);
52 return frustum.classify_aabb(*bounds);
59 const model_submit_extras& extras,
60 uint32_t submesh_index,
61 size_t instance_index,
64 return classify_submesh_cached(frustum, extras, submesh_index, instance_index, skinned) !=
72auto resolve_submesh_material(
const model_submit_extras& extras,
73 uint32_t submesh_index,
76 if(extras.material_overrides !=
nullptr && submesh_index < extras.material_overrides->
size())
78 const auto& override_material = (*extras.material_overrides)[submesh_index];
81 return override_material;
84 return group_material;
87auto compute_bounds_screen_radius_squared(
const math::vec3& origin,
89 const math::vec3& view_origin,
90 const math::mat4& projection) ->
float
92 const float screen_multiple = 0.5f * std::max(std::abs(projection[0][0]), std::abs(projection[1][1]));
93 float projection_w_scale = std::abs(projection[2][3]);
94 if(projection_w_scale < 0.000001f)
96 projection_w_scale = 1.0f;
98 const float dist_sqr = glm::length2(origin - view_origin) * projection_w_scale;
103 constexpr float min_dist_sqr = 0.0001f;
104 return math::square(screen_multiple * radius) / std::max(min_dist_sqr, dist_sqr);
107auto compute_bounds_screen_radius_squared(
const math::vec3& origin,
float radius,
const camera&
view) ->
float
109 return compute_bounds_screen_radius_squared(origin, radius,
view.get_position(),
view.get_projection().get_matrix());
112auto compute_submesh_world_bounds_sphere(
const mesh::submesh& sm,
const math::mat4& world_matrix) ->
math::bsphere
115 const math::vec3 local_center = sm.bbox.get_center();
116 const math::vec3 local_extents = sm.bbox.get_extents();
117 const float local_radius = glm::length(local_extents);
118 const math::vec3 world_center = world_transform.transform_coord(local_center);
119 const auto scale = world_transform.get_scale();
120 const float max_scale = std::max({std::abs(
scale.x), std::abs(
scale.y), std::abs(
scale.z)});
121 return math::bsphere{world_center, local_radius * max_scale};
124auto compute_screen_rect_from_sphere(
const camera& cam,
const math::vec3& world_center,
float screen_radius) ->
irect32_t
126 const auto& viewport_pos = cam.get_viewport_pos();
127 const auto& viewport_size = cam.get_viewport_size();
128 if(viewport_size.width == 0 || viewport_size.height == 0)
132 const auto view_proj = cam.get_view_projection();
133 math::vec4 clip = view_proj * math::vec4{world_center.x, world_center.y, world_center.z, 1.0f};
134 const float clip_w = clip.w;
135 if(std::abs(clip_w) < 0.000001f)
137 return {viewport_pos.x,
139 viewport_pos.x +
static_cast<std::int32_t
>(viewport_size.width),
140 viewport_pos.y +
static_cast<std::int32_t
>(viewport_size.height)};
142 const float recip_w = 1.0f / clip_w;
143 const float ndc_x = clip.x * recip_w;
144 const float ndc_y = clip.y * recip_w;
145 const float center_x = ((ndc_x * 0.5f) + 0.5f) *
float(viewport_size.width) + float(viewport_pos.x);
146 const float center_y = ((ndc_y * -0.5f) + 0.5f) *
float(viewport_size.height) + float(viewport_pos.y);
147 const float radius_px = screen_radius * float(viewport_size.height);
148 const float left_f = center_x - radius_px;
149 const float right_f = center_x + radius_px;
150 const float top_f = center_y - radius_px;
151 const float bottom_f = center_y + radius_px;
152 const std::int32_t min_x = viewport_pos.x;
153 const std::int32_t min_y = viewport_pos.y;
154 const std::int32_t max_x = viewport_pos.x +
static_cast<std::int32_t
>(viewport_size.width);
155 const std::int32_t max_y = viewport_pos.y +
static_cast<std::int32_t
>(viewport_size.height);
156 const std::int32_t
left = math::clamp(
static_cast<std::int32_t
>(std::floor(left_f)), min_x, max_x);
157 const std::int32_t
right = math::clamp(
static_cast<std::int32_t
>(std::ceil(right_f)), min_x, max_x);
158 const std::int32_t
top = math::clamp(
static_cast<std::int32_t
>(std::floor(top_f)), min_y, max_y);
159 const std::int32_t
bottom = math::clamp(
static_cast<std::int32_t
>(std::ceil(bottom_f)), min_y, max_y);
169 float screen_radius =
percent * 0.005f;
170 rect = compute_screen_rect_from_sphere(cam,
center, screen_radius);
176 return !mesh_lods_.empty();
181 if(mesh_lods_.empty())
186 lod = math::clamp<std::size_t>(lod, 0, mesh_lods_.size() - 1);
188 for(
int i =
int(lod); i >= 0; --i)
190 auto lod_mesh = mesh_lods_[i];
202 bool recalculate_lod_limits =
false;
203 if(lod >= mesh_lods_.size())
205 mesh_lods_.resize(lod + 1);
207 recalculate_lod_limits =
true;
210 mesh_lods_[lod] =
mesh;
212 if(recalculate_lod_limits)
217 resize_materials(
mesh);
222 if(
index >= materials_.size())
224 materials_.resize(
index + 1);
232 if(
index >= material_instances_.size())
234 material_instances_.resize(
index + 1,
nullptr);
247 if(mesh_lods_.empty())
252 if(mesh_lods_.size() == 1)
254 const auto& mesh_asset = mesh_lods_[0];
255 if(mesh_asset && mesh_asset.get())
257 return mesh_asset.get()->get_lod_count();
261 return static_cast<uint32_t
>(mesh_lods_.size());
270 if(!mesh_lods_.empty())
272 auto&
mesh = mesh_lods_[0];
273 resize_materials(
mesh);
284 return material_instances_;
290 materials_ = materials;
295 material_instances_ = materials;
300 if(materials_.size() <=
index)
305 return materials_[
index];
310 if(
index < material_instances_.size())
312 auto instance = material_instances_[
index];
319 auto instance = get_material(
index);
320 if(instance.is_valid())
322 return instance.get();
330 if(
index >= material_instances_.size())
332 auto asset_instance = get_material_instance(
index);
334 material_instances_.resize(
index + 1,
nullptr);
335 material_instances_[
index] = asset_instance->clone();
338 auto& instance = material_instances_[
index];
342 auto asset_instance = get_material_instance(
index);
347 instance = asset_instance->clone();
352 instance = std::make_shared<pbr_material>();
362 data.transition_time = get_lod_transition_time().count();
363 const auto lod_count = get_lods_count();
364 const auto base_mesh = get_lod(0);
371 if(!world_bounds.is_populated())
378 const math::bsphere bsphere{world_bounds.get_center(), glm::length(world_bounds.get_extents())};
379 const float screen_radius_squared = compute_bounds_screen_radius_squared(bsphere.position, bsphere.radius, cam);
381 const float screen_radius = std::sqrt(std::max(0.0f, screen_radius_squared));
382 data.percent = math::clamp(screen_radius * 200.0f, 0.0f, 100.0f);
383 data.center = bsphere.position;
385 const float lod_screen_size_min = 0.005f;
386 const float cull_threshold_squared =
math::square(lod_screen_size_min * 0.5f);
387 const bool is_visible = cull_threshold_squared <= screen_radius_squared;
394 if(lod_override_enabled_)
396 lod = math::clamp<std::size_t>(lod_override_level_, 0, lod_count - 1);
398 else if(lod_count > 1 && lod_screen_sizes_.size() >= lod_count)
401 const uint32_t prev_lod = data.current_lod_index;
402 const float hysteresis = lod_hysteresis_;
404 for(std::int32_t lod_index =
static_cast<std::int32_t
>(lod_count) - 1; lod_index >= 0; --lod_index)
406 const auto index =
static_cast<size_t>(lod_index);
407 float screen_size = lod_screen_sizes_[
index];
408 float screen_size_squared =
math::square(screen_size * 0.5f);
412 if(prev_lod ==
index)
416 float adjusted_size = screen_size * (1.0f + hysteresis);
417 screen_size_squared =
math::square(adjusted_size * 0.5f);
423 float adjusted_size = screen_size * (1.0f - hysteresis);
424 screen_size_squared =
math::square(adjusted_size * 0.5f);
427 if(screen_size_squared >= screen_radius_squared)
429 lod =
static_cast<std::size_t
>(lod_index);
435 float biased_lod =
static_cast<float>(lod) + lod_selection_bias_;
436 biased_lod = math::clamp(biased_lod, 0.0f,
static_cast<float>(lod_count - 1));
437 lod =
static_cast<std::size_t
>(biased_lod);
441 if(data.target_lod_index != lod && data.target_lod_index == data.current_lod_index)
443 data.target_lod_index =
static_cast<std::uint32_t
>(lod);
444 data.current_time = 0.0f;
448 if(data.current_lod_index != data.target_lod_index)
450 data.current_time += dt;
454 if(data.current_time >= data.transition_time)
456 data.current_lod_index = data.target_lod_index;
457 data.current_time = 0.0f;
463auto model::select_submesh_lod_for_sphere(
const mesh& m,
464 uint32_t submesh_index,
467 const camera& cam)
const -> uint32_t
469 if(lod_override_enabled_)
474 const auto lod_count = get_lods_count();
475 if(lod_count <= base_lod + 1)
484 if(mesh_lods_.size() != 1)
489 if(lod_screen_sizes_.size() < lod_count)
494 const float screen_radius_squared =
495 compute_bounds_screen_radius_squared(world_sphere.position, world_sphere.radius, cam);
501 for(std::int32_t lod_index =
static_cast<std::int32_t
>(lod_count) - 1;
502 lod_index >=
static_cast<std::int32_t
>(base_lod);
505 const float screen_size = lod_screen_sizes_[
static_cast<std::size_t
>(lod_index)];
506 const float screen_size_squared =
math::square(screen_size * 0.5f);
507 if(screen_size_squared < screen_radius_squared)
512 if(submesh_index >=
m.get_submeshes(
static_cast<uint32_t
>(lod_index)).size())
517 return static_cast<uint32_t
>(lod_index);
524 uint32_t submesh_index,
526 const math::mat4& world_matrix,
527 const camera& cam)
const -> uint32_t
530 if(lod_override_enabled_ || mesh_lods_.size() != 1 || get_lods_count() <= base_lod + 1)
535 const auto& base_submeshes = m.get_submeshes(base_lod);
536 if(submesh_index >= base_submeshes.size())
540 const auto* sm = base_submeshes[submesh_index];
541 if(sm ==
nullptr || !sm->bbox.is_populated())
548 const auto sphere = compute_submesh_world_bounds_sphere(*sm, world_matrix);
549 return select_submesh_lod_for_sphere(m, submesh_index, base_lod,
sphere, cam);
553 uint32_t submesh_index,
556 const camera& cam)
const -> uint32_t
558 if(!world_bounds.is_populated())
563 const math::bsphere sphere{world_bounds.get_center(), glm::length(world_bounds.get_extents())};
564 return select_submesh_lod_for_sphere(m, submesh_index, base_lod,
sphere, cam);
570 const auto lod_count = get_lods_count();
576 if(lod_override_enabled_)
578 return math::clamp<uint32_t>(lod_override_level_, 0, lod_count - 1);
581 if(lod_screen_sizes_.size() < lod_count)
586 if(!world_bounds.is_populated())
592 const math::bsphere bsphere{world_bounds.get_center(), glm::length(world_bounds.get_extents())};
593 const float screen_radius_squared = compute_bounds_screen_radius_squared(bsphere.position, bsphere.radius, cam);
596 for(std::int32_t lod_index =
static_cast<std::int32_t
>(lod_count) - 1; lod_index >= 0; --lod_index)
598 const auto index =
static_cast<size_t>(lod_index);
599 const float screen_size = lod_screen_sizes_[
index];
600 const float screen_size_squared =
math::square(screen_size * 0.5f);
601 if(screen_size_squared >= screen_radius_squared)
608 float biased_lod =
static_cast<float>(lod) + lod_selection_bias_ + extra_bias;
609 biased_lod = math::clamp(biased_lod, 0.0f,
static_cast<float>(lod_count - 1));
610 return static_cast<uint32_t
>(biased_lod);
616 lod_screen_sizes_.clear();
621 lod_screen_sizes_.resize(lod_count);
622 for(uint32_t i = 0; i < lod_count; ++i)
626 lod_screen_sizes_[i] = 1.0f;
630 lod_screen_sizes_[i] = 0.3f;
634 lod_screen_sizes_[i] = lod_screen_sizes_[i - 1] * 0.5f;
641 return lod_override_enabled_;
646 lod_override_enabled_ =
enabled;
651 return lod_override_level_;
656 lod_override_level_ = level;
661 return lod_selection_bias_;
666 lod_selection_bias_ = bias;
671 return lod_hysteresis_;
676 lod_hysteresis_ = hysteresis;
681 return lod_transition_time_;
686 lod_transition_time_ = time;
711 return lod_screen_sizes_;
716 lod_screen_sizes_ = sizes;
723 const std::vector<pose_mat4>& skinning_transforms,
730 const auto lod_mesh =
get_lod(lod);
736 auto mesh = lod_mesh.get();
744 const bool per_submesh_lod = cull_submeshes &&
view !=
nullptr;
749 if(non_skinned_submeshes_count > 0)
763 auto render_submesh = [
this, frustum, cull_submeshes, per_submesh_lod,
view, &extras]
764 (
const std::shared_ptr<unravel::mesh>&
mesh,
767 const math::mat4& matrix,
780 const auto resolve = [&](uint32_t submesh_index,
781 const math::mat4& world,
782 size_t instance) -> std::pair<const unravel::mesh::submesh*, uint32_t>
784 const auto* base_sm = submeshes[submesh_index];
787 return {base_sm, lod};
791 const auto* cached_bounds = get_cached_submesh_bounds(extras, submesh_index, instance,
false);
792 const uint32_t effective_lod =
793 cached_bounds !=
nullptr
796 if(effective_lod == lod)
798 return {base_sm, lod};
801 if(submesh_index >= lod_submeshes.size())
803 return {base_sm, lod};
805 const auto* lod_sm = lod_submeshes[submesh_index];
806 return lod_sm !=
nullptr ? std::make_pair(lod_sm, effective_lod)
807 : std::make_pair(base_sm, lod);
812 const auto& mat = resolve_submesh_material(extras,
static_cast<uint32_t
>(
index), group_mat);
820 const size_t transform_count =
pose.get_transform_count(
index);
822 for(
size_t i = 0; i < transform_count; ++i)
824 const auto* transform =
pose.get_transform(
index, i);
832 if(cull_submeshes && !is_submesh_visible_cached(*frustum, extras,
index, i,
false))
837 const auto [sm, sm_lod] = resolve(
static_cast<uint32_t
>(
index), *transform, i);
847 if(cull_submeshes && !is_submesh_visible_cached(*frustum, extras,
index, 0,
false))
852 const auto [sm, sm_lod] = resolve(
static_cast<uint32_t
>(
index), matrix, 0);
863 render_submesh(
mesh, lod, i, world_transform, submesh_transforms, params, callbacks);
873 if(skinned_submeshes_count > 0 && !skinning_transforms.empty())
887 auto render_submesh_skinned = [
this, frustum, cull_submeshes, per_submesh_lod,
view, &extras]
888 (
const std::shared_ptr<unravel::mesh>&
mesh,
892 const std::vector<pose_mat4>& skinning_transforms,
903 if(
index >= skinning_transforms.size())
908 const auto& mat = resolve_submesh_material(extras,
static_cast<uint32_t
>(
index), group_mat);
930 if(cull_submeshes && frustum !=
nullptr)
932 const auto* bounds = get_cached_submesh_bounds(extras,
static_cast<uint32_t
>(
index), 0,
true);
939 const auto& submesh_skinning_transforms = skinning_transforms[
index];
941 if(!submesh_skinning_transforms.transforms.empty())
946 const auto* base_sm = submeshes[
index];
947 const auto* sm = base_sm;
948 uint32_t sm_lod = lod;
951 const auto* bounds = get_cached_submesh_bounds(extras,
static_cast<uint32_t
>(
index), 0,
true);
952 if(bounds !=
nullptr)
956 if(effective_lod != lod)
959 if(
index < lod_submeshes.size() && lod_submeshes[
index] !=
nullptr)
961 sm = lod_submeshes[
index];
962 sm_lod = effective_lod;
980 render_submesh_skinned(
mesh, lod, i, submesh_transforms, skinning_transforms, params, callbacks);
992 const std::vector<pose_mat4>& skinning_transforms,
999 const auto lod_mesh =
get_lod(lod);
1005 auto mesh = lod_mesh.get();
1009 if(!vb || !ib || !vb->is_valid() || !ib->is_valid())
1017 constexpr uint32_t float_size =
static_cast<uint32_t
>(
sizeof(float));
1019 const uint32_t stride_bytes = vertex_format.getStride();
1020 const uint32_t pos_offset_bytes = vertex_format.getOffset(gfx::attribute::Position);
1029 const bool per_submesh_lod = cull_submeshes &&
view !=
nullptr;
1039 auto bind_and_submit = [&](uint32_t submesh_index, uint32_t effective_lod) ->
void
1041 const auto& lod_submeshes = (effective_lod == lod) ? submeshes :
mesh->
get_submeshes(effective_lod);
1042 if(submesh_index >= lod_submeshes.size())
1046 const auto* sub = lod_submeshes[submesh_index];
1047 if(!sub || sub->face_count == 0)
1052 const auto effective_ib = (effective_lod == lod) ? ib :
mesh->
get_hardware_ib(effective_lod);
1053 if(!effective_ib || !effective_ib->is_valid())
1059 params.
index_start =
static_cast<uint32_t
>(sub->face_start) * 3u;
1072 if(non_skinned_count > 0)
1087 for(uint32_t group_id = 0; group_id < group_count; ++group_id)
1092 const auto* sub = submeshes[
index];
1093 if(!sub || sub->face_count == 0)
1103 for(
size_t j = 0; j < transform_count; ++j)
1114 if(cull_submeshes && !is_submesh_visible_cached(*frustum, extras,
index, j,
false))
1118 uint32_t effective_lod = lod;
1121 const auto* cached_bounds =
1122 get_cached_submesh_bounds(extras,
static_cast<uint32_t
>(
index), j,
false);
1124 cached_bounds !=
nullptr
1126 static_cast<uint32_t
>(
index),
1133 bind_and_submit(
static_cast<uint32_t
>(
index), effective_lod);
1138 if(cull_submeshes && !is_submesh_visible_cached(*frustum, extras,
index, 0,
false))
1142 const uint32_t effective_lod = per_submesh_lod
1146 bind_and_submit(
static_cast<uint32_t
>(
index), effective_lod);
1160 if(skinned_count > 0 && !skinning_transforms.empty()
1161 && vertex_format.has(gfx::attribute::Weight) && vertex_format.has(gfx::attribute::Indices))
1176 for(uint32_t group_id = 0; group_id < group_count; ++group_id)
1181 if(
index >= skinning_transforms.size())
1185 const auto& bones = skinning_transforms[
index];
1186 if(bones.transforms.empty())
1190 const auto* sub = submeshes[
index];
1191 if(!sub || sub->face_count == 0)
1208 if(cull_submeshes && frustum !=
nullptr)
1210 const auto* bounds = get_cached_submesh_bounds(extras,
static_cast<uint32_t
>(
index), 0,
true);
1220 bind_and_submit(
static_cast<uint32_t
>(
index), lod);
1234 const auto m =
mesh.get();
1235 auto submeshes = m->get_data_groups_count();
1236 if(materials_.size() != submeshes)
1255 const math::mat4& world_transform,
1263 auto mesh_asset =
get_lod(lod_index);
1269 auto mesh = mesh_asset.get();
1278 const bool per_submesh_lod = cull_submeshes &&
view !=
nullptr;
1283 for (uint32_t data_group_id = 0; data_group_id < data_group_count; ++data_group_id)
1292 for (
size_t submesh_idx : submesh_indices)
1294 uint32_t submesh_index =
static_cast<uint32_t
>(submesh_idx);
1298 const auto& material_ptr = resolve_submesh_material(extras, submesh_index, group_material);
1310 for (
size_t instance_idx = 0; instance_idx < transform_count; ++instance_idx)
1312 const math::mat4* transform_ptr = submesh_transforms.
get_transform(submesh_index, instance_idx);
1324 if(cull_submeshes && !is_submesh_visible_cached(*frustum, extras, submesh_index, instance_idx,
false))
1329 uint32_t effective_lod = lod_index;
1332 const auto* cached_bounds =
1333 get_cached_submesh_bounds(extras, submesh_index, instance_idx,
false);
1335 cached_bounds !=
nullptr
1344 batch_key key(
mesh, material_ptr, effective_lod, submesh_index);
1345 if (!key.is_valid())
1360 if(cull_submeshes && !is_submesh_visible_cached(*frustum, extras, submesh_index, 0,
false))
1365 const uint32_t effective_lod = per_submesh_lod
1369 batch_key key(
mesh, material_ptr, effective_lod, submesh_index);
1370 if (!key.is_valid())
1387 uint8_t cascade_count,
1388 const math::mat4& world_transform,
1393 bool nested_cascades,
1396 auto mesh_asset = get_lod(lod_index);
1401 auto mesh = mesh_asset.get();
1407 bool collected_any =
false;
1408 auto collect_into_cascades =
1409 [&](
const shadow_batch_key& key, uint32_t submesh_index,
const math::mat4& transform,
size_t instance_idx) ->
void
1411 for(uint8_t ii = 0; ii < cascade_count; ++ii)
1413 const auto query = classify_submesh_cached(frustums[ii], extras, submesh_index, instance_idx,
false);
1421 collectors[ii].collect_renderable(key, instance);
1422 collected_any =
true;
1432 for(uint32_t data_group_id = 0; data_group_id < data_group_count; ++data_group_id)
1434 auto group_material = get_material_instance(data_group_id);
1437 for(
size_t submesh_idx : submesh_indices)
1439 const uint32_t submesh_index =
static_cast<uint32_t
>(submesh_idx);
1441 const auto& material_ptr = resolve_submesh_material(extras, submesh_index, group_material);
1453 if(submesh_transforms.has_transforms(submesh_index))
1455 const size_t transform_count = submesh_transforms.get_transform_count(submesh_index);
1456 for(
size_t instance_idx = 0; instance_idx < transform_count; ++instance_idx)
1458 const math::mat4* transform_ptr = submesh_transforms.get_transform(submesh_index, instance_idx);
1463 if(!submesh_transforms.get_transform_casts_shadow(submesh_index, instance_idx))
1467 collect_into_cascades(key, submesh_index, *transform_ptr, instance_idx);
1472 collect_into_cascades(key, submesh_index, world_transform, 0);
1477 return collected_any;
Provides storage for common representation of spherical bounding volume, and wraps up common function...
Storage for frustum planes / values and wraps up common functionality.
auto classify_aabb(const bbox &bounds) const -> volume_query
Classifies an axis-aligned bounding box (AABB) with respect to the frustum.
void collect_renderable(const Key &key, const batch_instance &instance)
Class representing a camera. Contains functionality for manipulating and updating a camera....
auto get_viewport_pos() const -> const ipoint32_t &
Retrieves the position of the viewport.
auto get_viewport_size() const -> const usize32_t &
Retrieves the size of the viewport.
Base class for materials used in rendering.
std::shared_ptr< material > sptr
Main class representing a 3D mesh with support for different LODs, submeshes, and skinning.
auto get_hardware_vb() const -> std::shared_ptr< gfx::vertex_buffer >
Retrieves the hardware vertex buffer for the mesh (shared across all LODs).
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_submeshes_count(uint32_t lod_index=0) const -> size_t
Gets the number of submeshes for this mesh.
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.
auto get_non_skinned_submeshes_count(uint32_t lod_index=0) const -> size_t
Gets the number of non-skinned submeshes for this mesh.
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 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 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 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.
auto get_data_groups_count() const -> size_t
Gets the number of data groups(materials) for this mesh.
void submit_for_vertex_pulling(const math::mat4 &world_transform, const submesh_pose_mat4 &submesh_transforms, const std::vector< pose_mat4 > &skinning_transforms, unsigned int lod, const submit_vertex_pulling_callbacks &callbacks, const math::frustum *frustum=nullptr, const camera *view=nullptr, const model_submit_extras &extras={}) const
Submits the model using vertex-pulling rendering.
auto get_material(uint32_t index) const -> asset_handle< material >
Gets the material for the specified index.
void set_lod_override_enabled(bool enabled)
Sets whether LOD override is enabled.
auto calculate_submesh_lod(const mesh &m, uint32_t submesh_index, uint32_t base_lod, const math::mat4 &world_matrix, const camera &cam) const -> uint32_t
Selects a LOD for a specific submesh based on its own screen size.
auto get_lod_screen_size_min() const -> float
Gets the minimum screen size used by the screen-radius-squared LOD and culling method.
auto get_lod(uint32_t lod) const -> asset_handle< mesh >
Gets the LOD (Level of Detail) mesh for the specified level.
void set_lod_transition_time(seconds_t time)
Sets the LOD transition time in seconds.
auto calculate_submesh_lod_from_world_bounds(const mesh &m, uint32_t submesh_index, uint32_t base_lod, const math::bbox &world_bounds, const camera &cam) const -> uint32_t
Selects a LOD for a submesh from an already-known world-space AABB.
static auto fallback_material() -> asset_handle< material > &
Gets the fallback material.
void set_material(asset_handle< material > material, uint32_t index)
Sets the material for the specified index.
auto get_lod_override_level() const -> uint32_t
Gets the LOD override level.
void set_lod_screen_sizes(const std::vector< float > &sizes)
Sets the per-LOD screen size table used by the screen-radius-squared method.
auto is_valid() const -> bool
Checks if the model is valid.
void set_materials(const std::vector< asset_handle< material > > &materials)
Sets the materials.
void set_material_instances(const std::vector< material::sptr > &materials)
static auto default_material() -> asset_handle< material > &
Gets the default material.
void submit(const math::mat4 &world_transform, const submesh_pose_mat4 &submesh_transforms, const pose_mat4 &bone_transforms, const std::vector< pose_mat4 > &skinning_transforms, unsigned int lod, const submit_callbacks &callbacks, const math::frustum *frustum=nullptr, const camera *view=nullptr, const model_submit_extras &extras={}) const
Submits the model for rendering.
void submit_for_batching(batch_collector &collector, const math::mat4 &world_transform, const submesh_pose_mat4 &submesh_transforms, uint32_t lod_index, float lod_param=0.0f, const math::frustum *frustum=nullptr, const camera *view=nullptr, const model_submit_extras &extras={}) const
Collects this model into a batch collector for instanced rendering.
auto get_material_instance(uint32_t index) const -> material::sptr
void set_lod_auto_screen_size_power_base(float value)
Sets the auto LOD screen size power base (used for generating a screen-size table).
void set_lod_screen_size_min(float value)
Sets the minimum screen size used by the screen-radius-squared LOD and culling method.
auto get_lod_selection_bias() const -> float
Gets the LOD selection bias.
auto get_material_instances() const -> const std::vector< material::sptr > &
void set_lod_hysteresis(float hysteresis)
Sets the LOD hysteresis factor.
std::chrono::duration< float > seconds_t
void set_lod_selection_bias(float bias)
Sets the LOD selection bias.
auto get_lod_override_enabled() const -> bool
Gets whether LOD override is enabled.
auto submit_for_shadow_batching_cascaded(std::vector< shadow_batch_collector > &collectors, uint8_t cascade_count, const math::mat4 &world_transform, const submesh_pose_mat4 &submesh_transforms, uint32_t lod_index, float lod_param, const math::frustum *frustums, bool nested_cascades, const model_submit_extras &extras={}) const -> bool
Collects shadow-map geometry into per-cascade shadow batch collectors. Batches by mesh/lod/submesh/cu...
void set_material_instance(material::sptr material, uint32_t index)
void set_lods(const std::vector< asset_handle< mesh > > &lods)
Sets the LOD meshes.
auto calculate_lod_data(lod_data &data, const math::bbox &world_bounds, const camera &cam, float dt) const -> bool
Calculates the LOD data for the model using distance-based hysteresis with time-based transitions....
auto get_lods_count() const -> uint32_t
Gets the number of LOD levels available. If there is only one explicit mesh, returns the internal LOD...
auto get_lod_screen_sizes() const -> const std::vector< float > &
Gets the per-LOD screen size table used by the screen-radius-squared method.
void set_lod_override_level(uint32_t level)
Sets the LOD override level.
auto get_or_emplace_material_instance(uint32_t index) -> material::sptr
void recalulate_lod_screen_size_limits(uint32_t lod_count)
Recalculates the screen-size LOD thresholds for the provided LOD count. This is a separate mechanism ...
void set_lod(asset_handle< mesh > mesh, uint32_t lod)
Sets the LOD (Level of Detail) mesh for the specified level.
auto get_lod_auto_screen_size_power_base() const -> float
Gets the auto LOD screen size power base (used for generating a screen-size table).
auto get_materials() const -> const std::vector< asset_handle< material > > &
Gets all the materials.
auto get_lod_hysteresis() const -> float
Gets the LOD hysteresis factor used to prevent rapid LOD switching.
auto get_lods() const -> const std::vector< asset_handle< mesh > > &
Gets all the LOD meshes.
auto get_lod_transition_time() const -> seconds_t
Gets the LOD transition time in seconds.
auto compute_lod_index(const math::bbox &world_bounds, const camera &cam, float extra_bias=0.0f) const -> uint32_t
Computes a LOD index for this model without hysteresis, transitions or visibility culling.
void set_world_transform(const void *_mtx, uint16_t _num)
void set_buffer(uint8_t _stage, index_buffer_handle _handle, access _access)
Hash specialization for batch_key to enable use in std::unordered_map.
auto make_shadow_batch_key(const std::shared_ptr< mesh > &mesh_ptr, uint32_t lod_index, uint32_t submesh_index, const std::shared_ptr< material > &material_ptr) -> shadow_batch_key
Build a shadow batch key from mesh geometry and optional material cutout metadata.
@ sphere
Sphere type reflection probe.
std::vector< float > scale
std::vector< uint32_t > indices
Thread-safe handle to an asset.
Storage for box vector values and wraps up common functionality.
Instance data for a single object in a batch.
math::vec3 lod_params
LOD blending parameters (x = transition factor: +[0,1] = fade out, -[0,1] = fade in; y,...
Batch key structure for grouping compatible draw calls.
Contains level of detail (LOD) data for an entity per view. Uses distance-based hysteresis for stable...
void calculate_screen_rect(const camera &cam)
float percent
Percentage of the model visible (0.0 to 100.0).
math::vec3 center
Center of the model in world space.
Parameters for the submit callbacks.
bool skinned
Indicates if the model is skinned.
Callbacks for submitting the model for rendering.
std::function< void(const params &info, const material &)> setup_params_per_submesh
Callback for setting up per submesh.
std::function< void(const params &info)> setup_begin
Callback for setup begin.
std::function< void(const params &info)> setup_params_per_instance
Callback for setting up per instance.
std::function< void(const params &info)> setup_end
Callback for setup end.
Per-invocation information for a vertex-pulling submesh submit.
uint32_t position_offset_floats
Byte offset of the position attribute converted to floats.
uint32_t index_count
Number of indices making up the submesh.
bool preserve_state
Hint: mirror submit_callbacks::params::preserve_state.
uint32_t vertex_stride_floats
Vertex stride expressed in float-sized elements.
uint32_t indices_offset_floats
Byte offset of the bone indices attribute converted to floats.
uint32_t submesh_index
Submesh index within the LOD mesh.
bool skinned
True during the skinned pass, false during non-skinned.
uint32_t index_start
Starting index of the submesh in the index buffer (in indices).
uint32_t weight_offset_floats
Byte offset of the bone weight attribute converted to floats.
Callbacks for submitting the model using vertex-pulling rendering.
std::function< void(const params &info)> setup_params_per_instance
Called once per pass after setup_begin. Typically used to set instance-level uniforms.
std::function< void(const params &info)> setup_params_per_submesh
Called once per submesh instance after u_world and the raw VB/IB have been bound.
std::function< void(const params &info)> setup_begin
Called once per pass (once for non-skinned, once for skinned). Typically used to bind the program.
std::function< void(const params &info)> setup_end
Called once per pass at the end. Typically used to end the program.
Batch key for shadow depth passes — geometry-first, optional cutout bucket.
auto has_transforms(uint32_t submesh_index) const -> bool
Checks if a submesh has any transforms.
auto get_transform_casts_shadow(uint32_t submesh_index, size_t instance_index) const -> bool
Checks if an instance casts shadows.
auto get_transform_active(uint32_t submesh_index, size_t instance_index) const -> bool
Checks if a transform is active.
auto get_transform(uint32_t submesh_index, size_t instance_index) const -> const math::mat4 *
Gets a specific transform for a submesh by its instance index.
auto get_transform_count(uint32_t submesh_index) const -> size_t
Gets the number of transform instances for a specific submesh.