diff --git a/assets/main.rcss b/assets/main.rcss index 2c98cea..94daaff 100644 --- a/assets/main.rcss +++ b/assets/main.rcss @@ -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); +} \ No newline at end of file diff --git a/assets/main.rml b/assets/main.rml index 88b98a9..44c5674 100644 --- a/assets/main.rml +++ b/assets/main.rml @@ -1,12 +1,17 @@ + RmlUi Raylib +

Hello from RmlUi

-
-
+
+
+
- + \ No newline at end of file diff --git a/assets/tests.rml b/assets/tests.rml new file mode 100644 index 0000000..7d44254 --- /dev/null +++ b/assets/tests.rml @@ -0,0 +1,262 @@ + + + + RmlUi Raylib Render Interface — Feature Tests + + + + + +
+
Basic rendering
+
+
+
+ +
+
Transforms
+
+
+
+
+
+ +
+
Clip mask
+
+
+
+
+
+
+
+
+
+
+ +
+
Shaders: linear-gradient
+
+
+
+
+ +
+
Shaders: radial-gradient
+
+
+
+ +
+
Shaders: conic-gradient
+
+
+
+ +
+
Filters (not yet implemented, expect no effect)
+
+
+
+
+
+ +
+
Backdrop-filter (not yet implemented, expect no effect)
+
+
+
+
+ +
+
Box shadow (not yet implemented, expect no effect)
+
+
+
+ +
+
Mask image (not yet implemented, expect no effect)
+
+
+ + +
\ No newline at end of file diff --git a/readme.md b/readme.md index 7abf2c2..b4721e1 100644 --- a/readme.md +++ b/readme.md @@ -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:* + \ No newline at end of file diff --git a/src/backend/RaylibRenderInterface.cpp b/src/backend/RaylibRenderInterface.cpp index ea908b0..6cc8475 100644 --- a/src/backend/RaylibRenderInterface.cpp +++ b/src/backend/RaylibRenderInterface.cpp @@ -3,20 +3,125 @@ #include #include +#include +#include + #include +#include #include +#include #include namespace { +// ---- Basic rendering: geometry ---- + struct GeometryData { std::vector vertices; std::vector 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 vertices, Rml::Span indices) { @@ -65,6 +170,8 @@ void RaylibRenderInterface::ReleaseGeometry( delete reinterpret_cast(handle); } +// ---- Basic rendering: textures ---- + Rml::TextureHandle RaylibRenderInterface::LoadTexture(Rml::Vector2i &texture_dimensions, const Rml::String &source) { @@ -91,7 +198,7 @@ RaylibRenderInterface::GenerateTexture(Rml::Span 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(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(handle); rlPushMatrix(); @@ -165,34 +278,34 @@ void RaylibRenderInterface::EnableClipMask(bool enable) { } } -void RaylibRenderInterface::RenderToClipMask(Rml::ClipMaskOperation operation, - Rml::CompiledGeometryHandle geometry, - Rml::Vector2f translation) { +void RaylibRenderInterface::RenderToClipMask( + Rml::ClipMaskOperation operation, Rml::CompiledGeometryHandle geometry, + Rml::Vector2f translation) { rlDrawRenderBatchActive(); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); switch (operation) { - case Rml::ClipMaskOperation::Set: - glClearStencil(0); - glClear(GL_STENCIL_BUFFER_BIT); - glStencilFunc(GL_ALWAYS, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - clip_mask_level = 1; - break; + case Rml::ClipMaskOperation::Set: + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + clip_mask_level = 1; + break; - case Rml::ClipMaskOperation::SetInverse: - glClearStencil(1); - glClear(GL_STENCIL_BUFFER_BIT); - glStencilFunc(GL_ALWAYS, 0, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - clip_mask_level = 1; - break; + case Rml::ClipMaskOperation::SetInverse: + glClearStencil(1); + glClear(GL_STENCIL_BUFFER_BIT); + glStencilFunc(GL_ALWAYS, 0, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + clip_mask_level = 1; + break; - case Rml::ClipMaskOperation::Intersect: - glStencilFunc(GL_EQUAL, static_cast(clip_mask_level), 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); - clip_mask_level += 1; - break; + case Rml::ClipMaskOperation::Intersect: + glStencilFunc(GL_EQUAL, static_cast(clip_mask_level), 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); + clip_mask_level += 1; + break; } DrawGeometryTriangles(geometry, translation); @@ -203,3 +316,254 @@ void RaylibRenderInterface::RenderToClipMask(Rml::ClipMaskOperation operation, glStencilFunc(GL_EQUAL, static_cast(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 ¶meters) { + 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(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(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(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(shader); + auto *geo = reinterpret_cast(handle); + + rlDrawRenderBatchActive(); + + switch (base->type) { + case GradientType::Linear: { + auto *data = static_cast(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(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(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(shader); + switch (base->type) { + case GradientType::Linear: + delete static_cast(base); + break; + case GradientType::Radial: + delete static_cast(base); + break; + case GradientType::Conic: + delete static_cast(base); + break; + } +} \ No newline at end of file diff --git a/src/backend/RaylibRenderInterface.h b/src/backend/RaylibRenderInterface.h index bfefe7b..52429ef 100644 --- a/src/backend/RaylibRenderInterface.h +++ b/src/backend/RaylibRenderInterface.h @@ -1,52 +1,86 @@ #pragma once #include - -// 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 class RaylibRenderInterface : public Rml::RenderInterface { public: + // ---- Basic rendering: geometry ---- Rml::CompiledGeometryHandle CompileGeometry(Rml::Span vertices, Rml::Span 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 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 ¶meters) 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; +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 0f261a1..edb5025 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,14 +1,14 @@ +#include +#include #include #include +#include #include #include -#include -#include -#include +#include "backend/RaylibInput.h" #include "backend/RaylibRenderInterface.h" #include "backend/RaylibSystemInterface.h" -#include "backend/RaylibInput.h" #include @@ -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,20 +30,20 @@ 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; - cam3.position = Vector3 {0.0f, 10.0f, 10.0f}; - cam3.target = Vector3 {0.0f, 0.0f, 0.0f}; - cam3.up = Vector3 {0, 1.0f, 0.0f}; + cam3.position = Vector3{0.0f, 10.0f, 10.0f}; + cam3.target = Vector3{0.0f, 0.0f, 0.0f}; + cam3.up = Vector3{0, 1.0f, 0.0f}; cam3.fovy = 45.0f; cam3.projection = CAMERA_PERSPECTIVE; - Vector3 cubePos = Vector3 {0.0f, 0.0f, 0.0f}; + Vector3 cubePos = Vector3{0.0f, 0.0f, 0.0f}; // DisableCursor(); - + while (!WindowShouldClose()) { if (IsWindowResized()) { @@ -55,20 +54,22 @@ 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); + 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); - DrawCubeWires(cubePos, 2.0f, 2.0f, 2.0f, BLUE); - DrawGrid(10, 1.0f); + DrawCube(cubePos, 2.0f, 2.0f, 2.0f, RED); + DrawCubeWires(cubePos, 2.0f, 2.0f, 2.0f, BLUE); + DrawGrid(10, 1.0f); EndMode3D(); - + ctx->Render(); EndDrawing(); }