gradients done, found some transform issues, added a testing rml file

This commit is contained in:
2026-08-31 21:56:25 -05:00
parent 260a037314
commit 499dc17f7c
7 changed files with 819 additions and 84 deletions

View File

@@ -20,11 +20,21 @@ body {
box-sizing: border-box;
}
div, section, article, header, footer, main, nav, aside {
div,
section,
article,
header,
footer,
main,
nav,
aside {
display: block;
}
span, em, strong, a {
span,
em,
strong,
a {
display: inline;
}
@@ -34,18 +44,40 @@ p {
margin: 1em 0px;
}
h1, h2, h3, h4, h5, h6 {
h1,
h2,
h3,
h4,
h5,
h6 {
display: block;
font-weight: bold;
margin: 0.83em 0px;
}
h1 { font-size: 2em; }
h2 { font-size: 1.5em; }
h3 { font-size: 1.17em; }
h4 { font-size: 1em; }
h5 { font-size: 0.83em; }
h6 { font-size: 0.67em; }
h1 {
font-size: 2em;
}
h2 {
font-size: 1.5em;
}
h3 {
font-size: 1.17em;
}
h4 {
font-size: 1em;
}
h5 {
font-size: 0.83em;
}
h6 {
font-size: 0.67em;
}
em {
font-style: italic;
@@ -72,7 +104,8 @@ blockquote {
color: #555555;
}
code, pre {
code,
pre {
font-family: "rmlui-debugger-font";
background-color: #606060;
color: #000000;
@@ -88,7 +121,8 @@ pre {
}
/* Lists */
ul, ol {
ul,
ol {
display: block;
margin: 1em 0px;
padding-left: 40px;
@@ -109,7 +143,8 @@ tr {
display: table-row;
}
td, th {
td,
th {
display: table-cell;
padding: 4px 8px;
text-align: left;
@@ -128,14 +163,19 @@ colgroup {
display: table-column-group;
}
thead, tbody, tfoot {
thead,
tbody,
tfoot {
display: table-row-group;
}
/* ==========================================================================
Form Inputs & Interactive UI Components
========================================================================== */
button, input, select, textarea {
button,
input,
select,
textarea {
display: inline-block;
font-family: "rmlui-debugger-font";
font-size: 14px;
@@ -144,7 +184,9 @@ button, input, select, textarea {
}
/* Traditional gray bevel button style */
button, input[type="submit"], input[type="button"] {
button,
input[type="submit"],
input[type="button"] {
padding: 4px 12px;
min-width: 40px;
background-color: #e1e1e1;
@@ -153,25 +195,33 @@ button, input[type="submit"], input[type="button"] {
text-align: center;
}
button:hover, input[type="submit"]:hover, input[type="button"]:hover {
button:hover,
input[type="submit"]:hover,
input[type="button"]:hover {
background-color: #d0d0d0;
border: 1px #4f4f4f;
}
button:active, input[type="submit"]:active, input[type="button"]:active {
button:active,
input[type="submit"]:active,
input[type="button"]:active {
background-color: #b5b5b5;
border: 1px #000000;
}
/* Standard text field styling */
input[type="text"], input[type="password"], textarea {
input[type="text"],
input[type="password"],
textarea {
padding: 4px;
background-color: #ffffff;
color: #000000;
border: 1px #767676;
}
input[type="text"]:focus, input[type="password"]:focus, textarea:focus {
input[type="text"]:focus,
input[type="password"]:focus,
textarea:focus {
border: 1px #005aff;
}
@@ -214,7 +264,8 @@ tab {
tab:selected {
background-color: #ffffff;
border-bottom: 1px #ffffff; /* Overlap the bottom border line */
border-bottom: 1px #ffffff;
/* Overlap the bottom border line */
font-weight: bold;
}
@@ -236,7 +287,8 @@ progress {
progress-value {
display: block;
height: 100%;
background-color: #0078d7; /* Classic Windows Blue fill */
background-color: #0078d7;
/* Classic Windows Blue fill */
}
/* ==========================================================================
@@ -265,3 +317,9 @@ progress-value {
.hidden {
display: none;
}
div.gradient-test {
decorator: linear-gradient(to right, #000000, #ffffff);
}

View File

@@ -1,12 +1,17 @@
<rml>
<head>
<title>RmlUi Raylib</title>
<link type="text/rcss" href="main.rcss" />
</head>
<body>
<h1>Hello from RmlUi</h1>
<div style="position: relative; width: 300px; height: 200px; overflow: hidden; border-radius: 40px; background-color: #0000ff">
<div style="position: absolute; top: -20px; left: -20px; width: 150px; height: 150px; background-color: #ff0000; transform: rotate(15deg);"></div>
<div style="position: relative; width: 300px; height: 200px; overflow: hidden; border-radius: 40px;"
class="gradient-test">
<div
style="position: absolute; top: -20px; left: -20px; width: 150px; height: 150px; background-color: #ff0000; transform: rotate(15deg);">
</div>
</div>
</body>
</rml>

262
assets/tests.rml Normal file
View File

@@ -0,0 +1,262 @@
<rml>
<head>
<title>RmlUi Raylib Render Interface — Feature Tests</title>
<style>
body {
width: 100vw;
height: 100vh;
overflow-y: auto;
background-color: #101010aa;
color: white;
font-size: 14px;
font-family: "rmlui-debugger-font";
}
.row {
display: block;
margin: 10px;
}
.label {
display: block;
margin-bottom: 4px;
}
.box {
display: inline-block;
width: 140px;
height: 100px;
margin-right: 12px;
vertical-align: top;
}
/* Basic rendering */
.basic-solid {
background-color: #3060ff;
}
.basic-border {
background-color: #202020;
border: 4px #ffcc00;
}
/* Transforms (2D and 3D) */
.transform-rotate {
background-color: #ff5050;
transform: rotate(20deg);
}
.transform-scale {
background-color: #50ff90;
transform: scale(1.3, 0.7);
}
.transform-skew {
background-color: #ffaa00;
transform: skew(15deg, 0deg);
}
.transform-3d {
background-color: #a060ff;
transform: perspective(400px) rotateY(45deg);
}
/* Clip mask */
.clip-parent {
position: relative;
background-color: #2050a0;
border-radius: 30px;
overflow: hidden;
}
.clip-parent-forced {
position: relative;
background-color: #2050a0;
border-radius: 30px;
overflow: hidden;
clip: always;
}
.clip-child-overflowing {
position: absolute;
top: -15px;
left: -15px;
width: 90px;
height: 90px;
background-color: #ff4040;
}
.clip-child-transform-only {
position: absolute;
top: 20px;
left: 20px;
width: 60px;
height: 60px;
background-color: #ff4040;
transform: rotate(30deg) scale(1.6);
}
/* Shaders: linear-gradient */
.gradient-linear {
decorator: linear-gradient(to right, #000000, #ffffff);
}
.gradient-linear-angled {
decorator: linear-gradient(45deg, red 20%, white, blue 80%);
}
.gradient-linear-repeating {
decorator: repeating-linear-gradient(to right, red, yellow 20px, red 40px);
}
/* Shaders: radial-gradient */
.gradient-radial {
decorator: radial-gradient(circle farthest-side at center, #ff6b6b, #fec84d, #4ecdc4);
}
.gradient-radial-ellipse {
decorator: radial-gradient(60px 30px at center, red, yellow, green);
}
/* Shaders: conic-gradient */
.gradient-conic {
decorator: conic-gradient(from 0deg at center, red, yellow, lime, aqua, blue, red);
}
.gradient-conic-pie {
decorator: conic-gradient(#9acd32 40%, #ffd700 0 75%, #f06 0);
}
/* Filters — not yet implemented */
.filter-blur {
background-color: #3060ff;
filter: blur(4px);
}
.filter-brightness {
background-color: #3060ff;
filter: brightness(1.8);
}
.filter-dropshadow {
background-color: #3060ff;
filter: drop-shadow(#000 6px 6px 2px);
}
.filter-grayscale {
decorator: linear-gradient(to right, red, blue);
filter: grayscale(1);
}
/* Backdrop-filter — not yet implemented */
.backdrop-behind {
position: relative;
background-color: #ff8800;
}
.backdrop-blur {
position: absolute;
top: 10px;
left: 10px;
width: 80px;
height: 60px;
backdrop-filter: blur(6px);
background-color: #ffffff40;
}
/* Box shadow — not yet implemented */
.box-shadow-basic {
background-color: #303030;
box-shadow: #000a 0 4px 12px;
}
.box-shadow-inset {
background-color: #303030;
box-shadow: #000a 0 0 10px inset;
}
/* Mask image — not yet implemented */
.mask-basic {
background-color: #40c0ff;
mask-image: radial-gradient(circle at center, white, transparent);
}
</style>
</head>
<body>
<div class="row">
<div class="label">Basic rendering</div>
<div class="box basic-solid"></div>
<div class="box basic-border"></div>
</div>
<div class="row">
<div class="label">Transforms</div>
<div class="box transform-rotate"></div>
<div class="box transform-scale"></div>
<div class="box transform-skew"></div>
<div class="box transform-3d"></div>
</div>
<div class="row">
<div class="label">Clip mask</div>
<div class="box clip-parent">
<div class="clip-child-overflowing"></div>
</div>
<div class="box clip-parent">
<div class="clip-child-transform-only"></div>
</div>
<div class="box clip-parent-forced">
<div class="clip-child-transform-only"></div>
</div>
</div>
<div class="row">
<div class="label">Shaders: linear-gradient</div>
<div class="box gradient-linear"></div>
<div class="box gradient-linear-angled"></div>
<div class="box gradient-linear-repeating"></div>
</div>
<div class="row">
<div class="label">Shaders: radial-gradient</div>
<div class="box gradient-radial"></div>
<div class="box gradient-radial-ellipse"></div>
</div>
<div class="row">
<div class="label">Shaders: conic-gradient</div>
<div class="box gradient-conic"></div>
<div class="box gradient-conic-pie"></div>
</div>
<div class="row">
<div class="label">Filters (not yet implemented, expect no effect)</div>
<div class="box filter-blur"></div>
<div class="box filter-brightness"></div>
<div class="box filter-dropshadow"></div>
<div class="box filter-grayscale"></div>
</div>
<div class="row">
<div class="label">Backdrop-filter (not yet implemented, expect no effect)</div>
<div class="box backdrop-behind">
<div class="backdrop-blur"></div>
</div>
</div>
<div class="row">
<div class="label">Box shadow (not yet implemented, expect no effect)</div>
<div class="box box-shadow-basic"></div>
<div class="box box-shadow-inset"></div>
</div>
<div class="row">
<div class="label">Mask image (not yet implemented, expect no effect)</div>
<div class="box mask-basic"></div>
</div>
</body>
</rml>

View File

@@ -8,12 +8,23 @@ I originally began creating this for a Game Jam project where I got the basic re
*Rendering features (from the RmlUi render interfface documentation https://mikke89.github.io/RmlUiDoc/pages/cpp_manual/interfaces/render.html#feature-table):*
- [x] Basic rendering
- [x] Clip Mask
- [x] Transforms
- [-] Transforms
- transform-only overflow (no explicit `clip: always`) may not be clipping correctly
- 3D transforms (e.g. `perspective()` + `rotateY()`) do not render
- [ ] Layers
- [ ] Render textures
- [ ] Mask images
- [ ] Filters
- [ ] Shaders
- [ ] Generic
- [x] Gradients
- [x] Linear
- [x] Radial
- [x] Conic
*Backend features:*
- [x] Input handling
*Issue tracker:*

View File

@@ -3,20 +3,125 @@
#include <raylib.h>
#include <rlgl.h>
#include <RmlUi/Core.h>
#include <RmlUi/Core/Dictionary.h>
#include <GL/gl.h>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
namespace {
// ---- Basic rendering: geometry ----
struct GeometryData {
std::vector<Rml::Vertex> vertices;
std::vector<int> indices;
};
// ---- Shaders: gradient data ----
enum class GradientType { Linear, Radial, Conic };
struct GradientStopData {
static constexpr int MAX_STOPS = 16;
GradientType type;
int stop_count = 0;
Rml::ColourbPremultiplied colors[MAX_STOPS];
float positions[MAX_STOPS];
};
struct LinearGradientData : GradientStopData {
LinearGradientData() { type = GradientType::Linear; }
Rml::Vector2f p0;
Rml::Vector2f p1;
float length = 1.0f;
bool repeating = false;
};
struct RadialGradientData : GradientStopData {
RadialGradientData() { type = GradientType::Radial; }
Rml::Vector2f center;
Rml::Vector2f radius;
bool repeating = false;
};
struct ConicGradientData : GradientStopData {
ConicGradientData() { type = GradientType::Conic; }
Rml::Vector2f center;
float angle = 0.0f;
bool repeating = false;
};
void FillGradientStops(GradientStopData &out, const Rml::ColorStopList &stops) {
out.stop_count = std::min((int)stops.size(), GradientStopData::MAX_STOPS);
for (int i = 0; i < out.stop_count; ++i) {
out.colors[i] = stops[i].color;
out.positions[i] = stops[i].position.number;
}
}
void UploadGradientStops(Shader &shader, int locStopCount, int locStopColors,
int locStopPositions, const GradientStopData &data) {
float colorsF[GradientStopData::MAX_STOPS * 4];
float positionsF[GradientStopData::MAX_STOPS];
for (int i = 0; i < data.stop_count; ++i) {
colorsF[i * 4 + 0] = data.colors[i].red / 255.0f;
colorsF[i * 4 + 1] = data.colors[i].green / 255.0f;
colorsF[i * 4 + 2] = data.colors[i].blue / 255.0f;
colorsF[i * 4 + 3] = data.colors[i].alpha / 255.0f;
positionsF[i] = data.positions[i];
}
SetShaderValue(shader, locStopCount, &data.stop_count, SHADER_UNIFORM_INT);
SetShaderValueV(shader, locStopColors, colorsF, SHADER_UNIFORM_VEC4,
data.stop_count);
SetShaderValueV(shader, locStopPositions, positionsF, SHADER_UNIFORM_FLOAT,
data.stop_count);
}
const char *kGradientVertexShader =
"#version 330\n"
"in vec3 vertexPosition;\n"
"in vec2 vertexTexCoord;\n"
"in vec4 vertexColor;\n"
"out vec2 fragTexCoord;\n"
"uniform mat4 mvp;\n"
"void main() {\n"
" fragTexCoord = vertexTexCoord;\n"
" gl_Position = mvp * vec4(vertexPosition, 1.0);\n"
"}\n";
const char *kGradientSampling =
"uniform int u_stopCount;\n"
"uniform vec4 u_stopColors[16];\n"
"uniform float u_stopPositions[16];\n"
"uniform int u_repeating;\n"
"vec4 SampleGradient(float t) {\n"
" if (u_repeating != 0) {\n"
" t = fract(t);\n"
" if (t < 0.0) t += 1.0;\n"
" } else {\n"
" t = clamp(t, 0.0, 1.0);\n"
" }\n"
" vec4 color = u_stopColors[0];\n"
" for (int i = 0; i < u_stopCount - 1; ++i) {\n"
" if (t >= u_stopPositions[i] && t <= u_stopPositions[i + 1]) {\n"
" float span = max(u_stopPositions[i + 1] - u_stopPositions[i], "
"0.0001);\n"
" float localT = (t - u_stopPositions[i]) / span;\n"
" color = mix(u_stopColors[i], u_stopColors[i + 1], localT);\n"
" }\n"
" }\n"
" return color;\n"
"}\n";
} // namespace
// ---- Basic rendering: geometry ----
Rml::CompiledGeometryHandle
RaylibRenderInterface::CompileGeometry(Rml::Span<const Rml::Vertex> vertices,
Rml::Span<const int> indices) {
@@ -65,6 +170,8 @@ void RaylibRenderInterface::ReleaseGeometry(
delete reinterpret_cast<GeometryData *>(handle);
}
// ---- Basic rendering: textures ----
Rml::TextureHandle
RaylibRenderInterface::LoadTexture(Rml::Vector2i &texture_dimensions,
const Rml::String &source) {
@@ -91,7 +198,7 @@ RaylibRenderInterface::GenerateTexture(Rml::Span<const Rml::byte> source_data,
std::memcpy(img.data, source_data.data(), byte_count);
Texture2D tex = LoadTextureFromImage(img);
UnloadImage(img); // frees img.data
UnloadImage(img);
return reinterpret_cast<Rml::TextureHandle>(new Texture2D(tex));
}
@@ -102,6 +209,8 @@ void RaylibRenderInterface::ReleaseTexture(Rml::TextureHandle handle) {
delete tex;
}
// ---- Basic rendering: scissor region ----
void RaylibRenderInterface::EnableScissorRegion(bool enable) {
scissor_enabled = enable;
if (enable)
@@ -121,6 +230,8 @@ void RaylibRenderInterface::ApplyScissor() {
scissor_region.Width(), scissor_region.Height());
}
// ---- Transforms ----
void RaylibRenderInterface::SetTransform(const Rml::Matrix4f *transform) {
if (transform) {
has_transform = true;
@@ -130,8 +241,10 @@ void RaylibRenderInterface::SetTransform(const Rml::Matrix4f *transform) {
}
}
void RaylibRenderInterface::DrawGeometryTriangles(Rml::CompiledGeometryHandle handle,
Rml::Vector2f translation) {
// ---- Clip mask ----
void RaylibRenderInterface::DrawGeometryTriangles(
Rml::CompiledGeometryHandle handle, Rml::Vector2f translation) {
auto *geo = reinterpret_cast<GeometryData *>(handle);
rlPushMatrix();
@@ -165,8 +278,8 @@ void RaylibRenderInterface::EnableClipMask(bool enable) {
}
}
void RaylibRenderInterface::RenderToClipMask(Rml::ClipMaskOperation operation,
Rml::CompiledGeometryHandle geometry,
void RaylibRenderInterface::RenderToClipMask(
Rml::ClipMaskOperation operation, Rml::CompiledGeometryHandle geometry,
Rml::Vector2f translation) {
rlDrawRenderBatchActive();
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
@@ -203,3 +316,254 @@ void RaylibRenderInterface::RenderToClipMask(Rml::ClipMaskOperation operation,
glStencilFunc(GL_EQUAL, static_cast<GLint>(clip_mask_level), 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
}
// ---- Shaders ----
void RaylibRenderInterface::EnsureLinearGradientShaderLoaded() {
if (linear_program.loaded)
return;
std::string fs =
std::string("#version 330\n"
"in vec2 fragTexCoord;\n"
"out vec4 finalColor;\n"
"uniform vec2 u_p0;\n"
"uniform vec2 u_p1;\n"
"uniform float u_length;\n") +
kGradientSampling +
"void main() {\n"
" vec2 dir = u_p1 - u_p0;\n"
" float len = max(u_length, 0.0001);\n"
" float t = dot(fragTexCoord - u_p0, normalize(dir)) / len;\n"
" finalColor = SampleGradient(t);\n"
"}\n";
linear_program.shader =
LoadShaderFromMemory(kGradientVertexShader, fs.c_str());
linear_program.loc_p0 = GetShaderLocation(linear_program.shader, "u_p0");
linear_program.loc_p1 = GetShaderLocation(linear_program.shader, "u_p1");
linear_program.loc_length =
GetShaderLocation(linear_program.shader, "u_length");
linear_program.loc_repeating =
GetShaderLocation(linear_program.shader, "u_repeating");
linear_program.loc_stopCount =
GetShaderLocation(linear_program.shader, "u_stopCount");
linear_program.loc_stopColors =
GetShaderLocation(linear_program.shader, "u_stopColors");
linear_program.loc_stopPositions =
GetShaderLocation(linear_program.shader, "u_stopPositions");
linear_program.loaded = true;
}
void RaylibRenderInterface::EnsureRadialGradientShaderLoaded() {
if (radial_program.loaded)
return;
std::string fs = std::string("#version 330\n"
"in vec2 fragTexCoord;\n"
"out vec4 finalColor;\n"
"uniform vec2 u_center;\n"
"uniform vec2 u_radius;\n") +
kGradientSampling +
"void main() {\n"
" vec2 offset = (fragTexCoord - u_center) / "
"max(u_radius, vec2(0.0001));\n"
" float t = length(offset);\n"
" finalColor = SampleGradient(t);\n"
"}\n";
radial_program.shader =
LoadShaderFromMemory(kGradientVertexShader, fs.c_str());
radial_program.loc_center =
GetShaderLocation(radial_program.shader, "u_center");
radial_program.loc_radius =
GetShaderLocation(radial_program.shader, "u_radius");
radial_program.loc_repeating =
GetShaderLocation(radial_program.shader, "u_repeating");
radial_program.loc_stopCount =
GetShaderLocation(radial_program.shader, "u_stopCount");
radial_program.loc_stopColors =
GetShaderLocation(radial_program.shader, "u_stopColors");
radial_program.loc_stopPositions =
GetShaderLocation(radial_program.shader, "u_stopPositions");
radial_program.loaded = true;
}
void RaylibRenderInterface::EnsureConicGradientShaderLoaded() {
if (conic_program.loaded)
return;
std::string fs = std::string("#version 330\n"
"in vec2 fragTexCoord;\n"
"out vec4 finalColor;\n"
"uniform vec2 u_center;\n"
"uniform float u_angle;\n") +
kGradientSampling +
"void main() {\n"
" vec2 offset = fragTexCoord - u_center;\n"
" float theta = atan(offset.y, offset.x) - u_angle + "
"1.5707963267948966;\n"
" float t = theta / 6.283185307179586;\n"
" t = t - floor(t);\n"
" finalColor = SampleGradient(t);\n"
"}\n";
conic_program.shader =
LoadShaderFromMemory(kGradientVertexShader, fs.c_str());
conic_program.loc_center =
GetShaderLocation(conic_program.shader, "u_center");
conic_program.loc_angle = GetShaderLocation(conic_program.shader, "u_angle");
conic_program.loc_repeating =
GetShaderLocation(conic_program.shader, "u_repeating");
conic_program.loc_stopCount =
GetShaderLocation(conic_program.shader, "u_stopCount");
conic_program.loc_stopColors =
GetShaderLocation(conic_program.shader, "u_stopColors");
conic_program.loc_stopPositions =
GetShaderLocation(conic_program.shader, "u_stopPositions");
conic_program.loaded = true;
}
Rml::CompiledShaderHandle
RaylibRenderInterface::CompileShader(const Rml::String &name,
const Rml::Dictionary &parameters) {
if (name == "linear-gradient") {
EnsureLinearGradientShaderLoaded();
auto *data = new LinearGradientData();
data->p0 = Rml::Get(parameters, "p0", Rml::Vector2f(0, 0));
data->p1 = Rml::Get(parameters, "p1", Rml::Vector2f(0, 0));
data->length = Rml::Get(parameters, "length", 1.0f);
data->repeating = Rml::Get(parameters, "repeating", false);
FillGradientStops(
*data, Rml::Get(parameters, "color_stop_list", Rml::ColorStopList()));
return reinterpret_cast<Rml::CompiledShaderHandle>(data);
}
if (name == "radial-gradient") {
EnsureRadialGradientShaderLoaded();
auto *data = new RadialGradientData();
data->center = Rml::Get(parameters, "center", Rml::Vector2f(0, 0));
data->radius = Rml::Get(parameters, "radius", Rml::Vector2f(1, 1));
data->repeating = Rml::Get(parameters, "repeating", false);
FillGradientStops(
*data, Rml::Get(parameters, "color_stop_list", Rml::ColorStopList()));
return reinterpret_cast<Rml::CompiledShaderHandle>(data);
}
if (name == "conic-gradient") {
EnsureConicGradientShaderLoaded();
auto *data = new ConicGradientData();
data->center = Rml::Get(parameters, "center", Rml::Vector2f(0, 0));
data->angle = Rml::Get(parameters, "angle", 0.0f);
data->repeating = Rml::Get(parameters, "repeating", false);
FillGradientStops(
*data, Rml::Get(parameters, "color_stop_list", Rml::ColorStopList()));
return reinterpret_cast<Rml::CompiledShaderHandle>(data);
}
TraceLog(LOG_WARNING, "[RmlUi] shader type '%s' not implemented",
name.c_str());
return 0;
}
void RaylibRenderInterface::RenderShader(Rml::CompiledShaderHandle shader,
Rml::CompiledGeometryHandle handle,
Rml::Vector2f translation,
Rml::TextureHandle texture) {
auto *base = reinterpret_cast<GradientStopData *>(shader);
auto *geo = reinterpret_cast<GeometryData *>(handle);
rlDrawRenderBatchActive();
switch (base->type) {
case GradientType::Linear: {
auto *data = static_cast<LinearGradientData *>(base);
BeginShaderMode(linear_program.shader);
float p0[2] = {data->p0.x, data->p0.y};
float p1[2] = {data->p1.x, data->p1.y};
int repeatingInt = data->repeating ? 1 : 0;
SetShaderValue(linear_program.shader, linear_program.loc_p0, p0,
SHADER_UNIFORM_VEC2);
SetShaderValue(linear_program.shader, linear_program.loc_p1, p1,
SHADER_UNIFORM_VEC2);
SetShaderValue(linear_program.shader, linear_program.loc_length,
&data->length, SHADER_UNIFORM_FLOAT);
SetShaderValue(linear_program.shader, linear_program.loc_repeating,
&repeatingInt, SHADER_UNIFORM_INT);
UploadGradientStops(linear_program.shader, linear_program.loc_stopCount,
linear_program.loc_stopColors,
linear_program.loc_stopPositions, *data);
break;
}
case GradientType::Radial: {
auto *data = static_cast<RadialGradientData *>(base);
BeginShaderMode(radial_program.shader);
float center[2] = {data->center.x, data->center.y};
float radius[2] = {data->radius.x, data->radius.y};
int repeatingInt = data->repeating ? 1 : 0;
SetShaderValue(radial_program.shader, radial_program.loc_center, center,
SHADER_UNIFORM_VEC2);
SetShaderValue(radial_program.shader, radial_program.loc_radius, radius,
SHADER_UNIFORM_VEC2);
SetShaderValue(radial_program.shader, radial_program.loc_repeating,
&repeatingInt, SHADER_UNIFORM_INT);
UploadGradientStops(radial_program.shader, radial_program.loc_stopCount,
radial_program.loc_stopColors,
radial_program.loc_stopPositions, *data);
break;
}
case GradientType::Conic: {
auto *data = static_cast<ConicGradientData *>(base);
BeginShaderMode(conic_program.shader);
float center[2] = {data->center.x, data->center.y};
int repeatingInt = data->repeating ? 1 : 0;
SetShaderValue(conic_program.shader, conic_program.loc_center, center,
SHADER_UNIFORM_VEC2);
SetShaderValue(conic_program.shader, conic_program.loc_angle, &data->angle,
SHADER_UNIFORM_FLOAT);
SetShaderValue(conic_program.shader, conic_program.loc_repeating,
&repeatingInt, SHADER_UNIFORM_INT);
UploadGradientStops(conic_program.shader, conic_program.loc_stopCount,
conic_program.loc_stopColors,
conic_program.loc_stopPositions, *data);
break;
}
}
rlPushMatrix();
rlTranslatef(translation.x, translation.y, 0.0f);
if (has_transform) {
rlMultMatrixf(current_transform.data());
}
rlSetTexture(rlGetTextureIdDefault());
rlBegin(RL_TRIANGLES);
for (size_t i = 0; i + 2 < geo->indices.size(); i += 3) {
for (int j = 0; j < 3; ++j) {
const Rml::Vertex &v = geo->vertices[geo->indices[i + j]];
rlTexCoord2f(v.tex_coord.x, v.tex_coord.y);
rlVertex2f(v.position.x, v.position.y);
}
}
rlEnd();
rlPopMatrix();
rlSetTexture(0);
rlDrawRenderBatchActive();
EndShaderMode();
}
void RaylibRenderInterface::ReleaseShader(Rml::CompiledShaderHandle shader) {
auto *base = reinterpret_cast<GradientStopData *>(shader);
switch (base->type) {
case GradientType::Linear:
delete static_cast<LinearGradientData *>(base);
break;
case GradientType::Radial:
delete static_cast<RadialGradientData *>(base);
break;
case GradientType::Conic:
delete static_cast<ConicGradientData *>(base);
break;
}
}

View File

@@ -1,52 +1,86 @@
#pragma once
#include <RmlUi/Core/RenderInterface.h>
// NOTE: RmlUi's RenderInterface signature has shifted across versions
// (raw pointer+count vs. Rml::Span<>). If this fails to compile against
// a different RmlUi commit, check RmlUi/Core/RenderInterface.h and adjust
// CompileGeometry/GenerateTexture's parameter types, internal logic
// stays the same.
#include <raylib.h>
class RaylibRenderInterface : public Rml::RenderInterface {
public:
// ---- Basic rendering: geometry ----
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;
// ---- Basic rendering: textures ----
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 handle) override;
// ---- Basic rendering: scissor region ----
void EnableScissorRegion(bool enable) override;
void SetScissorRegion(Rml::Rectanglei region) override;
// ---- Transforms ----
void SetTransform(const Rml::Matrix4f *transform) override;
// ---- Clip mask ----
void EnableClipMask(bool enable) override;
void RenderToClipMask(Rml::ClipMaskOperation operation,
Rml::CompiledGeometryHandle geometry,
Rml::Vector2f translation) override;
private:
void ApplyScissor();
// ---- Shaders ----
Rml::CompiledShaderHandle
CompileShader(const Rml::String &name,
const Rml::Dictionary &parameters) override;
void RenderShader(Rml::CompiledShaderHandle shader,
Rml::CompiledGeometryHandle geometry,
Rml::Vector2f translation,
Rml::TextureHandle texture) override;
void ReleaseShader(Rml::CompiledShaderHandle shader) override;
private:
// ---- Basic rendering: scissor region ----
void ApplyScissor();
bool scissor_enabled = false;
Rml::Rectanglei scissor_region;
// ---- Transforms ----
bool has_transform = false;
Rml::Matrix4f current_transform;
// ---- Clip mask ----
void DrawGeometryTriangles(Rml::CompiledGeometryHandle geometry,
Rml::Vector2f translation);
bool clip_mask_enabled = false;
unsigned int clip_mask_level = 0;
void DrawGeometryTriangles(Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation);
// ---- Shaders: gradient shader programs ----
struct GradientShaderProgram {
Shader shader{};
bool loaded = false;
int loc_stopCount = -1, loc_stopColors = -1, loc_stopPositions = -1,
loc_repeating = -1;
};
struct LinearShaderProgram : GradientShaderProgram {
int loc_p0 = -1, loc_p1 = -1, loc_length = -1;
};
struct RadialShaderProgram : GradientShaderProgram {
int loc_center = -1, loc_radius = -1;
};
struct ConicShaderProgram : GradientShaderProgram {
int loc_center = -1, loc_angle = -1;
};
void EnsureLinearGradientShaderLoaded();
void EnsureRadialGradientShaderLoaded();
void EnsureConicGradientShaderLoaded();
LinearShaderProgram linear_program;
RadialShaderProgram radial_program;
ConicShaderProgram conic_program;
};

View File

@@ -1,14 +1,14 @@
#include <RmlUi/Core.h>
#include <RmlUi/Core/Context.h>
#include <RmlUi/Core/Core.h>
#include <RmlUi/Core/Math.h>
#include <RmlUi/Debugger.h>
#include <RmlUi/Debugger/Debugger.h>
#include <raylib.h>
#include <RmlUi/Core.h>
#include <RmlUi/Debugger.h>
#include <RmlUi/Core/Context.h>
#include "backend/RaylibInput.h"
#include "backend/RaylibRenderInterface.h"
#include "backend/RaylibSystemInterface.h"
#include "backend/RaylibInput.h"
#include <GL/gl.h>
@@ -16,7 +16,6 @@ static RaylibSystemInterface system_interface;
static RaylibRenderInterface render_interface;
static Rml::Context *ctx;
int main() {
SetConfigFlags(FLAG_VSYNC_HINT | FLAG_WINDOW_RESIZABLE);
@@ -31,7 +30,7 @@ int main() {
ctx = Rml::CreateContext("main", Rml::Vector2i(600, 800));
Rml::Debugger::Initialise(ctx);
auto doc = ctx->LoadDocument("assets/main.rml");
auto doc = ctx->LoadDocument("assets/tests.rml");
doc->Show();
Camera3D cam3;
@@ -56,12 +55,14 @@ int main() {
ForwardInputToRml(ctx);
ctx->Update();
if (IsKeyPressed(KEY_Z)) cam3.target = Vector3 {0.0f, 0.0f, 0.0f};
if (IsKeyPressed(KEY_Z))
cam3.target = Vector3{0.0f, 0.0f, 0.0f};
BeginDrawing();
ClearBackground(BLACK);
DrawFPS(10, 10);
// DrawText("Hello!", GetScreenWidth()/2, GetScreenHeight()/2, 10, RAYWHITE);
// DrawText("Hello!", GetScreenWidth()/2, GetScreenHeight()/2, 10,
// RAYWHITE);
BeginMode3D(cam3);
DrawCube(cubePos, 2.0f, 2.0f, 2.0f, RED);