mirror of
https://github.com/CDevJoud/Minecraft-Community-Edition.git
synced 2026-07-16 06:31:20 +00:00
rmlui is WIP
This commit is contained in:
72
Minecraft-Community-Edition/UI/Backend/RmlUI_Backend.hpp
Normal file
72
Minecraft-Community-Edition/UI/Backend/RmlUI_Backend.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This source file is part of RmlUi, the HTML/CSS Interface Middleware
|
||||
*
|
||||
* For the latest information, see http://github.com/mikke89/RmlUi
|
||||
*
|
||||
* Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
|
||||
* Copyright (c) 2019-2023 The RmlUi Team, and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RMLUI_BACKENDS_BACKEND_H
|
||||
#define RMLUI_BACKENDS_BACKEND_H
|
||||
|
||||
#include <RmlUi/Core/Input.h>
|
||||
#include <RmlUi/Core/RenderInterface.h>
|
||||
#include <RmlUi/Core/SystemInterface.h>
|
||||
#include <RmlUi/Core/Types.h>
|
||||
|
||||
using KeyDownCallback = bool (*)(Rml::Context* context, Rml::Input::KeyIdentifier key, int key_modifier, float native_dp_ratio, bool priority);
|
||||
|
||||
/**
|
||||
This interface serves as a basic abstraction over the various backends included with RmlUi. It is mainly intended as an example to get something
|
||||
simple up and running, and provides just enough functionality for the included samples.
|
||||
|
||||
This interface may be used directly for simple applications and testing. However, for anything more advanced we recommend to use the backend as a
|
||||
starting point and copy relevant parts into the main loop of your application. On the other hand, the underlying platform and renderer used by the
|
||||
backend are intended to be re-usable as is.
|
||||
*/
|
||||
namespace Backend {
|
||||
|
||||
// Initializes the backend, including the custom system and render interfaces, and opens a window for rendering the RmlUi context.
|
||||
bool Initialize(const char* window_name, int width, int height, bool allow_resize);
|
||||
// Closes the window and release all resources owned by the backend, including the system and render interfaces.
|
||||
void Shutdown();
|
||||
|
||||
// Returns a pointer to the custom system interface which should be provided to RmlUi.
|
||||
Rml::SystemInterface* GetSystemInterface();
|
||||
// Returns a pointer to the custom render interface which should be provided to RmlUi.
|
||||
Rml::RenderInterface* GetRenderInterface();
|
||||
|
||||
// Polls and processes events from the current platform, and applies any relevant events to the provided RmlUi context and the key down callback.
|
||||
// @return False to indicate that the application should be closed.
|
||||
bool ProcessEvents(Rml::Context* context, KeyDownCallback key_down_callback = nullptr, bool power_save = false);
|
||||
// Request application closure during the next event processing call.
|
||||
void RequestExit();
|
||||
|
||||
// Prepares the render state to accept rendering commands from RmlUi, call before rendering the RmlUi context.
|
||||
void BeginFrame();
|
||||
// Presents the rendered frame to the screen, call after rendering the RmlUi context.
|
||||
void PresentFrame();
|
||||
|
||||
} // namespace Backend
|
||||
|
||||
#endif
|
||||
221
Minecraft-Community-Edition/UI/Backend/RmlUI_Platform_dms.cpp
Normal file
221
Minecraft-Community-Edition/UI/Backend/RmlUI_Platform_dms.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
#include "RmlUI_Platform_dms.hpp"
|
||||
#include <RmlUi/Core/Context.h>
|
||||
|
||||
#include <RmlUi/Core/StringUtilities.h>
|
||||
#include <Core/WindowBase.hpp>
|
||||
#include <Core/Clipboard.hpp>
|
||||
|
||||
namespace mce::ui::priv {
|
||||
SystemInterface_dms::SystemInterface_dms(core::QEventBus& qBus) : qBus(qBus) {
|
||||
SystemInterface_dms::clock.restart();
|
||||
|
||||
bool cursors_valid = true;
|
||||
cursors_valid &= cursor_default.loadFromSystem(sf::Cursor::Arrow);
|
||||
cursors_valid &= cursor_move.loadFromSystem(sf::Cursor::SizeAll);
|
||||
cursors_valid &= cursor_pointer.loadFromSystem(sf::Cursor::Hand);
|
||||
cursors_valid &= cursor_resize.loadFromSystem(sf::Cursor::SizeTopLeftBottomRight) || cursor_resize.loadFromSystem(sf::Cursor::SizeAll);
|
||||
cursors_valid &= cursor_cross.loadFromSystem(sf::Cursor::Cross);
|
||||
cursors_valid &= cursor_text.loadFromSystem(sf::Cursor::Text);
|
||||
cursors_valid &= cursor_unavailable.loadFromSystem(sf::Cursor::NotAllowed);
|
||||
}
|
||||
SystemInterface_dms::~SystemInterface_dms() {
|
||||
|
||||
}
|
||||
void SystemInterface_dms::setWindow(sf::WindowBase* window) {
|
||||
SystemInterface_dms::window = window;
|
||||
}
|
||||
double SystemInterface_dms::GetElapsedTime() {
|
||||
return SystemInterface_dms::clock.getElapsedTime().asSeconds();
|
||||
}
|
||||
// TODO we need a way to pass the window base to the SystemInterface_dms
|
||||
void SystemInterface_dms::SetMouseCursor(const Rml::String& cursorName) {
|
||||
if (cursorName.empty() || cursorName == "arrow") {
|
||||
window->setMouseCursor(cursor_default);
|
||||
}
|
||||
else if (cursorName == "move") {
|
||||
window->setMouseCursor(cursor_move);
|
||||
}
|
||||
else if (cursorName == "pointer") {
|
||||
window->setMouseCursor(cursor_pointer);
|
||||
}
|
||||
else if (cursorName == "resize") {
|
||||
window->setMouseCursor(cursor_resize);
|
||||
}
|
||||
else if (cursorName == "cross") {
|
||||
window->setMouseCursor(cursor_cross);
|
||||
}
|
||||
else if (cursorName == "text") {
|
||||
window->setMouseCursor(cursor_text);
|
||||
}
|
||||
else if (cursorName == "unavailable") {
|
||||
window->setMouseCursor(cursor_unavailable);
|
||||
}
|
||||
else if (Rml::StringUtilities::StartsWith(cursorName, "rmlui-scroll")) {
|
||||
window->setMouseCursor(cursor_move);
|
||||
}
|
||||
}
|
||||
// TODO we need a way to pass the window base to the SystemInterface_dms
|
||||
void SystemInterface_dms::SetClipboardText(const Rml::String& text) {
|
||||
sf::Clipboard::setString(text);
|
||||
}
|
||||
// TODO we need a way to pass the window base to the SystemInterface_dms
|
||||
void SystemInterface_dms::GetClipboardText(Rml::String& text) {
|
||||
text = sf::Clipboard::getString();
|
||||
}
|
||||
bool SystemInterface_dms::LogMessage(Rml::Log::Type type, const Rml::String& msg) {
|
||||
event::Log log;
|
||||
log.channel = "default";
|
||||
switch (type) {
|
||||
case Rml::Log::LT_ERROR:
|
||||
log.severity = event::Log::Severity::ERROR;
|
||||
break;
|
||||
case Rml::Log::LT_WARNING:
|
||||
log.severity = event::Log::Severity::WARN;
|
||||
break;
|
||||
case Rml::Log::LT_INFO:
|
||||
log.severity = event::Log::Severity::INFO;
|
||||
break;
|
||||
case Rml::Log::LT_DEBUG:
|
||||
log.severity = event::Log::Severity::DEBUG;
|
||||
break;
|
||||
default:
|
||||
log.severity = event::Log::Severity::INFO;
|
||||
}
|
||||
|
||||
log.msg += "RmlUI: ";
|
||||
log.msg += msg;
|
||||
qBus.post(log);
|
||||
|
||||
return true;
|
||||
}
|
||||
bool inputEventHandler(Rml::Context* context, sf::WindowHandle hWnd, sf::Event& event) {
|
||||
bool result = true;
|
||||
|
||||
switch (event.type) {
|
||||
case sf::Event::MouseMoved: result = context->ProcessMouseMove(event.mouseMove.x, event.mouseMove.y, getKeyModifierState()); break;
|
||||
case sf::Event::MouseButtonPressed: result = context->ProcessMouseButtonDown(event.mouseButton.button, getKeyModifierState()); break;
|
||||
case sf::Event::MouseButtonReleased: result = context->ProcessMouseButtonUp(event.mouseButton.button, getKeyModifierState()); break;
|
||||
case sf::Event::MouseWheelMoved: result = context->ProcessMouseWheel(float(-event.mouseWheel.delta), getKeyModifierState()); break;
|
||||
case sf::Event::MouseLeft: result = context->ProcessMouseLeave(); break;
|
||||
case sf::Event::TextEntered:
|
||||
{
|
||||
Rml::Character character = Rml::Character(event.text.unicode);
|
||||
if (character == Rml::Character('\r'))
|
||||
character = Rml::Character('\n');
|
||||
|
||||
if (event.text.unicode >= 32 || character == Rml::Character('\n'))
|
||||
result = context->ProcessTextInput(character);
|
||||
}
|
||||
break;
|
||||
case sf::Event::KeyPressed: result = context->ProcessKeyDown(convertKey(event.key.code), getKeyModifierState()); break;
|
||||
case sf::Event::KeyReleased: result = context->ProcessKeyUp(convertKey(event.key.code), getKeyModifierState()); break;
|
||||
default: break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Rml::Input::KeyIdentifier convertKey(sf::Keyboard::Key key) {
|
||||
switch (key) {
|
||||
case sf::Keyboard::Key::A: return Rml::Input::KI_A;
|
||||
case sf::Keyboard::Key::B: return Rml::Input::KI_B;
|
||||
case sf::Keyboard::Key::C: return Rml::Input::KI_C;
|
||||
case sf::Keyboard::Key::D: return Rml::Input::KI_D;
|
||||
case sf::Keyboard::Key::E: return Rml::Input::KI_E;
|
||||
case sf::Keyboard::Key::F: return Rml::Input::KI_F;
|
||||
case sf::Keyboard::Key::G: return Rml::Input::KI_G;
|
||||
case sf::Keyboard::Key::H: return Rml::Input::KI_H;
|
||||
case sf::Keyboard::Key::I: return Rml::Input::KI_I;
|
||||
case sf::Keyboard::Key::J: return Rml::Input::KI_J;
|
||||
case sf::Keyboard::Key::K: return Rml::Input::KI_K;
|
||||
case sf::Keyboard::Key::L: return Rml::Input::KI_L;
|
||||
case sf::Keyboard::Key::M: return Rml::Input::KI_M;
|
||||
case sf::Keyboard::Key::N: return Rml::Input::KI_N;
|
||||
case sf::Keyboard::Key::O: return Rml::Input::KI_O;
|
||||
case sf::Keyboard::Key::P: return Rml::Input::KI_P;
|
||||
case sf::Keyboard::Key::Q: return Rml::Input::KI_Q;
|
||||
case sf::Keyboard::Key::R: return Rml::Input::KI_R;
|
||||
case sf::Keyboard::Key::S: return Rml::Input::KI_S;
|
||||
case sf::Keyboard::Key::T: return Rml::Input::KI_T;
|
||||
case sf::Keyboard::Key::U: return Rml::Input::KI_U;
|
||||
case sf::Keyboard::Key::V: return Rml::Input::KI_V;
|
||||
case sf::Keyboard::Key::W: return Rml::Input::KI_W;
|
||||
case sf::Keyboard::Key::X: return Rml::Input::KI_X;
|
||||
case sf::Keyboard::Key::Y: return Rml::Input::KI_Y;
|
||||
case sf::Keyboard::Key::Z: return Rml::Input::KI_Z;
|
||||
case sf::Keyboard::Key::Num0: return Rml::Input::KI_0;
|
||||
case sf::Keyboard::Key::Num1: return Rml::Input::KI_1;
|
||||
case sf::Keyboard::Key::Num2: return Rml::Input::KI_2;
|
||||
case sf::Keyboard::Key::Num3: return Rml::Input::KI_3;
|
||||
case sf::Keyboard::Key::Num4: return Rml::Input::KI_4;
|
||||
case sf::Keyboard::Key::Num5: return Rml::Input::KI_5;
|
||||
case sf::Keyboard::Key::Num6: return Rml::Input::KI_6;
|
||||
case sf::Keyboard::Key::Num7: return Rml::Input::KI_7;
|
||||
case sf::Keyboard::Key::Num8: return Rml::Input::KI_8;
|
||||
case sf::Keyboard::Key::Num9: return Rml::Input::KI_9;
|
||||
case sf::Keyboard::Key::Numpad0: return Rml::Input::KI_NUMPAD0;
|
||||
case sf::Keyboard::Key::Numpad1: return Rml::Input::KI_NUMPAD1;
|
||||
case sf::Keyboard::Key::Numpad2: return Rml::Input::KI_NUMPAD2;
|
||||
case sf::Keyboard::Key::Numpad3: return Rml::Input::KI_NUMPAD3;
|
||||
case sf::Keyboard::Key::Numpad4: return Rml::Input::KI_NUMPAD4;
|
||||
case sf::Keyboard::Key::Numpad5: return Rml::Input::KI_NUMPAD5;
|
||||
case sf::Keyboard::Key::Numpad6: return Rml::Input::KI_NUMPAD6;
|
||||
case sf::Keyboard::Key::Numpad7: return Rml::Input::KI_NUMPAD7;
|
||||
case sf::Keyboard::Key::Numpad8: return Rml::Input::KI_NUMPAD8;
|
||||
case sf::Keyboard::Key::Numpad9: return Rml::Input::KI_NUMPAD9;
|
||||
case sf::Keyboard::Key::Left: return Rml::Input::KI_LEFT;
|
||||
case sf::Keyboard::Key::Right: return Rml::Input::KI_RIGHT;
|
||||
case sf::Keyboard::Key::Up: return Rml::Input::KI_UP;
|
||||
case sf::Keyboard::Key::Down: return Rml::Input::KI_DOWN;
|
||||
case sf::Keyboard::Key::Add: return Rml::Input::KI_ADD;
|
||||
case sf::Keyboard::Backspace: return Rml::Input::KI_BACK;
|
||||
case sf::Keyboard::Key::Delete: return Rml::Input::KI_DELETE;
|
||||
case sf::Keyboard::Key::Divide: return Rml::Input::KI_DIVIDE;
|
||||
case sf::Keyboard::Key::End: return Rml::Input::KI_END;
|
||||
case sf::Keyboard::Key::Escape: return Rml::Input::KI_ESCAPE;
|
||||
case sf::Keyboard::Key::F1: return Rml::Input::KI_F1;
|
||||
case sf::Keyboard::Key::F2: return Rml::Input::KI_F2;
|
||||
case sf::Keyboard::Key::F3: return Rml::Input::KI_F3;
|
||||
case sf::Keyboard::Key::F4: return Rml::Input::KI_F4;
|
||||
case sf::Keyboard::Key::F5: return Rml::Input::KI_F5;
|
||||
case sf::Keyboard::Key::F6: return Rml::Input::KI_F6;
|
||||
case sf::Keyboard::Key::F7: return Rml::Input::KI_F7;
|
||||
case sf::Keyboard::Key::F8: return Rml::Input::KI_F8;
|
||||
case sf::Keyboard::Key::F9: return Rml::Input::KI_F9;
|
||||
case sf::Keyboard::Key::F10: return Rml::Input::KI_F10;
|
||||
case sf::Keyboard::Key::F11: return Rml::Input::KI_F11;
|
||||
case sf::Keyboard::Key::F12: return Rml::Input::KI_F12;
|
||||
case sf::Keyboard::Key::F13: return Rml::Input::KI_F13;
|
||||
case sf::Keyboard::Key::F14: return Rml::Input::KI_F14;
|
||||
case sf::Keyboard::Key::F15: return Rml::Input::KI_F15;
|
||||
case sf::Keyboard::Key::Home: return Rml::Input::KI_HOME;
|
||||
case sf::Keyboard::Key::Insert: return Rml::Input::KI_INSERT;
|
||||
case sf::Keyboard::Key::LControl: return Rml::Input::KI_LCONTROL;
|
||||
case sf::Keyboard::Key::LShift: return Rml::Input::KI_LSHIFT;
|
||||
case sf::Keyboard::Key::Multiply: return Rml::Input::KI_MULTIPLY;
|
||||
case sf::Keyboard::Key::Pause: return Rml::Input::KI_PAUSE;
|
||||
case sf::Keyboard::Key::RControl: return Rml::Input::KI_RCONTROL;
|
||||
case sf::Keyboard::Enter: return Rml::Input::KI_RETURN;
|
||||
case sf::Keyboard::Key::RShift: return Rml::Input::KI_RSHIFT;
|
||||
case sf::Keyboard::Key::Space: return Rml::Input::KI_SPACE;
|
||||
case sf::Keyboard::Key::Subtract: return Rml::Input::KI_SUBTRACT;
|
||||
case sf::Keyboard::Key::Tab: return Rml::Input::KI_TAB;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return Rml::Input::KI_UNKNOWN;
|
||||
|
||||
}
|
||||
int getKeyModifierState() {
|
||||
int modifiers = 0;
|
||||
|
||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::LShift) || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::RShift))
|
||||
modifiers |= Rml::Input::KM_SHIFT;
|
||||
|
||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::LControl) || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::RControl))
|
||||
modifiers |= Rml::Input::KM_CTRL;
|
||||
|
||||
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::LAlt) || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::RAlt))
|
||||
modifiers |= Rml::Input::KM_ALT;
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include <RmlUi/Core/Input.h>
|
||||
#include <RmlUi/Core/SystemInterface.h>
|
||||
#include <RmlUi/Core/Types.h>
|
||||
|
||||
#include <Core/WindowBase.hpp>
|
||||
#include <Core/Event.hpp>
|
||||
#include <Core/Clock.hpp>
|
||||
#include <Core/QEventBus.hpp>
|
||||
|
||||
namespace mce::ui::priv {
|
||||
class SystemInterface_dms : public Rml::SystemInterface {
|
||||
public:
|
||||
SystemInterface_dms(core::QEventBus& qBus);
|
||||
~SystemInterface_dms();
|
||||
|
||||
void setWindow(sf::WindowBase* window);
|
||||
|
||||
double GetElapsedTime() override;
|
||||
|
||||
void SetMouseCursor(const Rml::String& cursorName) override;
|
||||
|
||||
void SetClipboardText(const Rml::String& text) override;
|
||||
void GetClipboardText(Rml::String& text) override;
|
||||
|
||||
bool LogMessage(Rml::Log::Type type, const Rml::String& msg) override;
|
||||
|
||||
private:
|
||||
|
||||
sf::WindowBase* window;
|
||||
core::QEventBus& qBus;
|
||||
sf::Clock clock;
|
||||
sf::Cursor cursor_default;
|
||||
sf::Cursor cursor_move;
|
||||
sf::Cursor cursor_pointer;
|
||||
sf::Cursor cursor_resize;
|
||||
sf::Cursor cursor_cross;
|
||||
sf::Cursor cursor_text;
|
||||
sf::Cursor cursor_unavailable;
|
||||
};
|
||||
bool inputEventHandler(Rml::Context* ctx, sf::WindowHandle hWnd, sf::Event& event);
|
||||
|
||||
Rml::Input::KeyIdentifier convertKey(sf::Keyboard::Key key);
|
||||
|
||||
int getKeyModifierState();
|
||||
}
|
||||
613
Minecraft-Community-Edition/UI/Backend/RmlUI_Renderer_bgfx.cpp
Normal file
613
Minecraft-Community-Edition/UI/Backend/RmlUI_Renderer_bgfx.cpp
Normal file
@@ -0,0 +1,613 @@
|
||||
#include "RmlUI_Renderer_bgfx.hpp"
|
||||
#include "RmlUi/Core/Log.h"
|
||||
#include "Graphics/BgfxRenderContext.hpp"
|
||||
#include <bx/math.h>
|
||||
|
||||
namespace mce::ui::priv {
|
||||
bgfx::VertexLayout RmlVertex::layout() {
|
||||
static bgfx::VertexLayout vLayout;
|
||||
vLayout.begin()
|
||||
.add(bgfx::Attrib::Position, 2, bgfx::AttribType::Float)
|
||||
.add(bgfx::Attrib::Color0, 4, bgfx::AttribType::Uint8, true)
|
||||
.add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float)
|
||||
.end();
|
||||
return vLayout;
|
||||
}
|
||||
|
||||
// Convert Rml::Matrix4f (column-major, same as glm) to float[16]
|
||||
static void Matrix4ToFloat16(const Rml::Matrix4f& m, float out[16]) {
|
||||
// Rml::Matrix4f stores data column-major, same layout as bgfx expects
|
||||
std::memcpy(out, m.data(), 16 * sizeof(float));
|
||||
}
|
||||
|
||||
RenderInterface_bgfx::RenderInterface_bgfx(core::ResourceManager& rm) :
|
||||
gfx::Renderer(0, rm.getRenderFactory(), rm.getRenderContext().getRenderAPI()),
|
||||
rsrcMgr(rm),
|
||||
programs({}),
|
||||
activeProgram(RmlProgramId::None),
|
||||
shader(nullptr),
|
||||
nextGeometryId(1),
|
||||
geometries({}),
|
||||
nextFilterId(1),
|
||||
filters({}),
|
||||
nextShaderId(1),
|
||||
shaders({}),
|
||||
nextTextureId(1),
|
||||
transform(Rml::Matrix4f::Identity()),
|
||||
projection(Rml::Matrix4f::Identity()),
|
||||
scissorEnabled(false),
|
||||
scissorRegion({}),
|
||||
clipMaskEnabled(false),
|
||||
stencilRef(0), // current stencil reference value
|
||||
stencilValue(1), // value written during clip-mask
|
||||
currentViewId(0),
|
||||
baseView(0), // first view allocated this frame
|
||||
viewDirty(true),
|
||||
lastBoundTexture(nullptr),
|
||||
viewportWidth(0), viewportHeight(0), viewportOffsetX(0), viewportOffsetY(0),
|
||||
valid(false),
|
||||
drawOrder(0) {
|
||||
programs.resize(static_cast<eastl_size_t>(RmlProgramId::Count));
|
||||
|
||||
this->valid = this->loadPrograms();
|
||||
|
||||
if (this->valid) {
|
||||
// Build a fullscreen quad [-1,1] covering NDC space.
|
||||
// Uses tex_coord for UV.
|
||||
struct FSVert { float x, y; uint32_t col; float u, v; };
|
||||
static const FSVert verts[] = {
|
||||
{-1.f, -1.f, 0xFFFFFFFF, 0.f, 0.f},
|
||||
{ 1.f, -1.f, 0xFFFFFFFF, 1.f, 0.f},
|
||||
{ 1.f, 1.f, 0xFFFFFFFF, 1.f, 1.f},
|
||||
{-1.f, 1.f, 0xFFFFFFFF, 0.f, 1.f},
|
||||
};
|
||||
static const int indices[] = { 0, 1, 2, 0, 2, 3 };
|
||||
|
||||
this->fullscreenQuad = CompileGeometry(
|
||||
{ reinterpret_cast<const Rml::Vertex*>(verts), 4 },
|
||||
{ indices, 6 }
|
||||
);
|
||||
}
|
||||
auto api = rsrcMgr.getRenderContext().getRenderAPI();
|
||||
this->frameBufferOriginBottomLeft = (api == gfx::RenderContext::API::OpenGL || api == gfx::RenderContext::API::OpenGLES);
|
||||
}
|
||||
RenderInterface_bgfx::~RenderInterface_bgfx() {
|
||||
|
||||
}
|
||||
void RenderInterface_bgfx::setViewport(int w, int h, int ox, int oy) {
|
||||
viewportWidth = w;
|
||||
viewportHeight = h;
|
||||
viewportOffsetX = ox;
|
||||
viewportOffsetY = oy;
|
||||
projection = makeProjection();
|
||||
}
|
||||
Rml::Matrix4f RenderInterface_bgfx::makeProjection() const {
|
||||
const float L = static_cast<float>(viewportOffsetX);
|
||||
const float R = L + static_cast<float>(viewportWidth);
|
||||
const float T = static_cast<float>(viewportOffsetY);
|
||||
const float B = T + static_cast<float>(viewportHeight);
|
||||
|
||||
Rml::Matrix4f proj = Rml::Matrix4f::Identity();
|
||||
|
||||
float* m = proj.data();
|
||||
|
||||
m[0] = 2.f / (R - L);
|
||||
m[5] = 2.f / (T - B);
|
||||
m[10] = 1.f;
|
||||
|
||||
m[12] = -(R + L) / (R - L);
|
||||
m[13] = -(T + B) / (T - B);
|
||||
m[14] = 0.f;
|
||||
m[15] = 1.f;
|
||||
|
||||
return proj;
|
||||
}
|
||||
Rml::CompiledGeometryHandle RenderInterface_bgfx::CompileGeometry(Rml::Span<const Rml::Vertex> vertices, Rml::Span<const int> indices) {
|
||||
BgfxCompiledGeometry geo;
|
||||
|
||||
gfx::RenderFactory& factory = rsrcMgr.getRenderFactory();
|
||||
const bgfx::Memory* vMem = bgfx::copy(
|
||||
vertices.data(),
|
||||
static_cast<uint32_t>(vertices.size() * sizeof(Rml::Vertex))
|
||||
);
|
||||
|
||||
gfx::flags::Buffer fl;
|
||||
fl.addFlag(gfx::flags::Buffer::None);
|
||||
geo.vb = factory.createVertexBuffer(vMem, RmlVertex::layout(), fl, "RmlUIVertex");
|
||||
|
||||
eastl::vector<uint32_t> idx32(indices.size());
|
||||
for (size_t i = 0; i < indices.size(); ++i) {
|
||||
idx32[i] = static_cast<uint32_t>(indices[i]);
|
||||
}
|
||||
|
||||
const bgfx::Memory* imem = bgfx::copy(idx32.data(), static_cast<uint32_t>(idx32.size() * sizeof(uint32_t)));
|
||||
gfx::flags::Buffer iFlags;
|
||||
iFlags.addFlag(gfx::flags::Buffer::Index32);
|
||||
geo.ib = factory.createIndexBuffer(imem, iFlags);
|
||||
|
||||
geo.num_indices = static_cast<int>(indices.size());
|
||||
|
||||
Rml::CompiledGeometryHandle handle = this->nextGeometryId++;
|
||||
this->geometries[handle] = geo;
|
||||
return handle;
|
||||
}
|
||||
void RenderInterface_bgfx::RenderGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture) {
|
||||
submitGeometry(handle, translation, texture);
|
||||
}
|
||||
void RenderInterface_bgfx::ReleaseGeometry(Rml::CompiledGeometryHandle handle) {
|
||||
auto it = this->geometries.find(handle);
|
||||
if (it == geometries.end()) return;
|
||||
geometries.erase(it);
|
||||
}
|
||||
Rml::TextureHandle RenderInterface_bgfx::LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source) {
|
||||
return Rml::TextureHandle();
|
||||
}
|
||||
Rml::TextureHandle RenderInterface_bgfx::GenerateTexture(Rml::Span<const Rml::byte> source_data, Rml::Vector2i source_dimensions) {
|
||||
|
||||
const bgfx::Memory* mem = bgfx::copy(source_data.data(), static_cast<uint32_t>(source_data.size()));
|
||||
auto& factory = rsrcMgr.getRenderFactory();
|
||||
eastl::shared_ptr<gfx::Texture> tex = factory.createTexture(
|
||||
sf::Vector2i(source_dimensions.x, source_dimensions.y),
|
||||
false, 1, bgfx::TextureFormat::RGBA8,
|
||||
BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP,
|
||||
mem
|
||||
);
|
||||
if (tex == nullptr) {
|
||||
return {};
|
||||
}
|
||||
// when the tex goes out of scope the reference count decreement and it will release the texture but Rml still want to access it
|
||||
// so we insert it in a container that increement the reference count
|
||||
generatedTextures.push_back(tex);
|
||||
return static_cast<Rml::TextureHandle>(tex->getTextureHandle().idx);
|
||||
|
||||
}
|
||||
void RenderInterface_bgfx::ReleaseTexture(Rml::TextureHandle texture_handle) {
|
||||
auto it = eastl::find_if(generatedTextures.begin(), generatedTextures.end(), [&texture_handle](const eastl::shared_ptr<gfx::Texture>& p) {
|
||||
return p->getTextureHandle().idx == static_cast<uint16_t>(texture_handle);
|
||||
});
|
||||
|
||||
generatedTextures.erase(it);
|
||||
}
|
||||
void RenderInterface_bgfx::EnableScissorRegion(bool enable) {
|
||||
this->scissorEnabled = enable;
|
||||
}
|
||||
void RenderInterface_bgfx::SetScissorRegion(Rml::Rectanglei region) {
|
||||
this->scissorRegion = region;
|
||||
}
|
||||
void RenderInterface_bgfx::EnableClipMask(bool enable) {
|
||||
this->clipMaskEnabled = enable;
|
||||
}
|
||||
void RenderInterface_bgfx::RenderToClipMask(Rml::ClipMaskOperation operation, Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation) {
|
||||
auto it = this->geometries.find(geometry);
|
||||
if (it == this->geometries.end()) return;
|
||||
|
||||
const auto& geo = it->second;
|
||||
|
||||
auto& creationShader = this->programs[static_cast<int>(RmlProgramId::Creation)];
|
||||
|
||||
if (creationShader == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
useProgram(RmlProgramId::Creation);
|
||||
|
||||
// Sets the transformation uniform
|
||||
auto translate_mat = Rml::Matrix4f::Translate(translation.x, translation.y, 0.0f);
|
||||
auto mvp = projection * transform * translate_mat;
|
||||
|
||||
float outproj[16];
|
||||
|
||||
Matrix4ToFloat16(mvp, outproj);
|
||||
|
||||
if (creationShader != nullptr) {
|
||||
creationShader->setUniform("u_transform", outproj);
|
||||
}
|
||||
|
||||
Renderer::setVertexBuffer(geo.vb);
|
||||
Renderer::setIndexBuffer(geo.ib);
|
||||
|
||||
uint64_t state = BGFX_STATE_MSAA;
|
||||
bgfx::setState(state);
|
||||
|
||||
if (scissorEnabled) {
|
||||
bgfx::setScissor(
|
||||
uint16_t(this->scissorRegion.Left()),
|
||||
uint16_t(this->scissorRegion.Top()),
|
||||
uint16_t(this->scissorRegion.Width()),
|
||||
uint16_t(this->scissorRegion.Height())
|
||||
);
|
||||
}
|
||||
|
||||
uint32_t stencil = 0;
|
||||
|
||||
switch (operation) {
|
||||
case Rml::ClipMaskOperation::Set:
|
||||
{
|
||||
stencilRef = stencilValue++;
|
||||
|
||||
stencil = 0
|
||||
| BGFX_STENCIL_TEST_ALWAYS
|
||||
| BGFX_STENCIL_FUNC_REF(stencilRef)
|
||||
| BGFX_STENCIL_FUNC_RMASK(0xFF)
|
||||
| BGFX_STENCIL_OP_FAIL_S_ZERO
|
||||
| BGFX_STENCIL_OP_FAIL_Z_ZERO
|
||||
| BGFX_STENCIL_OP_PASS_Z_REPLACE;
|
||||
break;
|
||||
}
|
||||
case Rml::ClipMaskOperation::SetInverse:
|
||||
{
|
||||
stencilRef = stencilValue++;
|
||||
|
||||
// Fullscreen quad to fill stencil
|
||||
{
|
||||
auto fsIt = geometries.find(fullscreenQuad);
|
||||
if (fsIt != geometries.end()) {
|
||||
const auto& fsGeo = fsIt->second;
|
||||
|
||||
float identity[16];
|
||||
bx::mtxIdentity(identity);
|
||||
|
||||
Renderer::setVertexBuffer(fsGeo.vb);
|
||||
Renderer::setIndexBuffer(fsGeo.ib);
|
||||
bgfx::setState(BGFX_STATE_MSAA);
|
||||
|
||||
uint32_t fillStencil = 0
|
||||
| BGFX_STENCIL_TEST_ALWAYS
|
||||
| BGFX_STENCIL_FUNC_REF(stencilRef)
|
||||
| BGFX_STENCIL_FUNC_RMASK(0xFF)
|
||||
| BGFX_STENCIL_OP_FAIL_S_REPLACE
|
||||
| BGFX_STENCIL_OP_FAIL_Z_REPLACE
|
||||
| BGFX_STENCIL_OP_PASS_Z_REPLACE;
|
||||
|
||||
bgfx::setStencil(fillStencil);
|
||||
|
||||
creationShader->setUniform("u_transform", identity);
|
||||
bgfx::submit(currentViewId, creationShader->getProgramHandle(), drawOrder++);
|
||||
}
|
||||
}
|
||||
auto translate_mat = Rml::Matrix4f::Translate(translation.x, translation.y, 0.0f);
|
||||
auto mvp = projection * transform * translate_mat;
|
||||
|
||||
float outproj[16];
|
||||
|
||||
Matrix4ToFloat16(mvp, outproj);
|
||||
creationShader->setUniform("u_transform", outproj);
|
||||
bgfx::setVertexBuffer(0, geo.vb->getNativeHandle());
|
||||
bgfx::setIndexBuffer(geo.ib->getNativeHandle());
|
||||
bgfx::setState(BGFX_STATE_MSAA);
|
||||
|
||||
stencil = 0
|
||||
| BGFX_STENCIL_TEST_ALWAYS
|
||||
| BGFX_STENCIL_FUNC_REF(0)
|
||||
| BGFX_STENCIL_FUNC_RMASK(0xFF)
|
||||
| BGFX_STENCIL_OP_FAIL_S_REPLACE
|
||||
| BGFX_STENCIL_OP_FAIL_Z_REPLACE
|
||||
| BGFX_STENCIL_OP_PASS_Z_REPLACE;
|
||||
break;
|
||||
}
|
||||
case Rml::ClipMaskOperation::Intersect:
|
||||
{
|
||||
uint8_t old_ref = stencilRef;
|
||||
stencilRef = stencilValue++;
|
||||
|
||||
if (stencilRef == old_ref + 1) {
|
||||
stencil = 0
|
||||
| BGFX_STENCIL_TEST_EQUAL
|
||||
| BGFX_STENCIL_FUNC_REF(old_ref)
|
||||
| BGFX_STENCIL_FUNC_RMASK(0xFF)
|
||||
| BGFX_STENCIL_OP_FAIL_S_KEEP
|
||||
| BGFX_STENCIL_OP_FAIL_Z_KEEP
|
||||
| BGFX_STENCIL_OP_PASS_Z_INCR;
|
||||
}
|
||||
else {
|
||||
stencil = 0
|
||||
| BGFX_STENCIL_TEST_LEQUAL
|
||||
| BGFX_STENCIL_FUNC_REF(stencilRef)
|
||||
| BGFX_STENCIL_FUNC_RMASK(0xFF)
|
||||
| BGFX_STENCIL_OP_FAIL_S_KEEP
|
||||
| BGFX_STENCIL_OP_FAIL_Z_KEEP
|
||||
| BGFX_STENCIL_OP_PASS_Z_REPLACE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (scissorEnabled) {
|
||||
bgfx::setScissor(
|
||||
uint16_t(scissorRegion.Left()),
|
||||
uint16_t(scissorRegion.Top()),
|
||||
uint16_t(scissorRegion.Width()),
|
||||
uint16_t(scissorRegion.Height())
|
||||
);
|
||||
}
|
||||
bgfx::setStencil(stencil);
|
||||
bgfx::submit(currentViewId, creationShader->getProgramHandle(), drawOrder++);
|
||||
}
|
||||
void RenderInterface_bgfx::SetTransform(const Rml::Matrix4f* transform) {}
|
||||
Rml::LayerHandle RenderInterface_bgfx::PushLayer() {
|
||||
return Rml::LayerHandle();
|
||||
}
|
||||
void RenderInterface_bgfx::CompositeLayers(Rml::LayerHandle source, Rml::LayerHandle destination, Rml::BlendMode blend_mode, Rml::Span<const Rml::CompiledFilterHandle> filters) {}
|
||||
void RenderInterface_bgfx::PopLayer() {}
|
||||
Rml::TextureHandle RenderInterface_bgfx::SaveLayerAsTexture() {
|
||||
return Rml::TextureHandle();
|
||||
}
|
||||
Rml::CompiledFilterHandle RenderInterface_bgfx::SaveLayerAsMaskImage() {
|
||||
return Rml::CompiledFilterHandle();
|
||||
}
|
||||
Rml::CompiledFilterHandle RenderInterface_bgfx::CompileFilter(const Rml::String& name, const Rml::Dictionary& parameters) {
|
||||
return Rml::CompiledFilterHandle();
|
||||
}
|
||||
void RenderInterface_bgfx::ReleaseFilter(Rml::CompiledFilterHandle filter) {}
|
||||
Rml::CompiledShaderHandle RenderInterface_bgfx::CompileShader(const Rml::String& name, const Rml::Dictionary& parameters) {
|
||||
return Rml::CompiledShaderHandle();
|
||||
}
|
||||
void RenderInterface_bgfx::RenderShader(Rml::CompiledShaderHandle shader_handle, Rml::CompiledGeometryHandle geometry_handle, Rml::Vector2f translation, Rml::TextureHandle texture) {}
|
||||
void RenderInterface_bgfx::ReleaseShader(Rml::CompiledShaderHandle effect_handle) {}
|
||||
bool RenderInterface_bgfx::loadPrograms() {
|
||||
struct ProgramDef { RmlProgramId id; const char* vs; const char* fs; };
|
||||
static const ProgramDef defs[] = {
|
||||
{ RmlProgramId::Color, "assets.shaders.rmlui.vs" , "assets.shaders.rmlui_color.fs"},
|
||||
{ RmlProgramId::Texture, "assets.shaders.rmlui.vs" , "assets.shaders.rmlui_texture.fs"},
|
||||
{ RmlProgramId::Passthrough, "assets.shaders.rmlui_passthrough.vs", "assets.shaders.rmlui_passthrough.fs" },
|
||||
{ RmlProgramId::BlendMask, "assets.shaders.rmlui_passthrough.vs", "assets.shaders.rmlui_blendmask.fs" },
|
||||
{ RmlProgramId::Blur, "assets.shaders.rmlui_passthrough.vs", "assets.shaders.rmlui_blur.fs" },
|
||||
{ RmlProgramId::DropShadow, "assets.shaders.rmlui_passthrough.vs", "assets.shaders.rmlui_dropshadow.fs" },
|
||||
{ RmlProgramId::ColorMatrix, "assets.shaders.rmlui_passthrough.vs", "assets.shaders.rmlui_colormatrix.fs"},
|
||||
{ RmlProgramId::Creation, "assets.shaders.rmlui.vs" , "assets.shaders.rmlui_creation.fs" },
|
||||
{ RmlProgramId::Gradient, "assets.shaders.rmlui.vs" , "assets.shaders.rmlui_gradient.fs" }
|
||||
};
|
||||
|
||||
for (auto& def : defs) {
|
||||
eastl::shared_ptr<gfx::ShaderProgram> sp = rsrcMgr.getShader(def.vs, def.fs);
|
||||
if (!sp) {
|
||||
Rml::Log::Message(Rml::Log::LT_ERROR, "RmlUi-BGFX: Failed to load program %s, / %s", def.vs, def.fs);
|
||||
destroyPrograms();
|
||||
return false;
|
||||
}
|
||||
programs[static_cast<eastl_size_t>(def.id)] = sp;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderInterface_bgfx::destroyPrograms() {
|
||||
for (int i = 0; i < static_cast<int>(RmlProgramId::Count); ++i) {
|
||||
programs.pop_back();
|
||||
}
|
||||
}
|
||||
bgfx::ViewId RenderInterface_bgfx::allocateView() {
|
||||
auto bgfxCtx = reinterpret_cast<gfx::BgfxRenderContext*>(&rsrcMgr.getRenderContext());
|
||||
return bgfxCtx->nextViewId++;
|
||||
}
|
||||
void RenderInterface_bgfx::setupView(bgfx::ViewId view, const BgfxFrameBuffer& fb) {
|
||||
bgfx::setViewName(view, std::string("RmlUi_Layer: " + std::to_string(view)).c_str());
|
||||
bgfx::setViewFrameBuffer(view, fb.fb->getNativeHandle());
|
||||
bgfx::setViewRect(view, 0, 0, uint16_t(fb.width), uint16_t(fb.height));
|
||||
bgfx::setViewClear(view, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH | BGFX_CLEAR_STENCIL, 0x00000000, 1.0f, 0);
|
||||
|
||||
float proj[16];
|
||||
Matrix4ToFloat16(this->projection, proj);
|
||||
float identity[16];
|
||||
bx::mtxIdentity(identity);
|
||||
bgfx::setViewTransform(view, identity, proj);
|
||||
|
||||
bgfx::touch(view);
|
||||
}
|
||||
void RenderInterface_bgfx::setupViewSpace(bgfx::ViewId view) {
|
||||
const auto& topLayer = layers.getTopLayer();
|
||||
bgfx::setViewRect(view, 0, 0, uint16_t(topLayer.width), uint16_t(topLayer.height));
|
||||
}
|
||||
|
||||
void RenderInterface_bgfx::ensureView() {
|
||||
if (!this->viewDirty) return;
|
||||
viewDirty = false;
|
||||
}
|
||||
|
||||
|
||||
void RenderInterface_bgfx::useProgram(RmlProgramId id) {
|
||||
activeProgram = id;
|
||||
}
|
||||
void RenderInterface_bgfx::submitGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture, RmlProgramId program_override) {
|
||||
auto it = this->geometries.find(handle);
|
||||
if (it == this->geometries.end()) return;
|
||||
|
||||
const auto& geo = it->second;
|
||||
|
||||
RmlProgramId prog = program_override;
|
||||
if (prog == RmlProgramId::None) {
|
||||
if (texture == 0) {
|
||||
prog = RmlProgramId::Color;
|
||||
}
|
||||
else if (texture == TexturePostprocess) {
|
||||
prog = activeProgram;
|
||||
}
|
||||
else {
|
||||
prog = RmlProgramId::Texture;
|
||||
}
|
||||
}
|
||||
|
||||
useProgram(prog);
|
||||
|
||||
auto& shader = programs[static_cast<int>(prog)];
|
||||
if (!shader) return;
|
||||
setTransformUniform(translation);
|
||||
|
||||
auto translate_mat = Rml::Matrix4f::Translate(translation.x, translation.y, 0.0f);
|
||||
auto mvp = projection * transform * translate_mat;
|
||||
|
||||
float outproj[16];
|
||||
|
||||
Matrix4ToFloat16(mvp, outproj);
|
||||
|
||||
if (texture != 0 && texture != TexturePostprocess) {
|
||||
eastl::shared_ptr<gfx::Texture> tex = nullptr;
|
||||
if (texture == TextureEnableWithoutBinding) {
|
||||
tex = lastBoundTexture;
|
||||
}
|
||||
else {
|
||||
auto it = eastl::find_if(generatedTextures.begin(), generatedTextures.end(), [&texture](const eastl::shared_ptr<gfx::Texture>& p) {
|
||||
return p->getTextureHandle().idx == texture;
|
||||
});
|
||||
if (it != generatedTextures.end()) {
|
||||
tex = *it;
|
||||
}
|
||||
lastBoundTexture = tex;
|
||||
}
|
||||
}
|
||||
|
||||
auto state = buildBaseState();
|
||||
if (prog == RmlProgramId::Color || texture == 0) {
|
||||
auto nState = BGFX_STATE_BLEND_FUNC(gfx::flags::State::BlendOne, gfx::flags::State::BlendInvSrcAlpha);
|
||||
state.addFlag(gfx::flags::State::Enum(nState));
|
||||
}
|
||||
else {
|
||||
auto nState = BGFX_STATE_BLEND_FUNC(gfx::flags::State::BlendOne, gfx::flags::State::BlendInvSrcAlpha);
|
||||
state.addFlag(gfx::flags::State::Enum(nState));
|
||||
}
|
||||
|
||||
// Scissor
|
||||
if (scissorEnabled) {
|
||||
bgfx::setScissor(
|
||||
uint16_t(scissorRegion.Left()),
|
||||
uint16_t(scissorRegion.Top()),
|
||||
uint16_t(scissorRegion.Width()),
|
||||
uint16_t(scissorRegion.Height())
|
||||
);
|
||||
}
|
||||
|
||||
// Stencil
|
||||
if (clipMaskEnabled) {
|
||||
uint32_t stencil = buildStencilState();
|
||||
bgfx::setStencil(stencil);
|
||||
}
|
||||
|
||||
|
||||
Renderer::setState(state);
|
||||
|
||||
Renderer::setVertexBuffer(geo.vb);
|
||||
Renderer::setIndexBuffer(geo.ib);
|
||||
|
||||
if (lastBoundTexture != nullptr)
|
||||
shader->setUniform("s_texture0", lastBoundTexture);
|
||||
|
||||
shader->setUniform("u_transform", outproj);
|
||||
|
||||
Renderer::submit(shader, drawOrder++);
|
||||
}
|
||||
gfx::flags::State RenderInterface_bgfx::buildBaseState() const {
|
||||
static gfx::flags::State sf;
|
||||
sf.addFlag(gfx::flags::State::WriteRGB)
|
||||
.addFlag(gfx::flags::State::WriteA)
|
||||
.addFlag(gfx::flags::State::MSAA);
|
||||
return sf;
|
||||
}
|
||||
uint32_t RenderInterface_bgfx::buildStencilState() const {
|
||||
return 0
|
||||
| BGFX_STENCIL_TEST_EQUAL
|
||||
| BGFX_STENCIL_FUNC_REF(stencilRef)
|
||||
| BGFX_STENCIL_FUNC_RMASK(0xFF)
|
||||
| BGFX_STENCIL_OP_FAIL_S_KEEP
|
||||
| BGFX_STENCIL_OP_FAIL_Z_KEEP
|
||||
| BGFX_STENCIL_OP_PASS_Z_KEEP;
|
||||
}
|
||||
void RenderInterface_bgfx::setTransformUniform(Rml::Vector2f translation) {}
|
||||
|
||||
BgfxFrameBuffer RenderInterface_bgfx::createFrameBuffer(int w, int h, bool with_depth_stencil) {
|
||||
auto& factory = Renderer::getFactory();
|
||||
|
||||
BgfxFrameBuffer fb;
|
||||
fb.width = w;
|
||||
fb.height = h;
|
||||
|
||||
fb.color = factory.createTexture({ uint16_t(w), uint16_t(h) }, false, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_RT | BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP);
|
||||
if (with_depth_stencil) {
|
||||
fb.depth_stencil = factory.createTexture({ uint16_t(w), uint16_t(h) }, false, 1, bgfx::TextureFormat::D24S8, BGFX_TEXTURE_RT);
|
||||
|
||||
bgfx::Attachment attachments[2]{};
|
||||
attachments[0].init(fb.color->getTextureHandle());
|
||||
attachments[0].init(fb.depth_stencil->getTextureHandle());
|
||||
fb.fb = factory.createFrameBuffer(2, attachments, false);
|
||||
}
|
||||
else {
|
||||
bgfx::Attachment att;
|
||||
att.init(fb.color->getTextureHandle());
|
||||
fb.fb = factory.createFrameBuffer(1, &att, false);
|
||||
}
|
||||
|
||||
return fb;
|
||||
}
|
||||
void RenderInterface_bgfx::destroyFrameBuffer(BgfxFrameBuffer& fb) {
|
||||
fb.fb.reset();
|
||||
fb.color.reset();
|
||||
fb.depth_stencil.reset();
|
||||
fb.fb = nullptr;
|
||||
fb.color = nullptr;
|
||||
fb.depth_stencil = nullptr;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Layer Stack
|
||||
//
|
||||
|
||||
RenderInterface_bgfx::RenderLayerStack::~RenderLayerStack() {
|
||||
|
||||
}
|
||||
void RenderInterface_bgfx::RenderLayerStack::destroyAll(RenderInterface_bgfx& ri) {
|
||||
for (auto& fb : fb_layers_) {
|
||||
ri.destroyFrameBuffer(fb);
|
||||
}
|
||||
fb_layers_.clear();
|
||||
for (auto& fb : fb_postprocess_) {
|
||||
ri.destroyFrameBuffer(fb);
|
||||
}
|
||||
fb_postprocess_.clear();
|
||||
|
||||
layers_size_ = 0;
|
||||
}
|
||||
|
||||
void RenderInterface_bgfx::RenderLayerStack::beginFrame(RenderInterface_bgfx& ri, int w, int h) {
|
||||
ri_ = &ri;
|
||||
if (w != width_ || h != height_) {
|
||||
RenderLayerStack::destroyAll(ri);
|
||||
width_ = w;
|
||||
height_ = h;
|
||||
}
|
||||
|
||||
// === CRITICAL FIX: Pre-create ALL post-process buffers so vector never reallocates ===
|
||||
// This prevents dangling references mid-frame (the real cause of "garbage data")
|
||||
fb_postprocess_.reserve(8);
|
||||
while (fb_postprocess_.size() < 4) {
|
||||
this->fb_postprocess_.emplace_back(ri.createFrameBuffer(width_, height_, false));
|
||||
}
|
||||
|
||||
layers_size_ = 0;
|
||||
RenderLayerStack::pushLayer(ri);
|
||||
}
|
||||
void RenderInterface_bgfx::RenderLayerStack::endFrame() {
|
||||
// Keep framebuffers alive for reuse next frame.
|
||||
// Just reset the active layer count.
|
||||
layers_size_ = 0;
|
||||
}
|
||||
|
||||
Rml::LayerHandle RenderInterface_bgfx::RenderLayerStack::pushLayer(RenderInterface_bgfx& ri) {
|
||||
if (layers_size_ >= (int)fb_layers_.size()) {
|
||||
fb_layers_.push_back(ri.createFrameBuffer(width_, height_, true));
|
||||
}
|
||||
|
||||
Rml::LayerHandle handle = static_cast<Rml::LayerHandle>(layers_size_);
|
||||
layers_size_++;
|
||||
return handle;
|
||||
}
|
||||
void RenderInterface_bgfx::RenderLayerStack::popLayer() {
|
||||
if (layers_size_ > 1)
|
||||
layers_size_--;
|
||||
}
|
||||
const BgfxFrameBuffer& RenderInterface_bgfx::RenderLayerStack::getLayer(Rml::LayerHandle h) const {
|
||||
return fb_layers_[static_cast<int>(h)];
|
||||
}
|
||||
|
||||
const BgfxFrameBuffer& RenderInterface_bgfx::RenderLayerStack::getTopLayer() const {
|
||||
return fb_layers_[layers_size_ - 1];
|
||||
}
|
||||
}
|
||||
277
Minecraft-Community-Edition/UI/Backend/RmlUI_Renderer_bgfx.hpp
Normal file
277
Minecraft-Community-Edition/UI/Backend/RmlUI_Renderer_bgfx.hpp
Normal file
@@ -0,0 +1,277 @@
|
||||
#pragma once
|
||||
#include <RmlUi/Core/RenderInterface.h>
|
||||
#include <RmlUi/Core/Types.h>
|
||||
|
||||
#include <Graphics/Renderer.hpp>
|
||||
#include <Graphics/Color.hpp>
|
||||
#include <IO/VirtualFileSystem.hpp>
|
||||
|
||||
#include <EASTL/unordered_map.h>
|
||||
#include <EASTL/fixed_vector.h>
|
||||
|
||||
#include <Core/ResourceManager.hpp>
|
||||
|
||||
namespace mce::ui::priv {
|
||||
class RmlVertex {
|
||||
public:
|
||||
RmlVertex();
|
||||
RmlVertex(sf::Vector2f position, gfx::Color color, sf::Vector2f texCoords);
|
||||
sf::Vector2f position;
|
||||
gfx::Color color;
|
||||
sf::Vector2f texCoords;
|
||||
|
||||
static bgfx::VertexLayout layout();
|
||||
};
|
||||
enum class RmlProgramId : int {
|
||||
None = 0,
|
||||
Color, // vertex-color only
|
||||
Texture, // textured + vertex-color (premultiplied alpha)
|
||||
Passthrough, // fullscreen blit / layer composite
|
||||
BlendMask, // composite with blend-mask texture
|
||||
Blur, // separable Gaussian blur
|
||||
DropShadow, // drop-shadow filter
|
||||
ColorMatrix, // generic 4×5 colour-matrix filter
|
||||
Creation, // stencil-write (clip-mask geometry)
|
||||
Gradient, // linear / radial / conic gradient shader
|
||||
Count
|
||||
};
|
||||
|
||||
enum class FilterType { None, Blur, DropShadow, ColorMatrix, MaskImage };
|
||||
|
||||
struct CompiledFilter {
|
||||
FilterType type = FilterType::None;
|
||||
// Blur / DropShadow
|
||||
float sigma = 0.f;
|
||||
// DropShadow extras
|
||||
Rml::Vector2f offset = { 0, 0 };
|
||||
Rml::Colourf color = { 0, 0, 0, 1 };
|
||||
// ColorMatrix (row-major 4×5, stored as mat4 + vec4 translate)
|
||||
float color_matrix[16] = {};
|
||||
float color_translate[4] = {};
|
||||
// MaskImage
|
||||
eastl::shared_ptr<gfx::Texture> mask_texture = nullptr;
|
||||
};
|
||||
|
||||
enum class ShaderType { None, Gradient };
|
||||
|
||||
struct ShaderGradientStop { float position; Rml::Colourf color; };
|
||||
|
||||
struct CompiledShaderData {
|
||||
ShaderType type = ShaderType::None;
|
||||
// Gradient
|
||||
int gradient_function = 0; // 0=linear, 1=radial, 2=conic, 3=repeating-linear, …
|
||||
Rml::Vector2f p = {};
|
||||
Rml::Vector2f q = {};
|
||||
eastl::vector<ShaderGradientStop> stops;
|
||||
eastl::shared_ptr<gfx::Texture> stop_texture = nullptr;
|
||||
};
|
||||
|
||||
struct BgfxFrameBuffer {
|
||||
eastl::shared_ptr<gfx::FrameBuffer> fb = nullptr;
|
||||
eastl::shared_ptr<gfx::Texture> color = nullptr;
|
||||
eastl::shared_ptr<gfx::Texture> depth_stencil = nullptr;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
};
|
||||
|
||||
struct BgfxCompiledGeometry {
|
||||
eastl::shared_ptr<gfx::VertexBuffer> vb = nullptr;
|
||||
eastl::shared_ptr<gfx::IndexBuffer> ib = nullptr;
|
||||
int num_indices = 0;
|
||||
};
|
||||
|
||||
class RenderInterface_bgfx : public Rml::RenderInterface, public gfx::Renderer {
|
||||
public:
|
||||
RenderInterface_bgfx(core::ResourceManager& rm);
|
||||
~RenderInterface_bgfx();
|
||||
|
||||
void setViewport(int viewport_width, int viewport_height,
|
||||
int viewport_offset_x = 0, int viewport_offset_y = 0);
|
||||
|
||||
Rml::CompiledGeometryHandle CompileGeometry(Rml::Span<const Rml::Vertex> vertices,
|
||||
Rml::Span<const int> indices) override;
|
||||
void RenderGeometry(Rml::CompiledGeometryHandle handle,
|
||||
Rml::Vector2f translation,
|
||||
Rml::TextureHandle texture) override;
|
||||
void ReleaseGeometry(Rml::CompiledGeometryHandle handle) override;
|
||||
|
||||
Rml::TextureHandle LoadTexture(Rml::Vector2i& texture_dimensions,
|
||||
const Rml::String& source) override;
|
||||
Rml::TextureHandle GenerateTexture(Rml::Span<const Rml::byte> source_data,
|
||||
Rml::Vector2i source_dimensions) override;
|
||||
void ReleaseTexture(Rml::TextureHandle texture_handle) override;
|
||||
|
||||
void EnableScissorRegion(bool enable) override;
|
||||
void SetScissorRegion(Rml::Rectanglei region) override;
|
||||
|
||||
void EnableClipMask(bool enable) override;
|
||||
void RenderToClipMask(Rml::ClipMaskOperation mask_operation,
|
||||
Rml::CompiledGeometryHandle geometry,
|
||||
Rml::Vector2f translation) override;
|
||||
|
||||
void SetTransform(const Rml::Matrix4f* transform) override;
|
||||
|
||||
Rml::LayerHandle PushLayer() override;
|
||||
void CompositeLayers(Rml::LayerHandle source, Rml::LayerHandle destination,
|
||||
Rml::BlendMode blend_mode,
|
||||
Rml::Span<const Rml::CompiledFilterHandle> filters) override;
|
||||
void PopLayer() override;
|
||||
|
||||
Rml::TextureHandle SaveLayerAsTexture() override;
|
||||
|
||||
Rml::CompiledFilterHandle SaveLayerAsMaskImage() override;
|
||||
|
||||
Rml::CompiledFilterHandle CompileFilter(const Rml::String& name,
|
||||
const Rml::Dictionary& parameters) override;
|
||||
void ReleaseFilter(Rml::CompiledFilterHandle filter) override;
|
||||
|
||||
Rml::CompiledShaderHandle CompileShader(const Rml::String& name,
|
||||
const Rml::Dictionary& parameters) override;
|
||||
void RenderShader(Rml::CompiledShaderHandle shader_handle,
|
||||
Rml::CompiledGeometryHandle geometry_handle,
|
||||
Rml::Vector2f translation,
|
||||
Rml::TextureHandle texture) override;
|
||||
void ReleaseShader(Rml::CompiledShaderHandle effect_handle) override;
|
||||
|
||||
// Special texture-handle sentinels (same semantics as GL3)
|
||||
static constexpr Rml::TextureHandle TextureEnableWithoutBinding = Rml::TextureHandle(-1);
|
||||
static constexpr Rml::TextureHandle TexturePostprocess = Rml::TextureHandle(-2);
|
||||
|
||||
private:
|
||||
Rml::Matrix4f makeProjection() const;
|
||||
|
||||
bool loadPrograms();
|
||||
void destroyPrograms();
|
||||
|
||||
bgfx::ViewId allocateView();
|
||||
void setupView(bgfx::ViewId view, const BgfxFrameBuffer& fb);
|
||||
void setupViewSpace(bgfx::ViewId view);
|
||||
void ensureView();
|
||||
|
||||
void useProgram(RmlProgramId id);
|
||||
|
||||
void submitGeometry(Rml::CompiledGeometryHandle handle, Rml::Vector2f translation, Rml::TextureHandle texture, RmlProgramId program_override = RmlProgramId::None);
|
||||
void drawFullscreenQuad(const eastl::shared_ptr<gfx::Texture>& texture, RmlProgramId program);
|
||||
void drawFullscreenQuad(const eastl::shared_ptr<gfx::Texture>& texture, RmlProgramId program, Rml::Vector2f uv_offset, Rml::Vector2f uv_scaling, bool flip_v);
|
||||
void blitLayerToPostprocessPrimary(Rml::LayerHandle layerHandle);
|
||||
void renderFilters(Rml::Span<const Rml::CompiledFilterHandle> filterHandles);
|
||||
void renderBlur(float sigma, BgfxFrameBuffer& sourceDest, BgfxFrameBuffer& temp, Rml::Rectanglei windowFlipped);
|
||||
|
||||
gfx::flags::State buildBaseState() const;
|
||||
uint32_t buildStencilState() const;
|
||||
|
||||
void setTransformUniform(Rml::Vector2f translation);
|
||||
|
||||
// FrameBuffer helpers
|
||||
BgfxFrameBuffer createFrameBuffer(int w, int h, bool with_depth_stencil = true);
|
||||
void destroyFrameBuffer(BgfxFrameBuffer& fb);
|
||||
|
||||
bool frameBufferOriginBottomLeft = true;
|
||||
private:
|
||||
class RenderLayerStack {
|
||||
public:
|
||||
RenderLayerStack() = default;
|
||||
~RenderLayerStack();
|
||||
|
||||
Rml::LayerHandle pushLayer(RenderInterface_bgfx& ri);
|
||||
void popLayer();
|
||||
|
||||
const BgfxFrameBuffer& getLayer(Rml::LayerHandle h) const;
|
||||
const BgfxFrameBuffer& getTopLayer() const;
|
||||
Rml::LayerHandle getTopLayerHandle() const;
|
||||
|
||||
BgfxFrameBuffer& getPostProcessPrimary();
|
||||
BgfxFrameBuffer& getPostProcessSecondary();
|
||||
BgfxFrameBuffer& getPostProcessTertiary();
|
||||
BgfxFrameBuffer& getBlendMask();
|
||||
|
||||
void swapPostProcessPrimarySecondary();
|
||||
|
||||
void beginFrame(RenderInterface_bgfx& ri, int w, int h);
|
||||
void endFrame();
|
||||
|
||||
private:
|
||||
void destroyAll(RenderInterface_bgfx& ri);
|
||||
BgfxFrameBuffer& ensurePostProcess(RenderInterface_bgfx& ri, int idx);
|
||||
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
int layers_size_ = 0;
|
||||
|
||||
eastl::vector<BgfxFrameBuffer> fb_layers_;
|
||||
eastl::vector<BgfxFrameBuffer> fb_postprocess_;
|
||||
|
||||
RenderInterface_bgfx* ri_ = nullptr;
|
||||
};
|
||||
|
||||
RenderLayerStack layers;
|
||||
|
||||
// Programs
|
||||
eastl::fixed_vector<eastl::shared_ptr<gfx::ShaderProgram>, static_cast<int>(RmlProgramId::Count)> programs;
|
||||
RmlProgramId activeProgram;
|
||||
|
||||
//// Uniforms (created once, reused)
|
||||
//bgfx::UniformHandle u_transform = BGFX_INVALID_HANDLE; // mat4
|
||||
//bgfx::UniformHandle u_translate = BGFX_INVALID_HANDLE; // vec4
|
||||
//bgfx::UniformHandle u_texParams = BGFX_INVALID_HANDLE; // vec4 (uv_offset.xy, uv_scale.xy)
|
||||
//bgfx::UniformHandle u_blurParams = BGFX_INVALID_HANDLE; // vec4 (sigma, dir_x, dir_y, 0)
|
||||
//bgfx::UniformHandle u_texelSize = BGFX_INVALID_HANDLE; // vec4 (1/w, 1/h, 0, 0)
|
||||
//bgfx::UniformHandle u_colorMatrix = BGFX_INVALID_HANDLE; // mat4
|
||||
//bgfx::UniformHandle u_colorTranslate = BGFX_INVALID_HANDLE; // vec4
|
||||
//bgfx::UniformHandle u_shadowExtra = BGFX_INVALID_HANDLE; // vec4 (offset.xy, 0, 0)
|
||||
//bgfx::UniformHandle u_shadowColor = BGFX_INVALID_HANDLE; // vec4
|
||||
//bgfx::UniformHandle u_gradientParams = BGFX_INVALID_HANDLE; // vec4 (func, num_stops, 0, 0)
|
||||
//bgfx::UniformHandle u_gradientP = BGFX_INVALID_HANDLE; // vec4 (p.xy, q.xy)
|
||||
//bgfx::UniformHandle s_texture0 = BGFX_INVALID_HANDLE; // sampler
|
||||
//bgfx::UniformHandle s_texture1 = BGFX_INVALID_HANDLE; // sampler (blend mask / stops)
|
||||
eastl::shared_ptr<gfx::ShaderProgram> shader; // we load all the uniforms manually when calling `shader->setUniform()`
|
||||
|
||||
// Geometry registries
|
||||
Rml::CompiledGeometryHandle nextGeometryId;
|
||||
eastl::unordered_map<Rml::CompiledGeometryHandle, BgfxCompiledGeometry> geometries;
|
||||
|
||||
// Filter / Shader registries
|
||||
Rml::CompiledFilterHandle nextFilterId;
|
||||
eastl::unordered_map<Rml::CompiledFilterHandle, CompiledFilter> filters;
|
||||
|
||||
Rml::CompiledShaderHandle nextShaderId;
|
||||
eastl::unordered_map<Rml::CompiledShaderHandle, CompiledShaderData> shaders;
|
||||
|
||||
// Texture bookkeeping
|
||||
Rml::TextureHandle nextTextureId;
|
||||
|
||||
//Fullscreen-quad geometry (pre-built)
|
||||
Rml::CompiledGeometryHandle fullscreenQuad = 0;
|
||||
|
||||
// Render state
|
||||
Rml::Matrix4f transform;
|
||||
Rml::Matrix4f projection;
|
||||
|
||||
bool scissorEnabled;
|
||||
Rml::Rectanglei scissorRegion;
|
||||
|
||||
bool clipMaskEnabled;
|
||||
uint8_t stencilRef;
|
||||
uint8_t stencilValue;
|
||||
|
||||
uint16_t currentViewId;
|
||||
uint16_t baseView;
|
||||
bool viewDirty;
|
||||
|
||||
//bgfx::TextureHandle lastBoundTexture = BGFX_INVALID_HANDLE;
|
||||
eastl::shared_ptr<gfx::Texture> lastBoundTexture;
|
||||
|
||||
int viewportWidth;
|
||||
int viewportHeight;
|
||||
int viewportOffsetX;
|
||||
int viewportOffsetY;
|
||||
|
||||
bool valid;
|
||||
|
||||
uint32_t drawOrder;
|
||||
core::ResourceManager& rsrcMgr; // resource manager
|
||||
|
||||
eastl::vector<eastl::shared_ptr<gfx::Texture>> generatedTextures;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user