Unravel Engine C++ Reference
Loading...
Searching...
No Matches
defaults.cpp
Go to the documentation of this file.
1#include "defaults.h"
2
4
29
30#include <logging/logging.h>
31#include <string_utils/utils.h>
32#include <seq/seq.h>
33
34#include <graphics/graphics.h>
35
36#include <algorithm>
37#include <array>
38#include <cmath>
39#include <limits>
40#include <vector>
41
42namespace unravel
43{
44
45namespace
46{
47
48// Result of casting a ray against the renderable meshes in a scene.
49struct scene_ray_hit
50{
51 bool hit = false;
52 math::vec3 position{0.0f, 0.0f, 0.0f};
53 math::vec3 normal{0.0f, 1.0f, 0.0f};
54};
55
56// Moller-Trumbore ray/triangle intersection (two sided). origin/dir and the triangle vertices
57// must all live in the same coordinate space. On success out_t holds the ray parameter such that
58// the hit point is origin + dir * out_t.
59auto intersect_ray_triangle(const math::vec3& origin,
60 const math::vec3& dir,
61 const math::vec3& v0,
62 const math::vec3& v1,
63 const math::vec3& v2,
64 float& out_t) -> bool
65{
66 constexpr float epsilon = 1e-7f;
67 const math::vec3 edge1 = v1 - v0;
68 const math::vec3 edge2 = v2 - v0;
69 const math::vec3 pvec = math::cross(dir, edge2);
70 const float det = math::dot(edge1, pvec);
71 if(det > -epsilon && det < epsilon)
72 {
73 return false; // Ray is parallel to the triangle plane.
74 }
75 const float inv_det = 1.0f / det;
76 const math::vec3 tvec = origin - v0;
77 const float u = math::dot(tvec, pvec) * inv_det;
78 if(u < 0.0f || u > 1.0f)
79 {
80 return false;
81 }
82 const math::vec3 qvec = math::cross(tvec, edge1);
83 const float v = math::dot(dir, qvec) * inv_det;
84 if(v < 0.0f || u + v > 1.0f)
85 {
86 return false;
87 }
88 const float t = math::dot(edge2, qvec) * inv_det;
89 if(t <= epsilon)
90 {
91 return false; // Intersection is behind the ray origin.
92 }
93 out_t = t;
94 return true;
95}
96
97// Tests a single mesh submesh for the closest triangle hit along the ray. The ray is expected in the
98// submesh-local space described by combined; closest_world_t and out_hit are updated in world space.
99void raycast_mesh_submesh(mesh& msh,
100 const mesh::submesh& submesh,
101 const math::transform& combined,
102 const math::vec3& ray_origin,
103 const math::vec3& ray_dir,
104 float& closest_world_t,
105 scene_ray_hit& out_hit)
106{
107 const auto* vertex_data = msh.get_system_vb();
108 const auto* index_data = msh.get_system_ib();
109 if(vertex_data == nullptr || index_data == nullptr)
110 {
111 return;
112 }
113 const auto& vertex_format = msh.get_vertex_format();
114 if(!vertex_format.has(gfx::attribute::Position))
115 {
116 return;
117 }
118 const auto face_count = msh.get_face_count();
119 if(submesh.face_start < 0 || submesh.face_count == 0)
120 {
121 return;
122 }
123 const auto face_begin = static_cast<uint32_t>(submesh.face_start);
124 if(face_begin >= face_count)
125 {
126 return;
127 }
128 const auto face_end = std::min(face_begin + submesh.face_count, face_count);
129
130 // Move the ray into submesh-local space so the stored vertex positions can be used directly.
131 const math::transform inv_combined = math::inverse(combined);
132 const math::vec3 local_origin = inv_combined.transform_coord(ray_origin);
133 const math::vec3 local_dir = inv_combined.transform_coord(ray_origin + ray_dir) - local_origin;
134
135 for(uint32_t f = face_begin; f < face_end; ++f)
136 {
137 const uint32_t i0 = index_data[f * 3 + 0];
138 const uint32_t i1 = index_data[f * 3 + 1];
139 const uint32_t i2 = index_data[f * 3 + 2];
140
141 std::array<float, 4> p0{};
142 std::array<float, 4> p1{};
143 std::array<float, 4> p2{};
144 gfx::vertex_unpack(p0.data(), gfx::attribute::Position, vertex_format, vertex_data, i0);
145 gfx::vertex_unpack(p1.data(), gfx::attribute::Position, vertex_format, vertex_data, i1);
146 gfx::vertex_unpack(p2.data(), gfx::attribute::Position, vertex_format, vertex_data, i2);
147
148 const math::vec3 v0{p0[0], p0[1], p0[2]};
149 const math::vec3 v1{p1[0], p1[1], p1[2]};
150 const math::vec3 v2{p2[0], p2[1], p2[2]};
151
152 float local_t = 0.0f;
153 if(!intersect_ray_triangle(local_origin, local_dir, v0, v1, v2, local_t))
154 {
155 continue;
156 }
157
158 // Compare hits in world space so submeshes with different scales remain consistent.
159 const math::vec3 world_hit = combined.transform_coord(local_origin + local_dir * local_t);
160 const float world_t = math::dot(world_hit - ray_origin, ray_dir);
161 if(world_t <= 0.0f || world_t >= closest_world_t)
162 {
163 continue;
164 }
165
166 closest_world_t = world_t;
167 out_hit.hit = true;
168 out_hit.position = world_hit;
169 const math::vec3 local_normal = math::normalize(math::cross(v1 - v0, v2 - v0));
170 out_hit.normal = math::normalize(combined.transform_normal(local_normal));
171 }
172}
173
174// Casts a ray from the camera through the supplied viewport position and returns the closest triangle
175// hit across every active model in the scene. Falls back to no-hit when the ray misses all geometry.
176auto raycast_scene_closest_surface(scene& scn, const camera& cam, const math::vec2& screen_pos) -> scene_ray_hit
177{
178 scene_ray_hit result;
179
180 math::vec3 ray_origin;
181 math::vec3 ray_dir;
182 if(!cam.viewport_to_ray(screen_pos, ray_origin, ray_dir))
183 {
184 return result;
185 }
186 ray_dir = math::normalize(ray_dir);
187
188 float closest_world_t = std::numeric_limits<float>::max();
189
190 scn.registry->view<transform_component, model_component, active_component>().each(
191 [&](auto e, auto&& transform_comp, auto&& model_comp, auto&&) -> void
192 {
193 if(!model_comp.is_enabled())
194 {
195 return;
196 }
197 const auto& mdl = model_comp.get_model();
198 if(!mdl.is_valid())
199 {
200 return;
201 }
202 auto lod = mdl.get_lod(0);
203 if(!lod)
204 {
205 return;
206 }
207 const auto mesh_ptr = lod.get();
208 if(!mesh_ptr)
209 {
210 return;
211 }
212
213 const auto& world_transform = transform_comp.get_transform_global();
214
215 // Broadphase: reject meshes whose world-space bounds the ray never enters, or that lie
216 // entirely beyond the closest hit found so far.
217 math::bbox world_bounds = math::bbox::mul(mesh_ptr->get_bounds(), world_transform);
218 float box_t = 0.0f;
219 if(!world_bounds.intersect(ray_origin, ray_dir, box_t, false))
220 {
221 return;
222 }
223 if(box_t > closest_world_t)
224 {
225 return;
226 }
227
228 const auto& submeshes = mesh_ptr->get_submeshes();
229 const auto node_transforms = mesh_ptr->get_submesh_node_transforms();
230 for(size_t s = 0; s < submeshes.size(); ++s)
231 {
232 const auto* submesh = submeshes[s];
233 if(submesh == nullptr)
234 {
235 continue;
236 }
237 math::transform combined = world_transform;
238 if(s < node_transforms.size())
239 {
240 combined = world_transform * node_transforms[s];
241 }
242 raycast_mesh_submesh(*mesh_ptr, *submesh, combined, ray_origin, ray_dir, closest_world_t, result);
243 }
244 });
245
246 return result;
247}
248
249// Where a dropped object should be placed and how it relates to the surface it landed on.
250struct drop_placement
251{
252 math::vec3 contact{0.0f, 0.0f, 0.0f}; // World-space contact point (surface hit or ground plane).
253 math::vec3 normal{0.0f, 1.0f, 0.0f}; // Surface normal at the contact point (world up when no hit).
254 bool on_surface = false; // True when the cursor was over existing geometry.
255};
256
257// Resolves the contact point for a viewport drop. Prefers the exact surface hit under the cursor and
258// falls back to projecting onto the world ground plane (XZ at the origin) when nothing is hit.
259auto compute_drop_placement(scene& scn, const camera& cam, const math::vec2& screen_pos) -> drop_placement
260{
261 const auto surface = raycast_scene_closest_surface(scn, cam, screen_pos);
262 if(surface.hit)
263 {
264 return {surface.position, surface.normal, true};
265 }
266
267 math::vec3 projected_pos{0.0f, 0.0f, 0.0f};
268 cam.viewport_to_world(screen_pos,
269 math::plane::from_point_normal(math::vec3{0.0f, 0.0f, 0.0f}, math::vec3{0.0f, 1.0f, 0.0f}),
270 projected_pos,
271 false);
272 return {projected_pos, math::vec3{0.0f, 1.0f, 0.0f}, false};
273}
274
275// Builds the shortest-arc rotation that maps the 'from' direction onto the 'to' direction.
276auto shortest_arc_rotation(const math::vec3& from, const math::vec3& to) -> math::quat
277{
278 const math::vec3 f = math::normalize(from);
279 const math::vec3 t = math::normalize(to);
280 const float d = math::dot(f, t);
281 if(d >= 1.0f - 1e-6f)
282 {
283 return math::quat(1.0f, 0.0f, 0.0f, 0.0f); // Already aligned.
284 }
285 if(d <= -1.0f + 1e-6f)
286 {
287 // Opposite directions: rotate 180 degrees around any orthogonal axis.
288 math::vec3 axis = math::cross(math::vec3{1.0f, 0.0f, 0.0f}, f);
289 if(math::dot(axis, axis) < 1e-6f)
290 {
291 axis = math::cross(math::vec3{0.0f, 0.0f, 1.0f}, f);
292 }
293 return math::angleAxis(math::pi<float>(), math::normalize(axis));
294 }
295 const math::vec3 axis = math::normalize(math::cross(f, t));
296 return math::angleAxis(std::acos(d), axis);
297}
298
299// Accumulates the object's geometry bounds expressed in the root entity's local frame (inv_root). This
300// mirrors calc_bounds_global_impl but keeps the box in the object's own oriented frame instead of
301// re-aligning to the world axes, so a rotated object yields a tight oriented box rather than an inflated
302// world AABB whose base dips below the actual geometry.
303void accumulate_obb_local(math::bbox& local_bounds, entt::handle entity, const math::transform& inv_root, int depth)
304{
305 auto& tc = entity.get<transform_component>();
306 const auto world_xform = tc.get_transform_global();
307 const math::transform to_root = inv_root * world_xform;
308
309 if(auto* mc = entity.try_get<model_component>())
310 {
311 const auto& b = mc->get_local_bounds(0);
312 if(b.is_populated())
313 {
314 for(const auto& corner : b.get_corners())
315 {
316 local_bounds.add_point(to_root.transform_coord(corner));
317 }
318 }
319 }
320
321 if(auto* txt = entity.try_get<text_component>())
322 {
323 const auto b = txt->get_render_bounds();
324 for(const auto& corner : b.get_corners())
325 {
326 local_bounds.add_point(to_root.transform_coord(corner));
327 }
328 }
329
330 if(auto* pc = entity.try_get<particle_emitter_component>())
331 {
332 // Particle bounds come back in world space, so bring them straight into the root frame.
333 const auto b = pc->get_updated_world_bounds(world_xform);
334 for(const auto& corner : b.get_corners())
335 {
336 local_bounds.add_point(inv_root.transform_coord(corner));
337 }
338 }
339
340 if(depth != 0)
341 {
342 for(auto child : tc.get_children())
343 {
344 accumulate_obb_local(local_bounds, child, inv_root, depth > 0 ? depth - 1 : -1);
345 }
346 }
347}
348
349// Returns the eight world-space corners of the object's oriented bounding box. Returns false when the
350// object has no renderable bounds to rest on.
351auto calc_obb_world_corners(entt::handle entity, std::array<math::vec3, 8>& out_corners, int depth = -1) -> bool
352{
353 if(!entity || !entity.all_of<transform_component>())
354 {
355 return false;
356 }
357 auto& tc = entity.get<transform_component>();
358 const auto root_world = tc.get_transform_global();
359 const auto inv_root = math::inverse(root_world);
360
362 accumulate_obb_local(local, entity, inv_root, depth);
363 if(!local.is_populated())
364 {
365 return false;
366 }
367
368 const auto local_corners = local.get_corners();
369 for(size_t i = 0; i < local_corners.size(); ++i)
370 {
371 out_corners[i] = root_world.transform_coord(local_corners[i]);
372 }
373 return true;
374}
375
376// Shifts the entity along up_dir so the base of its oriented bounding box rests on the plane that passes
377// through contact with normal up_dir. Uses the object's oriented box (not the inflated world AABB) so a
378// rotated object sits flush on the surface instead of floating above it.
379void rest_entity_on_contact(entt::handle object, const math::vec3& contact, const math::vec3& up_dir)
380{
381 if(!object || !object.all_of<transform_component>())
382 {
383 return;
384 }
385 std::array<math::vec3, 8> corners{};
386 if(!calc_obb_world_corners(object, corners))
387 {
388 return;
389 }
390 float min_proj = std::numeric_limits<float>::max();
391 for(const auto& corner : corners)
392 {
393 min_proj = std::min(min_proj, math::dot(corner - contact, up_dir));
394 }
395 auto& tc = object.get<transform_component>();
396 tc.set_position_global(tc.get_position_global() - up_dir * min_proj);
397}
398
399// Applies surface-aware placement to a freshly created object: optionally aligns it to the surface
400// normal, then rests the base of its bounding box on the contact point.
401void finalize_surface_placement(entt::handle object, const drop_placement& placement, bool align_to_normal)
402{
403 if(!object || !object.all_of<transform_component>())
404 {
405 return;
406 }
407 math::vec3 up_dir{0.0f, 1.0f, 0.0f};
408 if(align_to_normal && placement.on_surface)
409 {
410 auto& tc = object.get<transform_component>();
411 const math::quat align = shortest_arc_rotation(up_dir, placement.normal);
412 tc.set_rotation_global(align * tc.get_rotation_global());
413 up_dir = placement.normal;
414 }
415 rest_entity_on_contact(object, placement.contact, up_dir);
416}
417
418// Add a shared helper to drive the timed camera focus transition
419void run_camera_focus_transition(entt::handle camera,
420 const math::vec3& target_center,
421 float radius,
422 bool keep_rotation,
423 float duration)
424{
425 if(!camera.all_of<transform_component, camera_component>())
426 {
427 return;
428 }
429
430 auto& trans_comp = camera.get<transform_component>();
431 auto& camera_comp = camera.get<camera_component>();
432 const auto& cam = camera_comp.get_camera();
433
434 float aspect = cam.get_aspect_ratio();
435 float fov = cam.get_fov();
436 float horizontal_fov = math::degrees(2.0f * math::atan(math::tan(math::radians(fov) / 2.0f) * aspect));
437 float mfov = math::min(fov, horizontal_fov);
438 float target_distance = radius / (math::sin(math::radians(mfov) / 2.0f));
439
440 math::vec3 start_position = trans_comp.get_position_global();
441 float start_ortho_size = camera_comp.get_ortho_size();
442
443 // Calculate initial target position to check proximity
444 math::vec3 initial_target_position{};
445 if(keep_rotation)
446 {
447 // Keep camera rotation unchanged: move along current forward (Z axis) so center is on view axis
448 math::vec3 forward = math::normalize(trans_comp.get_z_axis_global());
449 if(math::length(forward) < 0.001f)
450 {
451 forward = math::vec3{0.0f, 0.0f, -1.0f};
452 }
453 initial_target_position = target_center - target_distance * forward;
454 }
455 else
456 {
457 // Point camera towards the center using current position direction
458 math::vec3 dir = math::normalize(target_center - start_position);
459 if(math::length(dir) < 0.001f)
460 {
461 dir = math::vec3{0.0f, 0.0f, -1.0f};
462 }
463 initial_target_position = target_center - target_distance * dir;
464 }
465
466 // Check if we're already very close to the calculated target position
467 float distance_to_target = math::length(start_position - initial_target_position);
468 float proximity_threshold = target_distance * 0.15f; // 15% of target distance
469
470 // If we're very close, back away to provide a wider view before focusing
471 float adjusted_target_distance = target_distance;
472 if(distance_to_target < proximity_threshold)
473 {
474 // Increase distance by 50% to back away and provide wider view
475 adjusted_target_distance = target_distance * 3.0f;
476 }
477
478 // Calculate final target position with adjusted distance
479 math::vec3 target_position{};
480 if(keep_rotation)
481 {
482 math::vec3 forward = math::normalize(trans_comp.get_z_axis_global());
483 if(math::length(forward) < 0.001f)
484 {
485 forward = math::vec3{0.0f, 0.0f, -1.0f};
486 }
487 target_position = target_center - adjusted_target_distance * forward;
488 }
489 else
490 {
491 math::vec3 dir = math::normalize(target_center - start_position);
492 if(math::length(dir) < 0.001f)
493 {
494 dir = math::vec3{0.0f, 0.0f, -1.0f};
495 }
496 target_position = target_center - adjusted_target_distance * dir;
497 }
498
499 if(duration <= 0.0f)
500 {
501 trans_comp.set_position_global(target_position);
502 if(!keep_rotation)
503 {
504 trans_comp.look_at(target_center);
505 }
506 camera_comp.set_ortho_size(radius);
507 camera_comp.update(trans_comp.get_transform_global());
508 seq::scope::stop_all("camera_focus");
509 return;
510 }
511
512 auto ease = seq::ease::smooth_stop;
513 auto seq_duration = std::chrono::duration_cast<seq::duration_t>(std::chrono::duration<float>(duration));
514
515 struct camera_transition_state
516 {
517 math::vec3 current_position;
518 float current_ortho_size;
519 };
520
521 auto state = std::make_shared<camera_transition_state>();
522 state->current_position = start_position;
523 state->current_ortho_size = start_ortho_size;
524
525 auto position_action = seq::change_to(state->current_position, target_position, seq_duration, state, ease);
526 auto ortho_action = seq::change_to(state->current_ortho_size, radius, seq_duration, state, ease);
527
528 auto combined_action = seq::together(position_action, ortho_action);
529 combined_action.on_step.connect([camera, state, keep_rotation, target_center]()
530 {
531 if(camera.valid())
532 {
533 auto& tc = camera.get<transform_component>();
534 auto& cc = camera.get<camera_component>();
535 tc.set_position_global(state->current_position);
536 if(!keep_rotation)
537 {
538 tc.look_at(target_center);
539 }
540 cc.set_ortho_size(state->current_ortho_size);
541 cc.update(tc.get_transform_global());
542 }
543 });
544
545 seq::scope::stop_all("camera_focus");
546 auto action_id = seq::start(combined_action, "camera_focus");
547}
548
549
550// Private recursive helper; never emits the 1-unit fallback
551void calc_bounds_global_impl(math::bbox& bounds, entt::handle entity, int depth)
552{
553 auto& tc = entity.get<transform_component>();
554 const auto world_xform = tc.get_transform_global();
555
556 // include this entity's own model AABB if it has one
557 if(auto* mc = entity.try_get<model_component>())
558 {
559 mc->update_world_bounds(world_xform);
560 auto b = mc->get_world_bounds();
561 for(const auto& corner : b.get_corners())
562 {
563 bounds.add_point(corner);
564 }
565 }
566
567 if(auto* tc = entity.try_get<text_component>())
568 {
569 auto b = tc->get_render_bounds();
570 for(const auto& corner : b.get_corners())
571 {
572 bounds.add_point(world_xform.transform_coord(corner));
573 }
574 }
575
576 if(auto* pc = entity.try_get<particle_emitter_component>())
577 {
578 auto b = pc->get_updated_world_bounds(world_xform);
579 for(const auto& corner : b.get_corners())
580 {
581 bounds.add_point(corner);
582 }
583 }
584
585 // recurse into children
586 if(depth != 0) // depth<0 means infinite
587 {
588 for(auto child : tc.get_children())
589 {
590 calc_bounds_global_impl(bounds, child, depth > 0 ? depth - 1 : -1);
591 }
592 }
593}
594} // namespace
595
597{
598 APPLOG_TRACE("{}::{}", hpp::type_name_str<defaults>(), __func__);
599
600 return init_assets(ctx);
601}
602
604{
605 APPLOG_TRACE("{}::{}", hpp::type_name_str<defaults>(), __func__);
606
607 {
608 // 100
609 font::default_thin() = {};
610 // 200
612 // 300
613 font::default_light() = {};
614 // 400
616 // 500
618 // 600
620 // 700
621 font::default_bold() = {};
622 // 800
623 font::default_heavy() = {};
624 // 900
625 font::default_black() = {};
626 }
629 default_textures::get().clear();
630 return true;
631}
632
634{
635 auto& manager = ctx.get_cached<asset_manager>();
636 {
637 const auto id = "engine:/embedded/cube";
638 auto instance = std::make_shared<mesh>();
639 instance->create_cube(gfx::mesh_vertex::get_layout(), 1.0f, 1.0f, 1.0f, 1, 1, 1, mesh_create_origin::center);
640 manager.get_asset_from_instance(id, instance);
641 }
642 {
643 const auto id = "engine:/embedded/cube_rounded";
644 auto instance = std::make_shared<mesh>();
645 instance->create_rounded_cube(gfx::mesh_vertex::get_layout(), 1.0f, 1.0f, 1.0f, 1, 1, 1, mesh_create_origin::center);
646 manager.get_asset_from_instance(id, instance);
647 }
648 {
649 const auto id = "engine:/embedded/sphere";
650 auto instance = std::make_shared<mesh>();
651 instance->create_sphere(gfx::mesh_vertex::get_layout(), 0.5f, 20, 20, mesh_create_origin::center);
652 manager.get_asset_from_instance(id, instance);
653 }
654 {
655 const auto id = "engine:/embedded/plane";
656 auto instance = std::make_shared<mesh>();
657 instance->create_plane(gfx::mesh_vertex::get_layout(), 10.0f, 10.0f, 1, 1, mesh_create_origin::center);
658 manager.get_asset_from_instance(id, instance);
659 }
660 {
661 const auto id = "engine:/embedded/cylinder";
662 auto instance = std::make_shared<mesh>();
663 instance->create_cylinder(gfx::mesh_vertex::get_layout(), 0.5f, 2.0f, 20, 20, mesh_create_origin::center);
664 manager.get_asset_from_instance(id, instance);
665 }
666 {
667 const auto id = "engine:/embedded/capsule_2m";
668 auto instance = std::make_shared<mesh>();
669 instance->create_capsule(gfx::mesh_vertex::get_layout(), 0.5f, 2.0f, 20, 20, mesh_create_origin::center);
670 manager.get_asset_from_instance(id, instance);
671 }
672 {
673 const auto id = "engine:/embedded/capsule_1m";
674 auto instance = std::make_shared<mesh>();
675 instance->create_capsule(gfx::mesh_vertex::get_layout(), 0.5f, 1.0f, 20, 20, mesh_create_origin::center);
676 manager.get_asset_from_instance(id, instance);
677 }
678 {
679 const auto id = "engine:/embedded/cone";
680 auto instance = std::make_shared<mesh>();
681 instance->create_cone(gfx::mesh_vertex::get_layout(), 0.5f, 0.0f, 2, 20, 20, mesh_create_origin::bottom);
682 manager.get_asset_from_instance(id, instance);
683 }
684 {
685 const auto id = "engine:/embedded/torus";
686 auto instance = std::make_shared<mesh>();
687 instance->create_torus(gfx::mesh_vertex::get_layout(), 1.0f, 0.5f, 20, 20, mesh_create_origin::center);
688 manager.get_asset_from_instance(id, instance);
689 }
690 {
691 const auto id = "engine:/embedded/terrain_test";
692 constexpr uint32_t sx = 64;
693 constexpr uint32_t sz = 64;
694 constexpr float k_pi = 3.14159265f;
695 std::vector<float> heights(static_cast<size_t>(sx + 1) * static_cast<size_t>(sz + 1));
696 for(uint32_t z = 0; z <= sz; ++z)
697 {
698 const float tz = static_cast<float>(z) / static_cast<float>(sz);
699 for(uint32_t x = 0; x <= sx; ++x)
700 {
701 const float tx = static_cast<float>(x) / static_cast<float>(sx);
702 const float nx = tx * 2.0f - 1.0f;
703 const float nz = tz * 2.0f - 1.0f;
704 heights[static_cast<size_t>(z) * static_cast<size_t>(sx + 1) + static_cast<size_t>(x)] =
705 std::sin(nx * k_pi * 2.0f) * std::cos(nz * k_pi * 2.0f) * 0.5f + 0.5f;
706 }
707 }
708 auto instance = std::make_shared<mesh>();
709 instance->create_heightfield(gfx::mesh_vertex::get_layout(),
710 heights,
711 sx,
712 sz,
713 25.0f,
714 25.0f,
715 4.0f,
717 true);
718 manager.get_asset_from_instance(id, instance);
719 }
720 {
721 const auto id = "engine:/embedded/teapot";
722 auto instance = std::make_shared<mesh>();
723 instance->create_teapot(gfx::mesh_vertex::get_layout());
724 manager.get_asset_from_instance(id, instance);
725 }
726 {
727 const auto id = "engine:/embedded/icosahedron";
728 auto instance = std::make_shared<mesh>();
729 instance->create_icosahedron(gfx::mesh_vertex::get_layout());
730 manager.get_asset_from_instance(id, instance);
731 }
732 {
733 const auto id = "engine:/embedded/dodecahedron";
734 auto instance = std::make_shared<mesh>();
735 instance->create_dodecahedron(gfx::mesh_vertex::get_layout());
736 manager.get_asset_from_instance(id, instance);
737 }
738
739 for(int i = 0; i < 20; ++i)
740 {
741 const auto id = std::string("engine:/embedded/icosphere") + std::to_string(i);
742 auto instance = std::make_shared<mesh>();
743 instance->create_icosphere(gfx::mesh_vertex::get_layout(), i);
744 manager.get_asset_from_instance(id, instance);
745 }
746
747 {
748 // 100
749 font::default_thin() = manager.get_asset<font>("engine:/data/fonts/Inter/static/Inter-Thin.ttf");
750 // 200
751 font::default_extra_light() = manager.get_asset<font>("engine:/data/fonts/Inter/static/Inter-ExtraLight.ttf");
752 // 300
753 font::default_light() = manager.get_asset<font>("engine:/data/fonts/Inter/static/Inter-Light.ttf");
754 // 400
755 font::default_regular() = manager.get_asset<font>("engine:/data/fonts/Inter/static/Inter-Regular.ttf");
756 // 500
757 font::default_medium() = manager.get_asset<font>("engine:/data/fonts/Inter/static/Inter-Medium.ttf");
758 // 600
759 font::default_semi_bold() = manager.get_asset<font>("engine:/data/fonts/Inter/static/Inter-SemiBold.ttf");
760 // 700
761 font::default_bold() = manager.get_asset<font>("engine:/data/fonts/Inter/static/Inter-Bold.ttf");
762 // 800
763 font::default_heavy() = manager.get_asset<font>("engine:/data/fonts/Inter/static/Inter-ExtraBold.ttf");
764 // 900
765 font::default_black() = manager.get_asset<font>("engine:/data/fonts/Inter/static/Inter-Black.ttf");
766
767 }
768
769 {
770 material::default_color_map() = manager.get_asset<gfx::texture>("engine:/data/textures/default_color.dds");
771 material::default_normal_map() = manager.get_asset<gfx::texture>("engine:/data/textures/default_normal.dds");
772 }
773 {
774 const auto id = "engine:/embedded/standard";
775 auto instance = std::make_shared<pbr_material>();
776 auto asset = manager.get_asset_from_instance<material>(id, instance);
777
778 model::default_material() = asset;
779 }
780
781 {
782 const auto id = "engine:/embedded/fallback";
783 auto instance = std::make_shared<pbr_material>();
784 instance->set_emissive_color(math::color::purple());
785 instance->set_base_color(math::color::purple());
786 instance->set_roughness(1.0f);
787 auto asset = manager.get_asset_from_instance<material>(id, instance);
788
789 model::fallback_material() = asset;
790 }
791
792 default_textures::get().generate();
793
794 return true;
795}
796
797void defaults::focus_camera_on_bounds(entt::handle camera, const math::bsphere& bounds, float duration)
798{
799 run_camera_focus_transition(camera, bounds.position, bounds.radius, /*keep_rotation=*/true, duration);
800}
801
802void defaults::focus_camera_on_bounds(entt::handle camera, const math::bbox& bounds, float duration)
803{
804 const math::vec3 center = bounds.get_center();
805 const float radius = math::length(bounds.get_dimensions()) / 2.0f;
806 run_camera_focus_transition(camera, center, radius, /*keep_rotation=*/true, duration);
807}
808auto defaults::create_embedded_mesh_entity(rtti::context& ctx, scene& scn, const std::string& name) -> entt::handle
809{
810 auto& am = ctx.get_cached<asset_manager>();
811 const auto id = "engine:/embedded/" + string_utils::replace(string_utils::to_lower(name), " ", "_");
812 auto asset = am.get_asset<mesh>(id);
813 auto mesh_asset = asset.get();
814 auto bounds = mesh_asset->get_bounds();
815 math::vec3 position = {0.0f, bounds.get_extents().y, 0.0f};
816
817 return create_mesh_entity_at(ctx, scn, id, position);
818}
819
820auto defaults::create_prefab_at(rtti::context& ctx, scene& scn, const std::string& key) -> entt::handle
821{
822 auto& am = ctx.get_cached<asset_manager>();
823 auto asset = am.get_asset<prefab>(key);
824
825 auto object = scn.instantiate(asset);
826 return object;
827}
828
829auto defaults::create_prefab_at(rtti::context& ctx, scene& scn, const std::string& key, math::vec3 pos) -> entt::handle
830{
831 auto& am = ctx.get_cached<asset_manager>();
832 auto asset = am.get_asset<prefab>(key);
833
834 auto object = scn.instantiate(asset);
835
836 auto& trans_comp = object.get<transform_component>();
837 trans_comp.set_position_global(pos);
838
839 return object;
840}
841
843 scene& scn,
844 const std::string& key,
845 const camera& cam,
846 math::vec2 pos,
847 bool align_to_surface) -> entt::handle
848{
849 const auto placement = compute_drop_placement(scn, cam, pos);
850 auto object = create_prefab_at(ctx, scn, key, placement.contact);
851 finalize_surface_placement(object, placement, align_to_surface);
852 return object;
853}
854
855auto defaults::create_mesh_entity_at(rtti::context& ctx, scene& scn, const std::string& key, math::vec3 pos)
856 -> entt::handle
857{
858 auto& am = ctx.get_cached<asset_manager>();
859 auto asset = am.get_asset<mesh>(key);
860
861 model mdl;
862
863 mdl.set_lod(asset, 0);
864
865 auto mesh_ptr = asset.get();
866 if(mesh_ptr)
867 {
868 const auto& material_uids = mesh_ptr->get_default_material_uids();
869 for(size_t i = 0; i < material_uids.size(); ++i)
870 {
871 auto mat_asset = am.get_asset<material>(material_uids[i]);
872 if(mat_asset)
873 {
874 mdl.set_material(mat_asset, static_cast<uint32_t>(i));
875 }
876 }
877 }
878
879 std::string name = fs::path(key).stem().string();
880 auto object = scn.create_entity(name);
881
882 auto& model_comp = object.emplace<model_component>();
883 model_comp.set_casts_shadow(true);
884 model_comp.set_model(mdl);
885
886 auto& trans_comp = object.get<transform_component>();
887 trans_comp.set_position_global(pos);
888
889 if(model_comp.is_skinned())
890 {
891 object.emplace<animation_component>();
892 }
893
894 return object;
895}
896
898 scene& scn,
899 const std::string& key,
900 const camera& cam,
901 math::vec2 pos,
902 bool align_to_surface) -> entt::handle
903{
904 const auto placement = compute_drop_placement(scn, cam, pos);
905 auto object = create_mesh_entity_at(ctx, scn, key, placement.contact);
906 finalize_surface_placement(object, placement, align_to_surface);
907 return object;
908}
909
910auto defaults::create_terrain(rtti::context& ctx, scene& scn, math::vec3 pos) -> entt::handle
911{
912 static constexpr const char* terrain_mesh_id = "engine:/embedded/terrain_test";
913 auto object = create_mesh_entity_at(ctx, scn, terrain_mesh_id, pos);
914 if(object)
915 {
916 object.get<tag_component>().name = "Terrain";
917 }
918 return object;
919}
920
922 -> entt::handle
923{
924 auto& am = ctx.get_cached<asset_manager>();
925
926 auto object = scn.create_entity(name + " Light");
927
928 auto& transf_comp = object.get_or_emplace<transform_component>();
929 transf_comp.set_position_local({0.0f, 1.0f, 0.0f});
930
932 {
933 transf_comp.rotate_by_euler_local({50.0f, -30.0f + 180.0f, 0.0f});
934 }
935
936
937 light light_data;
938 light_data.color = math::color(255, 244, 214, 255);
939 light_data.type = type;
940
941 auto& light_comp = object.get_or_emplace<light_component>();
942 light_comp.set_light(light_data);
943
944 return object;
945}
946
948 -> entt::handle
949{
950 auto& am = ctx.get_cached<asset_manager>();
951
952 auto object = scn.create_entity("Reflection Probe" + name);
953
954 auto& transf_comp = object.get_or_emplace<transform_component>();
955 transf_comp.set_position_local({0.0f, 0.1f, 0.0f});
956
957 reflection_probe probe;
959 probe.type = type;
960
961 auto& reflection_comp = object.get_or_emplace<reflection_probe_component>();
962 reflection_comp.set_probe(probe);
963
964
965 return object;
966}
967
968auto defaults::create_volume_entity(rtti::context& ctx, scene& scn, const std::string& name, volume_mode mode) -> entt::handle
969{
970 auto object = scn.create_entity(name);
971 auto& volume_comp = object.emplace<volume_component>();
972 volume_comp.mode = mode;
973
974 object.emplace<assao_component>();
975 object.emplace<auto_exposure_component>();
976 object.emplace<bloom_component>();
977 object.emplace<tonemapping_component>();
978 object.emplace<fxaa_component>();
979 object.emplace<taa_component>();
980 object.emplace<ssr_component>();
981 object.emplace<ssil_component>().enabled = false;
982 return object;
983}
984
985auto defaults::create_default_volume_entity_for_preview(rtti::context& ctx, scene& scn, const std::string& name, volume_mode mode) -> entt::handle
986{
987 auto object = scn.create_entity(name);
988 auto& volume_comp = object.emplace<volume_component>();
989 volume_comp.mode = mode;
990
991 object.emplace<tonemapping_component>();
992 object.emplace<fxaa_component>();
993 object.emplace<bloom_component>();
994
995 return object;
996}
997
998
999auto defaults::create_camera_entity(rtti::context& ctx, scene& scn, const std::string& name) -> entt::handle
1000{
1001 auto object = scn.create_entity(name);
1002
1003 auto& transf_comp = object.get_or_emplace<transform_component>();
1004 transf_comp.set_position_local({0.0f, 1.0f, -10.0f});
1005
1006 object.emplace<camera_component>();
1007
1008
1009 return object;
1010}
1011
1012auto defaults::create_ui_document_entity(rtti::context& ctx, scene& scn, const std::string& name) -> entt::handle
1013{
1014 auto object = scn.create_entity(name);
1015 object.emplace<ui_document_component>();
1016 return object;
1017}
1018
1019auto defaults::create_text_entity(rtti::context& ctx, scene& scn, const std::string& name) -> entt::handle
1020{
1021 auto object = scn.create_entity(name);
1022
1023 auto& text = object.emplace<text_component>();
1024 text.set_text("Hello World!");
1025
1026 return object;
1027}
1028
1029auto defaults::create_particle_emitter_entity(rtti::context& ctx, scene& scn, const std::string& name) -> entt::handle
1030{
1031 auto object = scn.create_entity(name);
1032 object.emplace<particle_emitter_component>();
1033 return object;
1034}
1035
1036auto defaults::create_audio_source_entity(rtti::context& ctx, scene& scn, const std::string& name) -> entt::handle
1037{
1038 auto object = scn.create_entity(name);
1039 object.emplace<audio_source_component>();
1040 return object;
1041}
1042
1043auto defaults::parse_scene_preset(hpp::string_view value, scene_preset& out) -> bool
1044{
1045 if(value.empty() || value == "medium" || value == "standard" || value == "default")
1046 {
1047 out = scene_preset::medium;
1048 return true;
1049 }
1050 if(value == "low")
1051 {
1052 out = scene_preset::low;
1053 return true;
1054 }
1055 if(value == "high")
1056 {
1057 out = scene_preset::high;
1058 return true;
1059 }
1060 if(value == "showcase")
1061 {
1062 out = scene_preset::showcase;
1063 return true;
1064 }
1065 return false;
1066}
1067
1069{
1070 switch(preset)
1071 {
1072 case scene_preset::low:
1073 return "low";
1074 case scene_preset::medium:
1075 return "medium";
1076 case scene_preset::high:
1077 return "high";
1078 case scene_preset::showcase:
1079 return "showcase";
1080 }
1081 return "medium";
1082}
1083
1088
1090{
1091 auto camera = create_camera_entity(ctx, scn, "Main Camera");
1093
1094 {
1095 auto object = create_light_entity(ctx, scn, light_type::directional, "Sky & Directional");
1096 auto& skylight = object.emplace<skylight_component>();
1097 auto& light_comp = object.get<light_component>();
1098 auto light = light_comp.get_light();
1099
1101 {
1102 skylight.set_cloud_mode(skylight_component::cloud_mode::none);
1104 }
1105 else if(preset == scene_preset::medium)
1106 {
1107 skylight.set_cloud_mode(skylight_component::cloud_mode::flat);
1109 }
1110 else if(preset == scene_preset::high)
1111 {
1112 skylight.set_cloud_mode(skylight_component::cloud_mode::flat);
1115
1116 }
1117 else if(preset == scene_preset::showcase)
1118 {
1119 skylight.set_cloud_mode(skylight_component::cloud_mode::volumetric);
1123 }
1124
1125 light_comp.set_light(light);
1126 }
1127
1128 {
1129 auto object = create_reflection_probe_entity(ctx, scn, probe_type::sphere, " Global");
1130 auto& reflection_comp = object.get_or_emplace<reflection_probe_component>();
1131 auto probe = reflection_comp.get_probe();
1132 probe.method = reflect_method::environment;
1133 probe.sphere_data.range = 1000.0f;
1134 reflection_comp.set_probe(probe);
1136 {
1137 // Low-end preset: skip reflection bakes entirely to save GPU. Scripts can still call
1138 // mark_dirty() later to force a one-off bake if absolutely needed.
1139 reflection_comp.set_update_mode(probe_update_mode::on_demand);
1140 }
1141 }
1142
1143 {
1144 auto volume = create_volume_entity(ctx, scn, "Volume Global", volume_mode::global);
1145
1147 {
1148 if(auto* comp = volume.try_get<assao_component>())
1149 comp->enabled = false;
1150 if(auto* comp = volume.try_get<bloom_component>())
1151 comp->enabled = false;
1152 if(auto* comp = volume.try_get<ssr_component>())
1153 comp->enabled = false;
1154 if(auto* comp = volume.try_get<ssil_component>())
1155 comp->enabled = false;
1156 if(auto* comp = volume.try_get<auto_exposure_component>())
1157 comp->enabled = false;
1158 if(auto* comp = volume.try_get<bloom_component>())
1159 comp->enabled = false;
1160 }
1161 else if(preset == scene_preset::medium)
1162 {
1163 if(auto* comp = volume.try_get<ssr_component>())
1164 {
1165 comp->settings.fidelityfx.max_rays = 4;
1166 comp->settings.fidelityfx.resolution = trace_resolution::half;
1167 }
1168 if(auto* comp = volume.try_get<ssil_component>())
1169 comp->enabled = false;
1170
1171 }
1172 else if(preset == scene_preset::high)
1173 {
1174 if(auto* comp = volume.try_get<auto_exposure_component>())
1175 comp->enabled = true;
1176 if(auto* comp = volume.try_get<bloom_component>())
1177 comp->enabled = true;
1178 if(auto* comp = volume.try_get<ssil_component>())
1179 {
1180 comp->enabled = true;
1181 comp->settings.resolution = trace_resolution::half;
1182 }
1183 if(auto* comp = volume.try_get<ssil_component>())
1184 {
1185 comp->enabled = true;
1186 comp->settings.resolution = trace_resolution::half;
1187 }
1188 if(auto* comp = volume.try_get<taa_component>())
1189 comp->enabled = true;
1190 }
1191 else if(preset == scene_preset::showcase)
1192 {
1193 if(auto* comp = volume.try_get<auto_exposure_component>())
1194 comp->enabled = true;
1195 if(auto* comp = volume.try_get<bloom_component>())
1196 comp->enabled = true;
1197 if(auto* comp = volume.try_get<ssil_component>())
1198 comp->enabled = true;
1199 if(auto* comp = volume.try_get<taa_component>())
1200 comp->enabled = true;
1201 }
1202 }
1203}
1204
1206{
1207 {
1208 auto object = create_volume_entity(ctx, scn, "Volume Global", volume_mode::global);
1209 }
1210 {
1211 auto object = create_light_entity(ctx, scn, light_type::directional, "Sky & Directional");
1212 auto& skylight = object.get_or_emplace<skylight_component>();
1213 }
1214
1215 {
1216 auto object = create_reflection_probe_entity(ctx, scn, probe_type::sphere, " Global");
1217 auto& reflection_comp = object.get_or_emplace<reflection_probe_component>();
1218 auto probe = reflection_comp.get_probe();
1219 probe.method = reflect_method::environment;
1220 probe.sphere_data.range = 1000.0f;
1221 reflection_comp.set_probe(probe);
1222 }
1223
1224}
1225
1226auto defaults::create_default_3d_scene_for_preview(rtti::context& ctx, scene& scn, const usize32_t& size)
1227 -> entt::handle
1228{
1229 auto camera = create_camera_entity(ctx, scn, "Main Camera");
1230 auto post_process_volume = create_default_volume_entity_for_preview(ctx, scn, "Volume Global", volume_mode::global);
1231
1232 {
1233 auto& transf_comp = camera.get<transform_component>();
1234 transf_comp.set_position_local({0.0f, 6.6f, 10.0f});
1235 transf_comp.set_rotation_euler_local({20.0f, 180.0f, 0.0f});
1236 auto& camera_comp = camera.get<camera_component>();
1237 camera_comp.set_viewport_size(size);
1238 }
1239
1240 {
1241 auto object = create_light_entity(ctx, scn, light_type::directional, "Sky & Directional");
1242
1243
1244 auto& transf_comp = object.get<transform_component>();
1245 transf_comp.set_rotation_euler_local({110.0f, -10.0f, -35.0f});
1246
1247 auto& light_comp = object.get_or_emplace<light_component>();
1248 auto light = light_comp.get_light();
1249 light.casts_shadows = false;
1250 light_comp.set_light(light);
1251
1252 auto& skylight = object.get_or_emplace<skylight_component>();
1253 }
1254
1255 {
1256 auto object = create_reflection_probe_entity(ctx, scn, probe_type::sphere, " Global");
1257 auto& reflection_comp = object.get_or_emplace<reflection_probe_component>();
1258 auto probe = reflection_comp.get_probe();
1259 probe.method = reflect_method::environment;
1260 probe.sphere_data.range = 1000.0f;
1261 reflection_comp.set_probe(probe);
1262 }
1263
1264 return camera;
1265}
1266
1267template<>
1269{
1270 auto bounds = calc_bounds_sphere_global(result.object, false);
1271 focus_camera_on_bounds(result.camera, bounds, 0.0f);
1272}
1273
1274template<>
1276{
1277 auto bounds = calc_bounds_sphere_global(result.object);
1278 if(bounds.radius < 1.0f)
1279 {
1280 float scale = 1.0f / bounds.radius;
1281 result.object.get<transform_component>().scale_by_local(math::vec3(scale));
1282 }
1283
1285}
1286
1287template<>
1289{
1290 auto bounds = calc_bounds_sphere_global(result.object);
1291 if(bounds.radius < 1.0f)
1292 {
1293 float scale = 1.0f / bounds.radius;
1294 result.object.get<transform_component>().scale_by_local(math::vec3(scale));
1295 }
1296
1298}
1299
1300template<>
1302 scene& scn,
1303 const asset_handle<material>& asset,
1304 const usize32_t& size, bool focus_camera)
1306{
1307 asset.get(true);
1308
1309 auto camera = create_default_3d_scene_for_preview(ctx, scn, size);
1310
1311
1312 auto object = create_embedded_mesh_entity(ctx, scn, "Sphere");
1313 auto& model_comp = object.get<model_component>();
1314 auto model = model_comp.get_model();
1315 model.set_material(asset, 0);
1316 model_comp.set_model(model);
1317 model_comp.set_casts_shadow(false);
1318
1319 if(focus_camera)
1320 {
1321 focus_camera_on_bounds(camera, calc_bounds_sphere_global(object, false), 0.0f);
1322 }
1323
1324
1326}
1327
1328template<>
1330 scene& scn,
1331 const asset_handle<prefab>& asset,
1332 const usize32_t& size, bool focus_camera)
1334{
1335 asset.get(true);
1336
1337 auto camera = create_default_3d_scene_for_preview(ctx, scn, size);
1338
1339
1340 auto object = scn.instantiate(asset, false);
1341
1342 if(object)
1343 {
1344 if(auto model_comp = object.try_get<model_component>())
1345 {
1346 model_comp->set_casts_shadow(false);
1347 }
1348
1349 auto bounds = calc_bounds_sphere_global(object);
1350 if(bounds.radius < 1.0f)
1351 {
1352 float scale = 1.0f / bounds.radius;
1353 object.get<transform_component>().scale_by_local(math::vec3(scale));
1354 }
1355
1356 if(focus_camera)
1357 {
1358 focus_camera_on_bounds(camera, calc_bounds_sphere_global(object), 0.0f);
1359 }
1360 }
1361
1363}
1364
1365template<>
1367 scene& scn,
1368 const asset_handle<mesh>& asset,
1369 const usize32_t& size, bool focus_camera)
1371{
1372 asset.get(true);
1373
1374 auto camera = create_default_3d_scene_for_preview(ctx, scn, size);
1375
1376 auto object = create_mesh_entity_at(ctx, scn, asset.id());
1377
1378 if(auto model_comp = object.try_get<model_component>())
1379 {
1380 model_comp->set_casts_shadow(false);
1381 }
1382
1383 auto bounds = calc_bounds_sphere_global(object);
1384 if(bounds.radius < 1.0f)
1385 {
1386 float scale = 1.0f / bounds.radius;
1387 object.get<transform_component>().scale_by_local(math::vec3(scale));
1388 }
1389
1390 if(focus_camera)
1391 {
1392 focus_camera_on_bounds(camera, calc_bounds_sphere_global(object), 0.0f);
1393 }
1394
1396}
1397
1398
1400 hpp::span<const entt::handle> entities,
1401 float duration)
1402{
1404 {
1405 math::bbox bounds;
1406
1407 bool valid = false;
1408 for(const auto& entity : entities)
1409 {
1410 if(!entity.valid())
1411 {
1412 return;
1413 }
1414 auto ebounds = calc_bounds_global(entity);
1415 bounds.add_point(ebounds.min);
1416 bounds.add_point(ebounds.max);
1417
1418 valid = true;
1419 }
1420 if(valid)
1421 {
1422 focus_camera_on_bounds(camera, bounds, duration);
1423 }
1424 }
1425}
1426
1427auto defaults::calc_bounds_global(entt::handle entity, int depth) -> math::bbox
1428{
1429 // 1) Get the “true” union of all models (possibly empty)
1430 math::bbox bounds;
1431 calc_bounds_global_impl(bounds, entity, depth);
1432
1433 // 2) If nothing was found, fall back *once* here to a unit cube
1434 if(!bounds.is_populated())
1435 {
1436 const math::vec3 one{1, 1, 1};
1437 const auto pos = entity.get<transform_component>().get_position_global();
1438 bounds = math::bbox{pos - one, pos + one};
1439 }
1440
1441 return bounds;
1442}
1443
1444auto defaults::calc_bounds_sphere_global(entt::handle entity, bool use_bbox_diagonal) -> math::bsphere
1445{
1446 auto box = calc_bounds_global(entity);
1447 math::bsphere result;
1448 result.position = box.get_center();
1449
1450 // 2) radius is half the diagonal length
1451 math::vec3 diag = box.get_dimensions();
1452 float max_abs = math::max(math::abs(diag.x), math::max(math::abs(diag.y), math::abs(diag.z)));
1453 float radius = 0.5f * (use_bbox_diagonal ? math::length(diag) : max_abs);
1454 result.radius = radius;
1455
1456 return result;
1457}
1458} // namespace unravel
entt::handle b
const btCollisionObject * object
Provides storage for common representation of spherical bounding volume, and wraps up common function...
Definition bsphere.h:18
float radius
Definition bsphere.h:120
vec3 position
Definition bsphere.h:119
General purpose transformation class designed to maintain each component of the transformation separa...
Definition transform.hpp:27
auto transform_normal(const vec2_t &v) const noexcept -> vec2_t
Transform a 2D normal.
auto transform_coord(const vec2_t &v) const noexcept -> vec2_t
Transform a 2D coordinate.
Manages assets, including loading, unloading, and storage.
auto get_asset(const std::string &key, load_flags flags=load_flags::standard, load_mode mode=load_mode::immediate) -> asset_handle< T >
Gets an asset by its key.
Class that contains core data for audio listeners. There can only be one instance of it per scene.
Class that contains core data for audio sources.
Class that contains core camera data, used for rendering and other purposes.
Class representing a camera. Contains functionality for manipulating and updating a camera....
Definition camera.h:62
static auto get() -> default_textures &
Class that contains core light data, used for rendering and other purposes.
void set_light(const light &l)
Sets the light object.
Base class for materials used in rendering.
Definition material.h:44
static auto default_normal_map() -> asset_handle< gfx::texture > &
Gets the default normal map.
Definition material.cpp:32
static auto default_color_map() -> asset_handle< gfx::texture > &
Gets the default color map.
Definition material.cpp:26
Main class representing a 3D mesh with support for different LODs, submeshes, and skinning.
Definition mesh.h:323
auto get_bounds() const -> const math::bbox &
Gets the local bounding box for this mesh.
Definition mesh.cpp:2730
Class that contains core data for meshes.
void set_casts_shadow(bool cast_shadow)
Sets whether the model casts shadows.
Structure describing a LOD group (set of meshes), LOD transitions, and their materials.
Definition model.h:275
static auto fallback_material() -> asset_handle< material > &
Gets the fallback material.
Definition model.cpp:1248
void set_material(asset_handle< material > material, uint32_t index)
Sets the material for the specified index.
Definition model.cpp:220
static auto default_material() -> asset_handle< material > &
Gets the default material.
Definition model.cpp:1242
void set_lod(asset_handle< mesh > mesh, uint32_t lod)
Sets the LOD (Level of Detail) mesh for the specified level.
Definition model.cpp:200
Component that wraps the soa particle system emitter.
Class that contains core reflection probe data, used for rendering and other purposes.
auto get_probe() const -> const reflection_probe &
Gets the reflection probe object.
void set_probe(const reflection_probe &probe)
Sets the reflection probe object.
Class that contains sky light data.
@ flat
Flat projected clouds (cheap, single-sample scattering)
@ volumetric
Volumetric raymarched clouds (full scattering, half-res with temporal)
Temporal anti-aliasing (HDR, before tonemap). Mutually exclusive with FXAA when enabled.
void set_text(const std::string &text)
Sets the text content to be rendered.
Component that handles transformations (position, rotation, scale, etc.) in the ACE framework.
void set_position_local(const math::vec3 &position) noexcept
Sets the local position.
void set_position_global(const math::vec3 &position) noexcept
Sets the global position.
Spatial volume that applies post-processing effects when the camera is inside. Effect settings come f...
volume_mode mode
Volume mode: local uses bounds, global affects camera everywhere.
float x
float z
defaults::scene_preset preset
const char * id
math::vec3 contact
Definition defaults.cpp:252
math::vec3 position
Definition defaults.cpp:52
bool hit
Definition defaults.cpp:51
math::vec3 normal
Definition defaults.cpp:53
bool on_surface
Definition defaults.cpp:254
std::string name
Definition hub.cpp:33
#define APPLOG_TRACE(...)
Definition logging.h:17
texture_job_type type
const aiScene * scene
void vertex_unpack(float _output[4], attribute _attr, const vertex_layout &_decl, const void *_data, uint32_t _index)
Definition graphics.cpp:364
auto inverse(transform_t< T, Q > const &t) noexcept -> transform_t< T, Q >
float smooth_stop(float progress)
Modelled after quarter-cycle of sine wave (different phase)
Definition seq_ease.cpp:304
void stop_all(const std::string &scope)
Stops all actions within the specified scope.
Definition seq.cpp:160
auto start(seq_action action, const seq_scope_policy &scope_policy, hpp::source_location location) -> seq_id_t
Starts a new action.
Definition seq.cpp:8
auto together(const std::vector< seq_action > &actions, const sentinel_t &sentinel) -> seq_action
Creates a simultaneous action that executes a list of actions together.
Definition seq_core.cpp:109
auto change_to(T &object, const std::decay_t< T > &end, const duration_t &duration, const sentinel_t &sentinel, const ease_t &ease_func=ease::linear) -> seq_action
Creates an action to change an object to a specified value over a specified duration.
Definition seq_core.hpp:76
auto replace(const std::string &str, const std::string &search, const std::string &replace) -> std::string
Definition utils.cpp:28
auto to_lower(const std::string &str) -> std::string
Definition utils.cpp:42
probe_type
Enum class representing the type of reflection probe.
@ sphere
Sphere type reflection probe.
@ box
Box type reflection probe.
light_type
Enum representing the type of light.
Definition light.h:14
@ environment
Environment reflection method.
@ static_only
Static-only reflection method.
std::vector< float > scale
entt::handle entity
Thread-safe handle to an asset.
static auto get_layout() -> const vertex_layout &
Definition vertex_decl.h:15
Storage for box vector values and wraps up common functionality.
Definition bbox.h:21
bbox & add_point(const vec3 &point)
Grows the bounding box based on the point passed.
Definition bbox.cpp:924
bool intersect(const bbox &bounds) const
Tests to see if this AABB is intersected by another AABB.
Definition bbox.cpp:244
bbox & mul(const transform &t)
Transforms an axis aligned bounding box by the specified matrix.
Definition bbox.cpp:876
bool is_populated() const
Checks if the bounding box is populated.
Definition bbox.cpp:36
vec3 get_dimensions() const
Returns a vector containing the dimensions of the bounding box.
Definition bbox.cpp:941
vec3 get_extents() const
Returns a vector containing the extents of the bounding box (the half-dimensions)
Definition bbox.cpp:951
vec3 get_center() const
Returns a vector containing the exact center point of the box.
Definition bbox.cpp:946
static color purple()
Definition color.h:33
static auto from_point_normal(const vec3 &point, const vec3 &normal) -> plane
Creates a plane from a point and a normal.
Definition plane.cpp:20
Creates a default 3D scene for asset preview.
Definition defaults.h:289
static auto create_reflection_probe_entity(rtti::context &ctx, scene &scn, probe_type type, const std::string &name) -> entt::handle
Creates a reflection probe entity.
Definition defaults.cpp:947
static void focus_camera_on_3d_scene_for_asset_preview(rtti::context &ctx, const asset_preview_result &result)
static auto parse_scene_preset(hpp::string_view value, scene_preset &out) -> bool
Parse a preset name (low|medium|high|showcase; aliases: standard/default -> medium)....
static void create_scene_from_preset(rtti::context &ctx, scene &scn, scene_preset preset)
Creates a 3D scene from a quality preset.
static auto init_assets(rtti::context &ctx) -> bool
Initializes default assets.
Definition defaults.cpp:633
scene_preset
Quality presets for new scene creation (low = less expensive, high = more expensive).
Definition defaults.h:215
static auto init(rtti::context &ctx) -> bool
Initializes default settings and assets.
Definition defaults.cpp:596
static auto create_ui_document_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a UI document entity.
static auto create_text_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a text entity.
static auto create_audio_source_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a audio source entity.
static auto create_terrain(rtti::context &ctx, scene &scn, math::vec3 pos={0.0f, 0.0f, 0.0f}) -> entt::handle
Creates a test heightfield terrain entity (embedded procedural mesh).
Definition defaults.cpp:910
static auto create_volume_entity(rtti::context &ctx, scene &scn, const std::string &name, volume_mode mode=volume_mode::local) -> entt::handle
Creates a post process volume entity.
Definition defaults.cpp:968
static auto scene_preset_to_string(scene_preset preset) -> const char *
Stable string for logging / MCP / UI ("low", "medium", "high", "showcase").
static auto create_embedded_mesh_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates an embedded mesh entity.
Definition defaults.cpp:808
static auto create_mesh_entity_at(rtti::context &ctx, scene &scn, const std::string &key, const camera &cam, math::vec2 pos, bool align_to_surface=false) -> entt::handle
Creates a mesh entity at a specified position.
Definition defaults.cpp:897
static auto calc_bounds_sphere_global(entt::handle entity, bool use_bbox_diagonal=true) -> math::bsphere
Calculates the bounding sphere of an entity.
static void focus_camera_on_entities(entt::handle camera, hpp::span< const entt::handle > entities, float duration=0.0f)
Focuses a camera on a specified entity with a timed transition.
static auto create_particle_emitter_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a particle emitter entity.
static void focus_camera_on_bounds(entt::handle camera, const math::bsphere &bounds, float duration=0.0f)
Focuses a camera on bounds with a timed transition.
Definition defaults.cpp:797
static auto create_default_3d_scene_for_asset_preview(rtti::context &ctx, scene &scn, const asset_handle< T > &asset, const usize32_t &size, bool focus_camera=true) -> asset_preview_result
static auto calc_bounds_global(entt::handle entity, int depth=-1) -> math::bbox
Calculates the bounding box of an entity.
static auto create_light_entity(rtti::context &ctx, scene &scn, light_type type, const std::string &name) -> entt::handle
Creates a light entity.
Definition defaults.cpp:921
static void create_default_3d_scene_for_editing(rtti::context &ctx, scene &scn)
Creates a default 3D scene for editing.
static auto create_camera_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a camera entity.
Definition defaults.cpp:999
static void create_default_3d_scene(rtti::context &ctx, scene &scn)
Creates a default 3D scene.
static auto create_prefab_at(rtti::context &ctx, scene &scn, const std::string &key, const camera &cam, math::vec2 pos, bool align_to_surface=false) -> entt::handle
Creates a prefab entity at a specified position.
Definition defaults.cpp:842
static auto deinit(rtti::context &ctx) -> bool
Deinitializes default settings and assets.
Definition defaults.cpp:603
static auto default_light() -> asset_handle< font > &
Definition font.cpp:867
static auto default_heavy() -> asset_handle< font > &
Definition font.cpp:892
static auto default_extra_light() -> asset_handle< font > &
Definition font.cpp:862
static auto default_regular() -> asset_handle< font > &
Definition font.cpp:877
static auto default_bold() -> asset_handle< font > &
Definition font.cpp:887
static auto default_semi_bold() -> asset_handle< font > &
Definition font.cpp:882
static auto default_thin() -> asset_handle< font > &
Definition font.cpp:857
static auto default_black() -> asset_handle< font > &
Definition font.cpp:897
static auto default_medium() -> asset_handle< font > &
Definition font.cpp:872
bool enabled
Whether contact shadows are enabled for this light.
Definition light.h:347
sm_resolution resolution
Resolution of the shadow map.
Definition light.h:224
sm_impl type
Implementation type for shadow mapping.
Definition light.h:222
Struct representing a light.
Definition light.h:87
struct unravel::light::shadowmap_params shadow_params
struct unravel::light::contact_shadow_params contact_shadow
light_type type
The type of the light.
Definition light.h:89
math::color color
The color of the light.
Definition light.h:207
Represents a generic prefab with a buffer for serialized data.
Definition prefab.h:18
Structure representing a reflection probe.
probe_type type
Type of the reflection probe.
reflect_method method
Reflection method.
Represents a scene in the ACE framework, managing entities and their relationships.
Definition scene.h:70
Component that provides a tag (name or label) for an entity.
Component that holds a reference to a UI document for RmlUi rendering.
bool enabled