Unravel Engine C++ Reference
Loading...
Searching...
No Matches
ui_system.cpp
Go to the documentation of this file.
1#include "ui_system.h"
7#include "entt/entity/fwd.hpp"
9#include "glm/ext/scalar_constants.hpp"
10#include "glm/gtc/epsilon.hpp"
11#include "hpp/small_vector.hpp"
12
13#include <engine/events.h>
14#include <engine/play_mode.h>
15#include <engine/input/input.h>
20
21#include <logging/logging.h>
22
23#include <RmlUi/Core/Context.h>
24#include <RmlUi/Core/Core.h>
25#include <RmlUi/Core/Element.h>
26#include <RmlUi/Core/ElementDocument.h>
27#include <RmlUi/Core/FileInterface.h>
28#include <RmlUi/Core/StreamMemory.h>
29#include <RmlUi/Core/StyleSheetContainer.h>
30#include <RmlUi/Debugger/Debugger.h>
31#include <RmlUi/Core.h>
32
33#include <engine/ecs/ecs.h>
34#include <engine/engine.h>
36#include <math/math.h>
37
38#include <string_utils/utils.h>
40
41#include <graphics/texture.h>
42#include <graphics/graphics.h>
43
44#include <algorithm>
45#include <vector>
46
47namespace unravel
48{
49namespace
50{
51 uint64_t next_context_id = 0;
52
53 auto remove_context(Rml::Context* context, Rml::Context*& debug_target_context) -> void
54 {
55 if(debug_target_context == context)
56 {
57 debug_target_context = nullptr;
58 Rml::Debugger::SetContext(nullptr);
59 }
60 if(context)
61 {
62 Rml::RemoveContext(context->GetName());
63 context = nullptr;
64 }
65 }
66}
67
69{
70 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
71
72 // Connect to engine events (UI update/render is done in rendering_system::render_scene)
73 auto& ev = ctx.get_cached<events>();
74 ev.on_os_event.connect(sentinel_, 100, this, &ui_system::on_os_event);
75
76 // Initialize RmlUi backend
77 if(!RmlUi_Backend_Engine::initialize(ctx, "UnravelEngine UI"))
78 {
79 APPLOG_ERROR("Failed to initialize RmlUi backend");
80 return false;
81 }
82
83
84 // Create main UI context
85 // Get viewport size from renderer
86 int width = 1024, height = 768;
87 if(ctx.has<renderer>())
88 {
89 const auto& rend = ctx.get_cached<renderer>();
90 const auto& window = rend.get_main_window();
91 if(window)
92 {
93 auto size = window->get_window().get_size();
94 width = static_cast<int>(size.w);
95 height = static_cast<int>(size.h);
96 }
97 }
98 bool is_deploy_mode = ctx.has<deploy>();
99
100 if(!is_deploy_mode)
101 {
102 // Create debug context (empty, used for RmlUi debugger)
103 debug_context_ = Rml::CreateContext("debug", Rml::Vector2i(width, height));
104 if(!debug_context_)
105 {
106 APPLOG_ERROR("Failed to create RmlUi debug context");
108 return false;
109 }
110 Rml::Debugger::Initialise(debug_context_);
111 debug_render_layer_stack_ = std::make_shared<RmlUi_RenderLayerStack>();
112
113 }
114 // Register component callbacks
115 register_component_callbacks(ctx);
116
117 // Load test UI document
118 load_fonts();
119
120 return true;
121}
122
124{
125 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
126
127 auto& ecs_system = ctx.get_cached<ecs>();
128 auto& scene = ecs_system.get_scene();
129 auto& registry = *scene.registry;
130
131 auto view = registry.view<ui_document_component>();
132 for(auto entity : view)
133 {
134 auto& ui_comp = view.get<ui_document_component>(entity);
135 if(ui_comp.context)
136 {
137 if(ui_comp.document)
138 {
139 ui_comp.document->Close();
140 ui_comp.document = nullptr;
141 }
142 remove_context(ui_comp.context, debug_target_context_);
143 ui_comp.context = nullptr;
144 }
145 }
146
147 debug_target_context_ = nullptr;
148 if(debug_context_)
149 {
150 remove_context(debug_context_, debug_target_context_);
151 debug_context_ = nullptr;
152 }
153 Rml::Debugger::Shutdown();
155 APPLOG_INFO("UI system deinitialized successfully");
156 return true;
157}
158
159void ui_system::update_ui_document_common(entt::handle handle, ui_document_component& ui_comp, bool is_playing)
160{
161 bool active = handle.all_of<active_component>();
162 if(!is_playing && ui_comp.version != ui_comp.asset.version())
163 {
164 if(ui_comp.document)
165 {
166 ui_comp.document->Close();
167 ui_comp.document = nullptr;
168 }
169 if(ui_comp.context)
170 {
171 remove_context(ui_comp.context, debug_target_context_);
172 ui_comp.context = nullptr;
173 }
174 }
175 if(!ui_comp.is_loaded())
176 {
177 load_ui_document(handle, ui_comp, false);
178 }
179 if(!ui_comp.document || !ui_comp.context)
180 {
181 return;
182 }
183
184 bool active_and_enabled = active && ui_comp.is_enabled();
185 if(active_and_enabled)
186 {
187 if(!ui_comp.document->IsVisible())
188 {
189 ui_comp.document->Show();
190 }
191 }
192 else
193 {
194 if(ui_comp.document->IsVisible())
195 {
196 ui_comp.document->Hide();
197 }
198 }
199}
200
201
203 const camera& cam, float dp_ratio, bool process_input, bool& hit_found)
204{
205 APP_SCOPE_PERF("UI/Update World Space Document");
206 auto doc_size = Rml::Vector2i(static_cast<int>(comp.size.width), static_cast<int>(comp.size.height));
207 comp.context->SetDimensions(doc_size);
208 comp.context->SetDensityIndependentPixelRatio(dp_ratio);
209 if(!process_input)
210 {
211 return;
212 }
213 auto& input = ctx.get_cached<input_system>();
214 if(!input.manager.is_input_allowed())
215 {
216 return;
217 }
218 int px = -1;
219 int py = -1;
220 if(!hit_found)
221 {
222 auto& transform = handle.get<transform_component>();
223 const auto scale = comp.get_world_space_scale();
224 const auto quad_transform = transform.get_transform_global() * math::transform::scaling(scale);
225 math::vec2 pixel;
226 auto mouse_x = input.manager.get_mouse().get_position().x;
227 auto mouse_y = input.manager.get_mouse().get_position().y;
228 if(cam.project_to_quad(math::vec2(mouse_x, mouse_y), quad_transform, comp.size.width, comp.size.height, pixel))
229 {
230 px = static_cast<int>(pixel.x);
231 py = static_cast<int>(pixel.y);
232 }
233 }
234 if(process_mouse_move(scn, comp.context, px, py))
235 {
236 hit_found = true;
237 }
238}
239
240void ui_system::update_screen_space_document(rtti::context& ctx, Rml::Context* context, int viewport_width,
241 int viewport_height, float dp_ratio, scene& scn, bool process_input, bool& hit_found)
242{
243 APP_SCOPE_PERF("UI/Update Screen Space Document");
244 auto doc_size = Rml::Vector2i(viewport_width, viewport_height);
245 context->SetDimensions(doc_size);
246 context->SetDensityIndependentPixelRatio(dp_ratio);
247 if(!process_input)
248 {
249 return;
250 }
251 auto& input = ctx.get_cached<input_system>();
252 if(input.manager.is_input_allowed())
253 {
254 auto mouse_x = input.manager.get_mouse().get_position().x;
255 auto mouse_y = input.manager.get_mouse().get_position().y;
256 if(process_mouse_move(scn, context, static_cast<int>(mouse_x), static_cast<int>(mouse_y)))
257 {
258 hit_found = true;
259 }
260 }
261}
262
263void ui_system::update_world_space(rtti::context& ctx, entt::handle camera_entity, scene& scn, bool& process_input)
264{
265 APP_SCOPE_PERF("UI/Update World Space");
266 auto& camera_comp = camera_entity.get<camera_component>();
267 const auto& cam = camera_comp.get_camera();
268 auto& input = ctx.get_cached<input_system>();
269
270 // world space ui does not depend on the viewport size, so we can use a fixed dp_ratio
271 float dp_ratio = 1.0f;
272
273 auto& play = ctx.get_cached<play_mode>();
274 bool is_playing = play.is_active();
275
276
278 struct world_space_doc
279 {
280 entt::handle handle;
281 ui_document_component* comp = nullptr;
282 float distance = 0.0f;
283 };
284 std::vector<world_space_doc> world_space_visible;
285
286
288 [&](entt::entity entity, ui_document_component& ui_comp, active_component& active)
289 {
291 {
292 return;
293 }
294 auto handle = scn.create_handle(entity);
295 update_ui_document_common(handle, ui_comp, is_playing);
296 if(!ui_comp.context || !ui_comp.document || !ui_comp.is_enabled())
297 {
298 return;
299 }
300 if(!ui_comp.document->IsVisible())
301 {
302 return;
303 }
304 auto& transform_comp = handle.get<transform_component>();
305 const auto& world_transform = transform_comp.get_transform_global();
306 auto bbox = ui_comp.get_bounds();
307 if(!cam.test_obb(bbox, world_transform))
308 {
309 return;
310 }
311 float dist = math::distance2(cam.get_position(), transform_comp.get_position_global());
312 world_space_visible.push_back({handle, &ui_comp, dist});
313 });
314 std::sort(world_space_visible.begin(), world_space_visible.end(),
315 [](const world_space_doc& a, const world_space_doc& b) { return a.distance < b.distance; });
316 bool hit_found = false;
317 for(const auto& entry : world_space_visible)
318 {
319 auto& ui_comp = *entry.comp;
320 auto handle = entry.handle;
321 update_world_space_document(ctx, scn, handle, ui_comp, cam, dp_ratio, process_input, hit_found);
322 }
323
324 if(process_input)
325 {
326 process_input = !hit_found;
327 }
328}
329
330void ui_system::render_world_space(rtti::context& ctx, entt::handle camera_entity, scene& scn, delta_t dt)
331{
332 APP_SCOPE_PERF("UI/Render World Space");
333 (void)ctx;
334 (void)camera_entity;
335 (void)dt;
336 const auto current_frame = static_cast<uint64_t>(gfx::get_render_frame());
337
338
340 [&](entt::entity entity, ui_document_component& ui_comp, active_component& active)
341 {
343 {
344 return;
345 }
346 if(!ui_comp.context || !ui_comp.document || !ui_comp.is_enabled())
347 {
348 return;
349 }
350 if(!ui_comp.document->IsVisible())
351 {
352 return;
353 }
354 if(ui_comp.last_render_frame == current_frame)
355 {
356 return;
357 }
358 ui_comp.last_render_frame = current_frame;
360 if(!ui_comp.framebuffer || !ui_comp.framebuffer->is_valid())
361 {
362 return;
363 }
364 if(!ui_comp.render_layer_stack)
365 {
366 ui_comp.render_layer_stack = std::make_shared<RmlUi_RenderLayerStack>();
367 }
368 auto sz = ui_comp.framebuffer->get_size();
369 RmlUi_FrameState frame_state;
370 frame_state.viewport_width = static_cast<int>(sz.width);
371 frame_state.viewport_height = static_cast<int>(sz.height);
372 frame_state.framebuffer = ui_comp.framebuffer;
373 frame_state.clear_to_transparent = true;
374 frame_state.render_layers = ui_comp.render_layer_stack;
376 ui_comp.context->Update();
377 ui_comp.context->Render();
379 });
380}
381
382void ui_system::update_screen_space(rtti::context& ctx, entt::handle camera_entity, scene& scn, bool& process_input)
383{
384 APP_SCOPE_PERF("UI/Update Screen Space");
385 auto& camera_comp = camera_entity.get<camera_component>();
386 auto& input = ctx.get_cached<input_system>();
387 auto& play = ctx.get_cached<play_mode>();
388 bool is_playing = play.is_active();
389 float screen_space_dp_ratio = 1.0f;
390 auto viewport = camera_comp.get_viewport_size();
391 float screen_space_viewport_width = static_cast<int>(viewport.width);
392 float screen_space_viewport_height = static_cast<int>(viewport.height);
393 {
394 auto work_zone = input.manager.get_work_zone();
395 if(work_zone.w > 0 && work_zone.h > 0)
396 {
397 screen_space_dp_ratio =
398 (static_cast<float>(screen_space_viewport_width) / static_cast<float>(work_zone.w) +
399 static_cast<float>(screen_space_viewport_height) / static_cast<float>(work_zone.h)) *
400 0.5f;
401 }
402 }
403 bool hit_found = false;
404 scn.registry->view<ui_document_component>().each(
405 [&](entt::entity entity, ui_document_component& ui_comp)
406 {
408 {
409 return;
410 }
411 auto handle = scn.create_handle(entity);
412 update_ui_document_common(handle, ui_comp, is_playing);
413 if(!handle.all_of<active_component>())
414 {
415 return;
416 }
417 if(!ui_comp.context || !ui_comp.document || !ui_comp.is_enabled())
418 {
419 return;
420 }
421 if(!ui_comp.document->IsVisible())
422 {
423 return;
424 }
425 update_screen_space_document(ctx, ui_comp.context, screen_space_viewport_width,
426 screen_space_viewport_height, screen_space_dp_ratio, scn, process_input,
427 hit_found);
428 });
429 if(debug_context_)
430 {
431 bool debug_hit_found = false;
432 update_screen_space_document(ctx, debug_context_, screen_space_viewport_width,
433 screen_space_viewport_height, screen_space_dp_ratio, scn, process_input,
434 debug_hit_found);
435 }
436
437 if(process_input)
438 {
439 process_input = !hit_found;
440 }
441}
442
444 entt::handle camera_entity, scene& scn, delta_t dt)
445{
446 if(!output)
447 {
448 return;
449 }
450 APP_SCOPE_PERF("UI/Render Screen Space");
451 (void)ctx;
452 (void)camera_entity;
453 (void)dt;
455 [&](entt::entity entity, ui_document_component& ui_comp, active_component& active)
456 {
458 {
459 return;
460 }
461 if(!ui_comp.context || !ui_comp.document || !ui_comp.is_enabled())
462 {
463 return;
464 }
465 if(!ui_comp.document->IsVisible())
466 {
467 return;
468 }
469
470 if(!ui_comp.render_layer_stack)
471 {
472 ui_comp.render_layer_stack = std::make_shared<RmlUi_RenderLayerStack>();
473 }
474 auto sz = output->get_size();
475 RmlUi_FrameState frame_state;
476 frame_state.viewport_width = static_cast<int>(sz.width);
477 frame_state.viewport_height = static_cast<int>(sz.height);
478 frame_state.framebuffer = output;
479 frame_state.clear_to_transparent = false;
480 frame_state.render_layers = ui_comp.render_layer_stack;
482 ui_comp.context->Update();
483 ui_comp.context->Render();
485 });
486 if(debug_context_)
487 {
488 auto sz = output->get_size();
489 RmlUi_FrameState frame_state;
490 frame_state.viewport_width = static_cast<int>(sz.width);
491 frame_state.viewport_height = static_cast<int>(sz.height);
492 frame_state.framebuffer = output;
493 frame_state.clear_to_transparent = false;
494 frame_state.render_layers = debug_render_layer_stack_;
496 debug_context_->Update();
497 debug_context_->Render();
499 }
500}
501
502void ui_system::load_font(const std::string& path)
503{
504 const Rml::String font_path = fs::resolve_protocol(path).string();
505 Rml::LoadFontFace(font_path, false);
506 fonts_loaded_.insert(path);
507}
508
510{
511 load_font("engine:/data/fonts/Inter/InterVariable.ttf");
512 load_font("engine:/data/fonts/Inter/InterVariable-Italic.ttf");
513
514 load_font("engine:/data/fonts/OpenSans/OpenSans-VariableFont_wdth,wght.ttf");
515 load_font("engine:/data/fonts/OpenSans/OpenSans-Italic-VariableFont_wdth,wght.ttf");
516
517 load_font("engine:/data/fonts/RobotoMono/RobotoMono-VariableFont_wght.ttf");
518 load_font("engine:/data/fonts/RobotoMono/RobotoMono-Italic-VariableFont_wght.ttf");
519}
520
521void ui_system::on_create_component(entt::registry& r, entt::entity e)
522{
523
524
525}
526
527void ui_system::on_load_component(entt::registry& r, entt::entity e)
528{
529 auto& component = r.get<ui_document_component>(e);
530 if(!component.is_loaded())
531 {
532 auto& ctx = engine::context();
533 auto& system = ctx.get_cached<ui_system>();
534 auto handle = entt::handle(r, e);
535 system.load_ui_document(handle, component, true);
536 }
537}
538
539void ui_system::on_destroy_component(entt::registry& r, entt::entity e)
540{
541 auto& component = r.get<ui_document_component>(e);
542 if(component.context)
543 {
544 if(component.document)
545 {
546 component.document->Close();
547 component.document = nullptr;
548 }
549 Rml::Context* null_context = nullptr;
550 remove_context(component.context, null_context);
551 component.context = nullptr;
552 }
553 component.framebuffer.reset();
554 component.render_layer_stack.reset();
555}
556
558{
559 // This method would register ECS callbacks for ui_document_component
560 // creation and destruction. Implementation depends on your ECS system's
561 // callback mechanism. For now, this is a placeholder for the architecture.
562 APPLOG_INFO("UI component callbacks registered");
563}
564
566{
567 Rml::ReleaseFontResources();
568 Rml::ReleaseRenderManagers();
569 Rml::ReleaseTextures();
570 Rml::ReleaseCompiledGeometry();
571}
572
574{
575 return Rml::Debugger::IsVisible();
576}
578{
579 Rml::Debugger::SetVisible(enabled);
580}
581
583{
584 APP_SCOPE_PERF("UI/Update UI Documents");
585
586 scn.registry->view<ui_document_component>().each(
587 [&](entt::entity entity, ui_document_component& ui_comp)
588 {
589 auto handle = scn.create_handle(entity);
590 bool active = handle.all_of<active_component>();
591 auto& play = ctx.get_cached<play_mode>();
592 if(!play.is_simulation_running() && ui_comp.version != ui_comp.asset.version())
593 {
594 if(ui_comp.document)
595 {
596 ui_comp.document->Close();
597 ui_comp.document = nullptr;
598 }
599 if(ui_comp.context)
600 {
601 remove_context(ui_comp.context, debug_target_context_);
602 ui_comp.context = nullptr;
603 }
604 }
605 if(!ui_comp.is_loaded())
606 {
607 load_ui_document(handle, ui_comp, false);
608 }
609 if(!ui_comp.document || !ui_comp.context)
610 {
611 return;
612 }
613
614 bool active_and_enabled = active && ui_comp.is_enabled();
615 if(active_and_enabled)
616 {
617 if(!ui_comp.document->IsVisible())
618 {
619 ui_comp.document->Show();
620 }
621 }
622 else
623 {
624 if(ui_comp.document->IsVisible())
625 {
626 ui_comp.document->Hide();
627 }
628 }
629 });
630}
631
632auto ui_system::load_ui_document(entt::handle handle, ui_document_component& component, bool log_error) -> bool
633{
634 APP_SCOPE_PERF("UI/Load UI Document");
635 if(!component.asset)
636 {
637 if(log_error)
638 {
639 APPLOG_WARNING("Cannot load document: asset is empty");
640 }
641 return false;
642 }
643 auto asset = component.asset.get();
644 if(!asset)
645 {
646 if(log_error)
647 {
648 APPLOG_WARNING("Cannot load document: document_path is empty");
649 }
650 return false;
651 }
652
653 if(!component.context)
654 {
655 Rml::String context_name = "doc_" + std::to_string(next_context_id++);
656 int w = (component.render_mode == ui_render_mode::screen_space_overlay) ? 1024 : static_cast<int>(component.size.width);
657 int h = (component.render_mode == ui_render_mode::screen_space_overlay) ? 768 : static_cast<int>(component.size.height);
658 component.context = Rml::CreateContext(context_name, Rml::Vector2i(w, h));
659 if(!component.context)
660 {
661 if(log_error)
662 {
663 APPLOG_ERROR("Failed to create RmlUi context '{}' for document {}", context_name, component.asset.id());
664 }
665 return false;
666 }
667 }
668
669 auto real = fs::resolve_protocol(component.asset.id());
670 Rml::String url_safe_path = Rml::StringUtilities::Replace(real.string(), ':', '|');
671
672 auto raw_document = component.context->LoadDocumentFromMemory(asset->content, url_safe_path);
673 if(!raw_document)
674 {
675 if(log_error)
676 {
677 APPLOG_ERROR("Failed to load UI document: {}", component.asset.id());
678 }
679 return false;
680 }
681
682 raw_document->SetId("body");
683 raw_document->ReloadStyleSheet();
684 if(component.document)
685 {
686 component.document->Close();
687 }
688 component.document = raw_document;
689 component.version = component.asset.version();
690
691 return true;
692}
693
695{
696 if(gfx::needs_recreate(comp.framebuffer, comp.size))
697 {
698 comp.framebuffer.reset();
699
700 uint64_t texture_flags = BGFX_TEXTURE_RT | BGFX_TEXTURE_BLIT_DST | BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP;
701 auto color_texture = std::make_shared<gfx::texture>(static_cast<uint16_t>(comp.size.width),
702 static_cast<uint16_t>(comp.size.height),
703 false,
704 1,
705 gfx::texture_format::RGBA8,
706 texture_flags);
707
708 if(!color_texture->is_valid())
709 {
710 APPLOG_ERROR("Failed to create UI document color texture");
711 return;
712 }
713
714 std::vector<gfx::texture::ptr> textures;
715 textures.push_back(color_texture);
716 comp.framebuffer.reset();
717 comp.framebuffer = std::make_shared<gfx::frame_buffer>(textures);
718 if(!comp.framebuffer->is_valid())
719 {
720 APPLOG_ERROR("Failed to create UI document framebuffer");
721 comp.framebuffer.reset();
722 }
723 }
724
725}
726
727} // namespace unravel
uint32_t width
uint32_t height
entt::handle b
entt::handle a
static auto scaling(const vec2_t &scale) noexcept -> transform_t
Create a scaling transform.
Class that contains core camera data, used for rendering and other purposes.
auto get_camera() -> camera &
Gets the camera object.
Class representing a camera. Contains functionality for manipulating and updating a camera....
Definition camera.h:62
auto project_to_quad(const math::vec2 &viewport_point, const math::transform &quad_transform, uint32_t quad_width, uint32_t quad_height, math::vec2 &pixel_out) const -> bool
Raycasts from viewport point onto a quad plane and returns pixel coordinates.
Definition camera.cpp:582
Component that handles transformations (position, rotation, scale, etc.) in the ACE framework.
auto get_transform_global() const noexcept -> const math::transform &
Gets the global transform.
std::chrono::duration< float > delta_t
uint16_t view
#define APPLOG_WARNING(...)
Definition logging.h:19
#define APPLOG_ERROR(...)
Definition logging.h:20
#define APPLOG_INFO(...)
Definition logging.h:18
#define APPLOG_TRACE(...)
Definition logging.h:17
path resolve_protocol(const path &_path)
Given the specified path/filename, resolve the final full filename. This will be based on either the ...
auto needs_recreate(const gfx::frame_buffer::ptr &fbo, const usize32_t &size) -> bool
uint32_t get_render_frame()
void end_frame()
Finish frame and present to the framebuffer specified in begin_frame state Call this after rendering ...
void begin_frame(RmlUi_FrameState &state)
Prepare render state for RmlUi rendering Call this before rendering RmlUi context.
auto initialize(rtti::context &ctx, const char *window_name, int width, int height) -> bool
Initialize the RmlUi backend with engine context.
void shutdown()
Shutdown the RmlUi backend and cleanup resources.
@ screen_space_overlay
Screen-space overlay (viewport-sized or positioned)
@ world_space
World-space (rendered on 3D quad, uses transform)
std::vector< float > scale
#define APP_SCOPE_PERF(name_literal)
Create a scoped performance timer that records to the timeline profiler. Only accepts string literals...
Definition profiler.h:675
entt::handle entity
auto get_cached() -> T &
Definition context.hpp:49
T width
Definition basetypes.hpp:55
T height
Definition basetypes.hpp:56
Per-frame state passed to begin_frame. Contains all mutable state for the render pass.
gfx::frame_buffer::ptr framebuffer
std::shared_ptr< RmlUi_RenderLayerStack > render_layers
Manages the entity-component-system (ECS) operations for the ACE framework.
Definition ecs.h:12
static auto context() -> rtti::context &
Definition engine.cpp:111
hpp::event< void(rtti::context &, os::event &e)> on_os_event
os events
Definition events.h:35
Owns play-mode state and orchestrates the splash -> running lifecycle.
Definition play_mode.h:17
auto is_active() const -> bool
Definition play_mode.h:36
auto get_main_window() const -> render_window *
Definition renderer.cpp:316
Represents a scene in the ACE framework, managing entities and their relationships.
Definition scene.h:70
static auto get_scene(entt::handle entity) -> scene *
Gets the scene from an entity handle.
Definition scene.cpp:343
std::unique_ptr< entt::registry > registry
The registry that manages all entities in the scene.
Definition scene.h:187
auto create_handle(entt::entity e) -> entt::handle
Creates an entity in the scene.
Definition scene.cpp:428
Component that holds a reference to a UI document for RmlUi rendering.
asset_handle< ui_tree > asset
Path to the UI document file (HTML/RML)
auto get_world_space_scale() const -> math::vec2
usize32_t size
Document resolution (pixels). For screen-space: viewport size. For world-space: configurable (e....
Rml::ElementDocument * document
Loaded RmlUi document (owned by context)
auto is_enabled() const -> bool
Check if document is currently enabled.
gfx::frame_buffer::ptr framebuffer
Framebuffer for world-space render-to-texture (owned by component)
Rml::Context * context
Per-document RmlUi context (owned by this component)
std::shared_ptr< RmlUi_RenderLayerStack > render_layer_stack
Render layer stack for this document (owned by component, used during render pass)
auto get_bounds() const -> math::bbox
Gets the bounding box of the document.
auto is_loaded() const -> bool
Check if document is currently loaded.
ui_render_mode render_mode
Rendering mode: screen or world.
uint32_t last_render_frame
Last frame this world-space document was rendered (for render-once-per-frame)
System responsible for managing user interface components and rendering.
Definition ui_system.h:39
void on_os_event(rtti::context &ctx, os::event &event)
Handle OS events and forward to RmlUi.
void update_world_space(rtti::context &ctx, entt::handle camera_entity, scene &scn, bool &process_input)
Update world-space UI documents (common + world-space specific). Call before render_world_space.
auto init(rtti::context &ctx) -> bool
Initializes the UI system with the given context.
Definition ui_system.cpp:68
void render_screen_space(rtti::context &ctx, const gfx::frame_buffer::ptr &output, entt::handle camera_entity, scene &scn, delta_t dt)
Render screen-space overlay UI documents to output. Call after update_screen_space.
static void on_create_component(entt::registry &r, entt::entity e)
void update_screen_space(rtti::context &ctx, entt::handle camera_entity, scene &scn, bool &process_input)
Update screen-space UI documents (common + screen-space specific). Call before render_screen_space.
static void on_load_component(entt::registry &r, entt::entity e)
void update_ui_document_common(entt::handle handle, ui_document_component &ui_comp, bool is_playing)
void update_ui_documents(rtti::context &ctx, scene &scn)
Update UI documents (load, reload, visibility). No camera dependency.
auto process_mouse_move(scene &scn, Rml::Context *context, int x, int y) -> bool
void set_debugger_enabled(bool enabled)
void render_world_space(rtti::context &ctx, entt::handle camera_entity, scene &scn, delta_t dt)
Render world-space UI documents (sort + frame-state render). Call after update_world_space.
static void on_destroy_component(entt::registry &r, entt::entity e)
auto deinit(rtti::context &ctx) -> bool
Deinitializes the UI system with the given context.
auto is_debugger_enabled() const -> bool
void ensure_document_framebuffer(ui_document_component &comp)
Ensure framebuffer exists for document; create or resize if needed.
void register_component_callbacks(rtti::context &ctx)
Register component creation/destruction callbacks with ECS.
void update_screen_space_document(rtti::context &ctx, Rml::Context *context, int viewport_width, int viewport_height, float dp_ratio, scene &scn, bool process_input, bool &hit_found)
Update a single screen-space document (dimensions, density, input). Call from render path.
void load_fonts()
Load fonts.
void update_world_space_document(rtti::context &ctx, scene &scn, entt::handle handle, ui_document_component &comp, const camera &cam, float dp_ratio, bool process_input, bool &hit_found)
Update a single world-space document (dimensions, density, input). Call from render path,...
void load_font(const std::string &path)
auto load_ui_document(entt::handle entity, ui_document_component &component, bool log_error=true) -> bool
Load a UI document for a specific component.
gfx::uniform_handle handle
Definition uniform.cpp:9
bool enabled