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"
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>
51 uint64_t next_context_id = 0;
53 auto remove_context(Rml::Context* context, Rml::Context*& debug_target_context) ->
void
55 if(debug_target_context == context)
57 debug_target_context =
nullptr;
58 Rml::Debugger::SetContext(
nullptr);
62 Rml::RemoveContext(context->GetName());
70 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
73 auto& ev = ctx.get_cached<
events>();
89 const auto& rend = ctx.get_cached<
renderer>();
93 auto size = window->get_window().get_size();
98 bool is_deploy_mode = ctx.has<
deploy>();
103 debug_context_ = Rml::CreateContext(
"debug", Rml::Vector2i(
width,
height));
110 Rml::Debugger::Initialise(debug_context_);
111 debug_render_layer_stack_ = std::make_shared<RmlUi_RenderLayerStack>();
115 register_component_callbacks(ctx);
125 APPLOG_TRACE(
"{}::{}", hpp::type_name_str(*
this), __func__);
127 auto& ecs_system = ctx.get_cached<
ecs>();
139 ui_comp.document->Close();
140 ui_comp.document =
nullptr;
142 remove_context(ui_comp.context, debug_target_context_);
143 ui_comp.context =
nullptr;
147 debug_target_context_ =
nullptr;
150 remove_context(debug_context_, debug_target_context_);
151 debug_context_ =
nullptr;
153 Rml::Debugger::Shutdown();
155 APPLOG_INFO(
"UI system deinitialized successfully");
162 if(!is_playing && ui_comp.
version != ui_comp.
asset.version())
171 remove_context(ui_comp.
context, debug_target_context_);
184 bool active_and_enabled = active && ui_comp.
is_enabled();
185 if(active_and_enabled)
203 const camera& cam,
float dp_ratio,
bool process_input,
bool& hit_found)
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);
214 if(!
input.manager.is_input_allowed())
226 auto mouse_x =
input.manager.get_mouse().get_position().x;
227 auto mouse_y =
input.manager.get_mouse().get_position().y;
230 px =
static_cast<int>(pixel.x);
231 py =
static_cast<int>(pixel.y);
241 int viewport_height,
float dp_ratio,
scene& scn,
bool process_input,
bool& hit_found)
244 auto doc_size = Rml::Vector2i(viewport_width, viewport_height);
245 context->SetDimensions(doc_size);
246 context->SetDensityIndependentPixelRatio(dp_ratio);
252 if(
input.manager.is_input_allowed())
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)))
271 float dp_ratio = 1.0f;
278 struct world_space_doc
282 float distance = 0.0f;
284 std::vector<world_space_doc> world_space_visible;
307 if(!cam.test_obb(bbox, world_transform))
311 float dist = math::distance2(cam.get_position(), transform_comp.get_position_global());
312 world_space_visible.push_back({
handle, &ui_comp,
dist});
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)
319 auto& ui_comp = *
entry.comp;
326 process_input = !hit_found;
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);
394 auto work_zone =
input.manager.get_work_zone();
395 if(work_zone.w > 0 && work_zone.h > 0)
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)) *
403 bool hit_found =
false;
426 screen_space_viewport_height, screen_space_dp_ratio, scn, process_input,
431 bool debug_hit_found =
false;
433 screen_space_viewport_height, screen_space_dp_ratio, scn, process_input,
439 process_input = !hit_found;
474 auto sz = output->get_size();
488 auto sz = output->get_size();
496 debug_context_->Update();
497 debug_context_->Render();
505 Rml::LoadFontFace(font_path,
false);
506 fonts_loaded_.insert(path);
511 load_font(
"engine:/data/fonts/Inter/InterVariable.ttf");
512 load_font(
"engine:/data/fonts/Inter/InterVariable-Italic.ttf");
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");
517 load_font(
"engine:/data/fonts/RobotoMono/RobotoMono-VariableFont_wght.ttf");
518 load_font(
"engine:/data/fonts/RobotoMono/RobotoMono-Italic-VariableFont_wght.ttf");
530 if(!component.is_loaded())
533 auto& system = ctx.get_cached<
ui_system>();
534 auto handle = entt::handle(r, e);
535 system.load_ui_document(
handle, component,
true);
542 if(component.context)
544 if(component.document)
546 component.document->Close();
547 component.document =
nullptr;
549 Rml::Context* null_context =
nullptr;
550 remove_context(component.context, null_context);
551 component.context =
nullptr;
554 component.render_layer_stack.reset();
567 Rml::ReleaseFontResources();
568 Rml::ReleaseRenderManagers();
569 Rml::ReleaseTextures();
570 Rml::ReleaseCompiledGeometry();
575 return Rml::Debugger::IsVisible();
579 Rml::Debugger::SetVisible(
enabled);
592 if(!play.is_simulation_running() && ui_comp.
version != ui_comp.
asset.version())
601 remove_context(ui_comp.
context, debug_target_context_);
614 bool active_and_enabled = active && ui_comp.
is_enabled();
615 if(active_and_enabled)
643 auto asset = component.asset.get();
653 if(!component.context)
655 Rml::String context_name =
"doc_" + std::to_string(next_context_id++);
658 component.context = Rml::CreateContext(context_name, Rml::Vector2i(w, h));
659 if(!component.context)
663 APPLOG_ERROR(
"Failed to create RmlUi context '{}' for document {}", context_name, component.asset.id());
670 Rml::String url_safe_path = Rml::StringUtilities::Replace(real.string(),
':',
'|');
672 auto raw_document = component.context->LoadDocumentFromMemory(asset->content, url_safe_path);
677 APPLOG_ERROR(
"Failed to load UI document: {}", component.asset.id());
682 raw_document->SetId(
"body");
683 raw_document->ReloadStyleSheet();
684 if(component.document)
686 component.document->Close();
688 component.document = raw_document;
689 component.version = component.asset.version();
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),
705 gfx::texture_format::RGBA8,
708 if(!color_texture->is_valid())
710 APPLOG_ERROR(
"Failed to create UI document color texture");
714 std::vector<gfx::texture::ptr> textures;
715 textures.push_back(color_texture);
717 comp.
framebuffer = std::make_shared<gfx::frame_buffer>(textures);
720 APPLOG_ERROR(
"Failed to create UI document framebuffer");
std::shared_ptr< frame_buffer > ptr
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....
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.
std::chrono::duration< float > delta_t
#define APPLOG_WARNING(...)
#define APPLOG_ERROR(...)
#define APPLOG_TRACE(...)
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...
Per-frame state passed to begin_frame. Contains all mutable state for the render pass.
gfx::frame_buffer::ptr framebuffer
bool clear_to_transparent
std::shared_ptr< RmlUi_RenderLayerStack > render_layers
Manages the entity-component-system (ECS) operations for the ACE framework.
static auto context() -> rtti::context &
hpp::event< void(rtti::context &, os::event &e)> on_os_event
os events
Owns play-mode state and orchestrates the splash -> running lifecycle.
auto is_active() const -> bool
auto get_main_window() const -> render_window *
Represents a scene in the ACE framework, managing entities and their relationships.
static auto get_scene(entt::handle entity) -> scene *
Gets the scene from an entity handle.
std::unique_ptr< entt::registry > registry
The registry that manages all entities in the scene.
auto create_handle(entt::entity e) -> entt::handle
Creates an entity in the scene.
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.
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.
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.