#include "RmlManager.h" #pragma push_macro("byte") #pragma push_macro("GetNextSibling") #pragma push_macro("GetFirstChild") #undef byte #undef GetNextSibling #undef GetFirstChild #include #pragma pop_macro("GetFirstChild") #pragma pop_macro("GetNextSibling") #pragma pop_macro("byte") extern ID3D11Device* g_pd3dDevice; extern ID3D11DeviceContext* g_pImmediateContext; extern ID3D11RenderTargetView* g_pRenderTargetView; RmlManager& RmlManager::Get() { static RmlManager instance; return instance; } RmlManager::RmlManager() : m_initialised(false) , m_context(nullptr) { } RmlManager::~RmlManager() { Shutdown(); } bool RmlManager::Init(int width, int height) { if (m_initialised) return true; m_file_interface = std::make_unique(); m_system_interface = std::make_unique(); m_renderer = std::make_unique(g_pd3dDevice, g_pImmediateContext); if (!m_renderer->IsInitialised()) { app.DebugPrintf("[RmlManager] Renderer failed to initialise\n"); return false; } // Set asset path for RmlUi files (relative to exe directory) Rml::String assetPath = utils.GetExeDir().string() + "/Assets/UI"; m_file_interface->SetAssetPath(assetPath); Rml::SetRenderInterface(m_renderer.get()); Rml::SetSystemInterface(m_system_interface.get()); Rml::SetFileInterface(m_file_interface.get()); if (!Rml::Initialise()) { app.DebugPrintf("[RmlManager] RmlUi Core failed to initialise\n"); return false; } // Minecraft's UI fonts (from tryashtar/minecraft-ttf) if (!Rml::LoadFontFace(assetPath + "/fonts/Mojangles.ttf", "Minecraft Default", Rml::Style::FontStyle::Normal, Rml::Style::FontWeight::Normal, false)) app.DebugPrintf("[RmlManager] FAILED loading Mojangles.ttf\n"); if (!Rml::LoadFontFace(assetPath + "/fonts/MinecraftTen.ttf", "Minecraft Default", Rml::Style::FontStyle::Normal, Rml::Style::FontWeight::Bold, true)) app.DebugPrintf("[RmlManager] FAILED loading MinecraftTen.ttf\n"); m_context = Rml::CreateContext("main", Rml::Vector2i(width, height)); if (!m_context) { app.DebugPrintf("[RmlManager] Failed to create RmlUi context\n"); Rml::Shutdown(); return false; } app.DebugPrintf("[RmlManager] Context created (%dx%d)\n", width, height); m_renderer->SetViewport(width, height); m_initialised = true; app.DebugPrintf("[RmlManager] Initialised (%dx%d)\n", width, height); UpdateDpRatio(width, height); return true; } void RmlManager::Shutdown() { if (!m_initialised) return; if (m_context) { m_context->UnloadAllDocuments(); Rml::RemoveContext(m_context->GetName()); m_context = nullptr; } Rml::Shutdown(); m_renderer.reset(); m_system_interface.reset(); m_file_interface.reset(); m_initialised = false; app.DebugPrintf("[RmlManager] Shutdown\n"); } void RmlManager::Update() { if (!m_initialised || !m_context) return; m_context->Update(); } void RmlManager::Render() { if (!m_initialised || !m_context) return; // Ensure we render to the backbuffer (gamma post-process may have restored a // different RTV). Use the singleton backbuffer RTV that the rest of the engine // binds at frame start. if (g_pRenderTargetView) { ID3D11DepthStencilView* nullDSV = nullptr; g_pImmediateContext->OMSetRenderTargets(1, &g_pRenderTargetView, nullDSV); } // Clear any SRV still bound from gamma post-process so it doesn't // interfere with RmlUi's own texture binding. ID3D11ShaderResourceView* nullSRV[1] = { nullptr }; g_pImmediateContext->PSSetShaderResources(0, 1, nullSRV); m_renderer->BeginFrame(); //GabsPuN: This DebugPrintf is called each frame when RMLUI is not used. /* int docCount = 0; for (int i = 0; i < m_context->GetNumDocuments(); i++) { auto* doc = m_context->GetDocument(i); if (doc && doc->IsVisible()) docCount++; } if (docCount == 0) app.DebugPrintf("[RmlManager] WARNING: No visible documents in context\n"); */ m_context->Render(); m_renderer->EndFrame(); } void RmlManager::OnResize(int width, int height) { if (!m_initialised || !m_context) return; m_context->SetDimensions(Rml::Vector2i(width, height)); m_renderer->SetViewport(width, height); UpdateDpRatio(width, height); } void RmlManager::UpdateDpRatio(int width, int height) { if (!m_context) return; float scale_x = static_cast(width) / 1920; float scale_y = static_cast(height) / 1080; float base_ratio = (scale_x < scale_y) ? scale_x : scale_y; float auto_dp = base_ratio; float viewport_cap = 0.88f * static_cast(height) / 850.0f; if (auto_dp > viewport_cap) auto_dp = viewport_cap; float dp_ratio = auto_dp; if (dp_ratio < 0.5f) dp_ratio = 0.5f; if (dp_ratio > 3.0f) dp_ratio = 3.0f; m_context->SetDensityIndependentPixelRatio(dp_ratio); } static void ReTranslateElement(Rml::Element* elem, Rml::SystemInterface* system_if) { Rml::ElementText* text_elem = rmlui_dynamic_cast(elem); if (text_elem) { const Rml::Variant* src = elem->GetAttribute("data-rml-source"); if (src) { Rml::String original = src->Get(); Rml::String translated; system_if->TranslateString(translated, original); if (translated != text_elem->GetText()) text_elem->SetText(translated); } } for (int i = 0; i < elem->GetNumChildren(true); i++) ReTranslateElement(elem->GetChild(i), system_if); } void RmlManager::OnLanguageChanged() { if (!m_initialised || !m_context) return; app.DebugPrintf("[RmlManager] Re-translating all documents\n"); for (int i = 0; i < m_context->GetNumDocuments(); i++) { Rml::ElementDocument* doc = m_context->GetDocument(i); if (doc) ReTranslateElement(doc, m_system_interface.get()); } }