getting text rendering working with stb_truetype

This commit is contained in:
2026-02-21 12:47:32 +01:00
parent 2165ac748d
commit 06f784d0b1
15 changed files with 637 additions and 237 deletions

BIN
assets/fonts/KodeMono.ttf Normal file

Binary file not shown.

View File

@@ -0,0 +1,11 @@
#version 330 core
out vec4 pixel_color;
in vec4 frag_color;
in vec2 frag_uv_position;
uniform sampler2D glyph_atlas;
void main() {
pixel_color = vec4(1,1,1,texture(glyph_atlas, frag_uv_position).r);
};

View File

@@ -0,0 +1,73 @@
#version 330 core
layout (location = 0) in vec2 begin;
layout (location = 1) in int glyph;
layout (location = 2) in float lineHeight;
layout (location = 3) in vec4 color;
/*
typedef struct GlyphMeta GlyphMeta;
struct GlyphMeta {
// chunk 1
RLVector2 uv0;
RLVector2 uv1;
// chunk 2
real32 xOffset;
real32 yOffset;
real32 width;
real32 height;
};
*/
uniform samplerBuffer glyph_table;
uniform mat4 projection;
uniform sampler2D font;
out vec4 frag_color;
out vec2 frag_uv_position;
const vec2 rectangle_vertices[4] = vec2[](
vec2(-1, -1), // bl
vec2(-1, 1), // tl
vec2( 1, -1), // br
vec2( 1, 1) // tr
);
const vec2 uv0_vertices[4] = vec2[](
vec2(1, 0), // bl
vec2(1, 1), // tl
vec2(0, 0), // br
vec2(0, 1) // tr
);
const vec2 uv1_vertices[4] = vec2[](
vec2(0, 1),
vec2(0, 0),
vec2(1, 1),
vec2(1, 0)
);
void main() {
vec4 chunk1 = texelFetch(glyph_table, glyph * 2 + 0);
vec4 chunk2 = texelFetch(glyph_table, glyph * 2 + 1);
vec2 uv0 = chunk1.xy;
vec2 uv1 = chunk1.zw;
vec2 offset = chunk2.xy;
vec2 dims = chunk2.zw;
vec2 p0 = begin + offset*lineHeight;
vec2 p1 = begin + (offset + dims)*lineHeight;
vec2 dest_half_size = (p1 - p0) / 2;
vec2 dest_center = (p1 + p0) / 2;
vec2 dest_position = rectangle_vertices[gl_VertexID] * dest_half_size + dest_center;
vec2 uv_position = uv0 * uv0_vertices[gl_VertexID] + uv1 * uv1_vertices[gl_VertexID];
gl_Position = projection * vec4(dest_position, 0, 1);
frag_color = color;
frag_uv_position = vec2(uv_position.x, uv_position.y);
}

2
build
View File

@@ -7,7 +7,7 @@ echo [Building target]
if [ $DEBUG ]; then
time clang -O0 -g -g2 $COMMON_FLAGS -DDJSTDLIB_DEBUG=1 ./src/main.c -o ./target/somaesque $LIB_INCLUDE
else
time clang -O3 $COMMON_FLAGS ./src/main.c -o ./target/somaesque $LIB_INCLUDE
time clang -O2 $COMMON_FLAGS ./src/main.c -o ./target/somaesque $LIB_INCLUDE
fi
echo [Target built]

12
src/common.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef COMMON_H
#define COMMON_H
#include "lib/djstdlib/core.h"
#include "lib/raymath.h"
DefineList(RLVector2, RLVec2);
DefineList(RLVector4, RLVec4);
DefineList(real32, Float);
DefineList(uint32, UInt32);
#endif

View File

@@ -83,6 +83,13 @@ void setUniform3fvByLoc(int uniformLocation, RLVector3 *vector) {
glUniform3fv(uniformLocation, 1, Vector3ToFloat(*vector));
}
void setUniform1i(Shader *s, const char *uniformName, int32 i) {
glUniform1i(glGetUniformLocation(s->progId, uniformName), (GLint)i);
}
void setUniform1iByLoc(int uniformLocation, int32 i) {
glUniform1i(uniformLocation, (GLint)i);
}
void setUniform2fv(Shader *s, const char *uniformName, RLVector2 *vector) {
glUniform2fv(glGetUniformLocation(s->progId, uniformName), 1, (const GLfloat *)vector);
}

View File

@@ -22,6 +22,9 @@ void setUniform3fvByLoc(int uniformLocation, RLVector3 *vector);
void setUniform2fv(Shader *s, const char *uniformName, RLVector2 *vector);
void setUniform2fvByLoc(int uniformLocation, RLVector2 *vector);
void setUniform1i(Shader *s, const char *uniformName, int32 i);
void setUniform1iByLoc(int uniformLocation, int32 i);
int getUniformLocation(Shader *s, const char *uniformName);
#endif

View File

@@ -1,9 +1,88 @@
#include "Texture.h"
#include "../lib/loaders/stb_image.h"
#include "../lib/glad/glad.h"
#include "../lib/djstdlib/core.h"
Texture createTexture(const char* source_path) {
Texture createTexture(const char* bitmap, int32 width, int32 height) {
Texture result = {0};
result.width = width;
result.height = height;
glGenTextures(1, &result.tex_id);
glBindTexture(GL_TEXTURE_2D, result.tex_id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, result.width, result.height, 0, GL_RGB, GL_UNSIGNED_BYTE, bitmap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
return result;
}
DefineList(stbtt_bakedchar, STBBakedChar);
Font createFont(Arena *arena, string ttfLocation, real32 lineHeight) {
const int CODEPOINT_START = 32;
const int CODEPOINT_END = 126;
int32 atlasWidth = 16 * lineHeight;
int32 atlasHeight = 6 * lineHeight;
STBBakedCharList bakedCharlist = PushFullListZero(arena, STBBakedCharList, CODEPOINT_END - CODEPOINT_START);
string fontFile = os_readEntireFile(arena, ttfLocation);
char *bakedFontBitmap = PushArrayZero(arena, char, atlasWidth*atlasHeight);
int32 bake_result = stbtt_BakeFontBitmap(
(unsigned char *)fontFile.str, 0,
lineHeight,
(unsigned char *)bakedFontBitmap, atlasWidth, atlasHeight,
CODEPOINT_START, CODEPOINT_END - CODEPOINT_START,
bakedCharlist.data);
GlyphMetaList glyphMeta = PushFullListZero(arena, GlyphMetaList, bakedCharlist.length);
real32 x, y;
real32 lastX, lastY;
for (EachIn(bakedCharlist, i)) {
stbtt_aligned_quad q;
stbtt_GetBakedQuad(bakedCharlist.data, atlasWidth, atlasHeight, i, &x, &y, &q, 1);
glyphMeta.data[i] = (GlyphMeta){
.uv0=(RLVector2){q.s0, q.t1},
.uv1=(RLVector2){q.s1, q.t0},
.xOffset=q.x0 - lastX,
.yOffset=q.y0 + lineHeight,
.width=q.x1-q.x0,
.height=q.y1-q.y0,
};
lastX = x;
lastY = y;
}
Font result = {0};
result.glyphMeta = glyphMeta;
result.lineHeight = lineHeight;
result.charWidth = bakedCharlist.data[0].xadvance;
glGenTextures(1, &result.texId);
glBindTexture(GL_TEXTURE_2D, result.texId);
//glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, atlasWidth, atlasHeight, 0, GL_RED, GL_UNSIGNED_BYTE, bakedFontBitmap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
glGenBuffers(1, &result.glyphTableBufId);
glBindBuffer(GL_TEXTURE_BUFFER, result.glyphTableBufId);
glBufferData(GL_TEXTURE_BUFFER, sizeof(result.glyphMeta.data[0])*result.glyphMeta.length, result.glyphMeta.data, GL_STATIC_DRAW);
glGenTextures(1, &result.glyphTableTexId);
glBindTexture(GL_TEXTURE_BUFFER, result.glyphTableTexId);
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, result.glyphTableBufId);
return result;
}
Texture createTextureFromFile(const char* source_path) {
Texture result = {0};
glGenTextures(1, &result.tex_id);
glBindTexture(GL_TEXTURE_2D, result.tex_id);

View File

@@ -1,12 +1,45 @@
#ifndef LEDDA_TEXTURE_H
#define LEDDA_TEXTURE_H
typedef struct {
unsigned int tex_id;
int width;
int height;
} Texture;
#include "../lib/djstdlib/core.h"
#include "../lib/loaders/stb_truetype.h"
#include "../lib/loaders/stb_image.h"
#include "../lib/glad/glad.h"
#include "../lib/djstdlib/core.h"
#include "../lib/djstdlib/os.h"
#include "../common.h"
Texture createTexture(const char* source_path);
typedef struct Texture Texture;
struct Texture {
uint32 tex_id;
int32 width;
int32 height;
};
typedef struct GlyphMeta GlyphMeta;
struct GlyphMeta {
RLVector2 uv0;
RLVector2 uv1;
real32 xOffset;
real32 yOffset;
real32 width;
real32 height;
};
DefineList(GlyphMeta, GlyphMeta);
typedef struct Font Font;
struct Font {
GlyphMetaList glyphMeta;
real32 lineHeight;
real32 charWidth;
uint32 texId;
uint32 glyphTableTexId;
uint32 glyphTableBufId;
};
Texture createTexture(const char* bitmap, int32 width, int32 height);
Texture createTextureFromFile(const char* source_path);
Font createFont(Arena *arena, string ttfLocation, real32 lineHeight);
#endif

View File

@@ -1,10 +1,7 @@
#include "lib/djstdlib/core.h"
#define _POSIX_C_SOURCE 199309L
#include "time.h"
// Library initialisation
#define RAYMATH_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define STB_TRUETYPE_IMPLEMENTATION
#define TINYOBJ_LOADER_C_IMPLEMENTATION
// Project
@@ -67,6 +64,7 @@ struct SomaState {
RLVector3 rotAxisX;
RLVector3 rotAxisY;
UI_Rect *threedeePaneRect;
real64 explosionOffset;
};
typedef struct Soma Soma;
@@ -209,17 +207,12 @@ static void advanceDisplayReverse(Soma *soma) {
static void advanceDisplay(Soma *soma) {
if (soma->state.displayingSolutions) {
if (soma->state.displayedSolution == soma->solutions.length - 1) {
soma->state.displayedSolution = 0;
} else {
soma->state.explosionOffset = 0;
soma->state.displayedSolution += 1;
}
} else {
if (soma->state.displayedPolycube == soma->polycubeInput.length - 1) {
soma->state.displayedPolycube = 0;
soma->state.displayedSolution %= soma->solutions.length;
} else {
soma->state.displayedPolycube += 1;
}
soma->state.displayedPolycube %= soma->polycubes.length;
}
}
@@ -281,6 +274,11 @@ void processInput(Soma *soma) {
if (soma->state.displayingSolutions) {
real64 scrollDelta = soma->ui->prevInput->mouse.scroll.dY;
if (scrollDelta > 0.001 || scrollDelta < -0.001) {
soma->state.explosionOffset += scrollDelta;
if (soma->state.explosionOffset > 0) {
scrollDelta -= soma->state.explosionOffset;
soma->state.explosionOffset = 0;
}
SceneGraphNode *rootNode = getSceneGraphNode(soma->scene, soma->solutionNode);
int32 nextChildHandle = rootNode->firstChild;
while (nextChildHandle) {
@@ -335,11 +333,12 @@ static void ui_Interaction(UI_Context *ui, Soma *soma) {
real32 padding = 20;
real32 childGap = 5;
UI(ui, .childGap=padding, .flags=UI_Flag_Vertical) {
UI(.flags=UI_Flag_Center | UI_Flag_HeightGrow) {
UI(.childGap=padding, .flags=UI_Flag_Vertical | UI_Flag_Center) {
if (soma->state.displayingSolutions && soma->solutions.length > 0) {
SomaSolution *currentSolution = &soma->solutions.data[soma->state.displayedSolution];
for (int x = 0; x < soma->puzzleDims[0]; x++) UI(ui, .childGap=childGap, .flags=UI_Flag_Vertical) {
for (int y = 0; y < soma->puzzleDims[1]; y++) UI(ui, .childGap=childGap) {
for (int x = 0; x < soma->puzzleDims[0]; x++) UI(.childGap=childGap, .flags=UI_Flag_Vertical) {
for (int y = 0; y < soma->puzzleDims[1]; y++) UI(.childGap=childGap) {
for (int z = 0; z < soma->puzzleDims[2]; z++) {
for (EachIn(*currentSolution, i)) {
uint64 spaceRepr = currentSolution->data[i];
@@ -349,7 +348,7 @@ static void ui_Interaction(UI_Context *ui, Soma *soma) {
.dim_y = soma->puzzleDims[1],
.dim_z = soma->puzzleDims[2],
}, x, y, z);
if (cellActive) UI(ui,
if (cellActive) UI(
.width=boxSize,
.height=boxSize,
.color=soma->polycubeInput.data[i].color,
@@ -362,8 +361,8 @@ static void ui_Interaction(UI_Context *ui, Soma *soma) {
}
} else {
PolycubeInput *currentPolycube = &soma->polycubeInput.data[soma->state.displayedPolycube];
for (int x = 0; x < currentPolycube->repr.dim_x; x++) UI(ui, .childGap=childGap, .flags=UI_Flag_Vertical) {
for (int y = 0; y < currentPolycube->repr.dim_y; y++) UI(ui, .childGap=childGap) {
for (int x = 0; x < currentPolycube->repr.dim_x; x++) UI(.childGap=childGap, .flags=UI_Flag_Vertical) {
for (int y = 0; y < currentPolycube->repr.dim_y; y++) UI(.childGap=childGap) {
for (int z = 0; z < currentPolycube->repr.dim_z; z++) {
bool cellActive = filledAt(&currentPolycube->repr, x, y, z);
if (ui_CheckboxRect(ui, &cellActive, UI_CreateRect(
@@ -380,42 +379,45 @@ static void ui_Interaction(UI_Context *ui, Soma *soma) {
}
}
}
}
}
static void DJUI_Soma(UI_Context *ui, Soma *soma) {
UI(ui, .flags=UI_Flag_HeightGrow | UI_Flag_WidthGrow) {
UI(ui, .padding=UI_PadUniform(10), .color={0.2, 0.2, 0.2, 1}, .flags=UI_Flag_HeightGrow) {
RLVector4 darkgrey = {0.2, 0.2, 0.2, 1};
RLVector4 grey = {0.4, 0.4, 0.4, 1};
UI(.flags=UI_Flag_HeightGrow | UI_Flag_WidthGrow) {
UI(.padding={.left=20, .right=20, .top=5}, .color=darkgrey, .flags=UI_Flag_HeightGrow | UI_Flag_Vertical) {
UI() UI_Text(s("Somaesque"), .lineHeight=26);
ui_Interaction(ui, soma);
}
UI(ui, .flags=UI_Flag_Vertical | UI_Flag_HeightGrow | UI_Flag_WidthGrow) {
UI(ui, .padding=UI_PadUniform(5), .color={0.2, 0.2, 0.2, 1}, .flags=UI_Flag_WidthGrow) {
UI(ui, .width=65);
UI(ui, .flags=UI_Flag_WidthGrow);
UI(ui) {
RLVector4 color = {0.5, 0.5, 0.5, 1};
UI(ui, .childGap=10) {
if (ui_Button(ui, UI_CreateRect(.width=30, .height=30, .color=color))) {
UI(.flags=UI_Flag_Vertical | UI_Flag_HeightGrow | UI_Flag_WidthGrow) {
UI(.padding=UI_PadUniform(10), .color=darkgrey, .flags=UI_Flag_WidthGrow) {
UI(.flags=UI_Flag_WidthGrow) {
UI(.childGap=10) {
if (ui_Button(ui, UI_CreateRect(.height=30, .padding={.left=10, .right=10}, .color=grey), UI_CreateText(s("Previous"), .lineHeight=20))) {
advanceDisplayReverse(soma);
}
if (ui_Button(ui, UI_CreateRect(.width=30, .height=30, .color=color))) {
if (ui_Button(ui, UI_CreateRect(.height=30, .padding={.left=10, .right=10}, .color=grey), UI_CreateText(s("Next"), .lineHeight=20))) {
advanceDisplay(soma);
}
}
}
UI(ui, .flags=UI_Flag_WidthGrow);
UI(ui, .width=65, .childGap=5) {
UI(.childGap=10) {
if (ui_Button(ui, UI_CreateRect(
.width=30,
.height=30,
.color=soma->state.displayingSolutions ? (RLVector4){1,0,0,0.5} : COLOR_RED,
))) {
.padding={.left=10, .right=10},
.color=soma->state.displayingSolutions ? grey : (RLVector4){0.2, 0.2, 0.7, 1},
), UI_CreateText(s("Design"), .lineHeight=20))) {
soma->state.displayingSolutions = false;
}
if (ui_Button(ui, UI_CreateRect(
.width=30,
.height=30,
.color=soma->state.displayingSolutions ? COLOR_GREEN : (RLVector4){0,1,0,0.5},
))) {
.padding={.left=10, .right=10},
.color=soma->state.displayingSolutions ? (RLVector4){0.2, 0.2, 0.7, 1} : grey,
), UI_CreateText(s("Solve"), .lineHeight=20))) {
if (!soma->state.displayingSolutions) {
tryScheduleSolve(soma);
} else {
@@ -424,7 +426,7 @@ static void DJUI_Soma(UI_Context *ui, Soma *soma) {
}
}
}
UI(ui, .flags=UI_Flag_WidthGrow | UI_Flag_HeightGrow | UI_Flag_3DScene);
UI(.flags=UI_Flag_WidthGrow | UI_Flag_HeightGrow | UI_Flag_3DScene);
}
}
}
@@ -526,7 +528,8 @@ int mainGfx() {
renderer.phongShader = &phongShader;
renderer.cubeMesh = &cubeMesh;
UI_Context ui = ui_initContext(arena, &renderer);
Arena *uiArena = arenaAlloc(Megabytes(64));
UI_Context ui = ui_initContext(uiArena, &renderer);
Soma soma = {
.window = {
@@ -591,6 +594,13 @@ int mainGfx() {
getSceneGraphNode(&mainScene, renderer.light)->translation = (RLVector3){4.0f, 6.0f, 24.0f};
Font kodeMono = createFont(arena, s("./assets/fonts/KodeMono.ttf"), 96.0f);
renderer.activeFont = &kodeMono;
Shader textShader = createShader(s("./assets/shaders/text.vertex.glsl"), s("./assets/shaders/text.fragment.glsl"));
renderer.textShader = &textShader;
// Render loop
real64 lastFrame = glfwGetTime();
real64 timeDelta = 1.0f / TARGET_FPS;
real64 frameStart = lastFrame;

View File

@@ -1,87 +1,144 @@
#include "render.h"
#include "debug.h"
RenderObjects_Rectangle createRectangleObjects(Arena *arena, size_t count) {
RenderObjects_Rectangle result = {0};
static RenderObjects_Char createCharObjects(Arena *arena, size_t count) {
RenderObjects_Char result = {0};
result.count = count;
result.p0 = PushFullList(arena, RLVec2List, count);
result.p1 = PushFullList(arena, RLVec2List, count);
result.color = PushFullList(arena, RLVec4List, count);
result.borderRadius = PushFullList(arena, FloatList, count);
result.borderThickness = PushFullList(arena, FloatList, count);
result.edgeSoftness = PushFullList(arena, FloatList, count);
result.begin.buf = PushFullList(arena, RLVec2List, count);
result.glyph.buf = PushFullList(arena, IntList, count);
result.lineHeight.buf = PushFullList(arena, FloatList, count);
result.color.buf = PushFullList(arena, RLVec4List, count);
glGenVertexArrays(1, &result.vao);
glGenBuffers(1, &result.p0BufferId);
glGenBuffers(1, &result.p1BufferId);
glGenBuffers(1, &result.colorBufferId);
glGenBuffers(1, &result.borderRadiusBufferId);
glGenBuffers(1, &result.borderThicknessBufferId);
glGenBuffers(1, &result.edgeSoftnessBufferId);
glGenBuffers(1, &result.begin.bufId);
glGenBuffers(1, &result.glyph.bufId);
glGenBuffers(1, &result.lineHeight.bufId);
glGenBuffers(1, &result.color.bufId);
glBindVertexArray(result.vao);
glBindBuffer(GL_ARRAY_BUFFER, result.p0BufferId);
glBufferData(GL_ARRAY_BUFFER, result.p0.length * sizeof(RLVec2List_underlying), 0, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(RLVec2List_underlying), NULL);
int32 bufItemSize = sizeof(result.begin.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.begin.bufId);
glBufferData(GL_ARRAY_BUFFER, result.begin.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(0, 1);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, result.p1BufferId);
glBufferData(GL_ARRAY_BUFFER, result.p1.length * sizeof(RLVec2List_underlying), 0, GL_DYNAMIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(RLVec2List_underlying), NULL);
bufItemSize = sizeof(result.glyph.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.glyph.bufId);
glBufferData(GL_ARRAY_BUFFER, result.glyph.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribIPointer(1, 1, GL_INT, bufItemSize, NULL);
glVertexAttribDivisor(1, 1);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, result.colorBufferId);
glBufferData(GL_ARRAY_BUFFER, result.color.length * sizeof(RLVec4List_underlying), 0, GL_DYNAMIC_DRAW);
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(RLVec4List_underlying), NULL);
bufItemSize = sizeof(result.lineHeight.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.lineHeight.bufId);
glBufferData(GL_ARRAY_BUFFER, result.lineHeight.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(2, 1);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, result.borderRadiusBufferId);
glBufferData(GL_ARRAY_BUFFER, result.borderRadius.length * sizeof(FloatList_underlying), 0, GL_DYNAMIC_DRAW);
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, sizeof(FloatList_underlying), NULL);
bufItemSize = sizeof(result.color.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.color.bufId);
glBufferData(GL_ARRAY_BUFFER, result.color.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(3, 1);
glEnableVertexAttribArray(3);
glBindBuffer(GL_ARRAY_BUFFER, result.borderThicknessBufferId);
glBufferData(GL_ARRAY_BUFFER, result.borderThickness.length * sizeof(FloatList_underlying), 0, GL_DYNAMIC_DRAW);
glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(FloatList_underlying), NULL);
return result;
}
static RenderObjects_Rect createRectangleObjects(Arena *arena, size_t count) {
RenderObjects_Rect result = {0};
result.count = count;
result.p0.buf = PushFullList(arena, RLVec2List, count);
result.p1.buf = PushFullList(arena, RLVec2List, count);
result.color.buf = PushFullList(arena, RLVec4List, count);
result.borderRadius.buf = PushFullList(arena, FloatList, count);
result.borderThickness.buf = PushFullList(arena, FloatList, count);
result.edgeSoftness.buf = PushFullList(arena, FloatList, count);
glGenVertexArrays(1, &result.vao);
glGenBuffers(1, &result.p0.bufId);
glGenBuffers(1, &result.p1.bufId);
glGenBuffers(1, &result.color.bufId);
glGenBuffers(1, &result.borderRadius.bufId);
glGenBuffers(1, &result.borderThickness.bufId);
glGenBuffers(1, &result.edgeSoftness.bufId);
glGenBuffers(1, &result.edgeSoftness.bufId);
glBindVertexArray(result.vao);
int32 bufItemSize = sizeof(result.p0.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.p0.bufId);
glBufferData(GL_ARRAY_BUFFER, result.p0.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(0, 1);
glEnableVertexAttribArray(0);
bufItemSize = sizeof(result.p1.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.p1.bufId);
glBufferData(GL_ARRAY_BUFFER, result.p1.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(1, 1);
glEnableVertexAttribArray(1);
bufItemSize = sizeof(result.color.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.color.bufId);
glBufferData(GL_ARRAY_BUFFER, result.color.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(2, 1);
glEnableVertexAttribArray(2);
bufItemSize = sizeof(result.borderRadius.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.borderRadius.bufId);
glBufferData(GL_ARRAY_BUFFER, result.borderRadius.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(3, 1);
glEnableVertexAttribArray(3);
bufItemSize = sizeof(result.borderThickness.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.borderThickness.bufId);
glBufferData(GL_ARRAY_BUFFER, result.borderThickness.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(4, 1);
glEnableVertexAttribArray(4);
glBindBuffer(GL_ARRAY_BUFFER, result.edgeSoftnessBufferId);
glBufferData(GL_ARRAY_BUFFER, result.edgeSoftness.length * sizeof(FloatList_underlying), 0, GL_DYNAMIC_DRAW);
glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(FloatList_underlying), NULL);
bufItemSize = sizeof(result.edgeSoftness.buf.data[0]);
glBindBuffer(GL_ARRAY_BUFFER, result.edgeSoftness.bufId);
glBufferData(GL_ARRAY_BUFFER, result.edgeSoftness.buf.length * bufItemSize, 0, GL_STREAM_DRAW);
glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, bufItemSize, NULL);
glVertexAttribDivisor(5, 1);
glEnableVertexAttribArray(5);
return result;
}
void updateRectangleObjectBuffers(Renderer *r) {
#define GL_UpdateBuffer(buffer) \
glBindBuffer(GL_ARRAY_BUFFER, (buffer).bufId);\
glBufferSubData(GL_ARRAY_BUFFER, 0, (buffer).buf.length * sizeof((buffer).underlying), (buffer).buf.data);
static void updateCharObjectBuffers(Renderer *r) {
glBindVertexArray(r->chars.vao);
GL_UpdateBuffer(r->chars.begin);
GL_UpdateBuffer(r->chars.glyph);
GL_UpdateBuffer(r->chars.lineHeight);
GL_UpdateBuffer(r->chars.color);
}
static void updateRectangleObjectBuffers(Renderer *r) {
glBindVertexArray(r->rects.vao);
glBindBuffer(GL_ARRAY_BUFFER, r->rects.p0BufferId);
glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.p0.length * sizeof(RLVec2List_underlying), r->rects.p0.data);
glBindBuffer(GL_ARRAY_BUFFER, r->rects.p1BufferId);
glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.p1.length * sizeof(RLVec2List_underlying), r->rects.p1.data);
glBindBuffer(GL_ARRAY_BUFFER, r->rects.colorBufferId);
glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.color.length * sizeof(RLVec4List_underlying), r->rects.color.data);
glBindBuffer(GL_ARRAY_BUFFER, r->rects.borderRadiusBufferId);
glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.borderRadius.length * sizeof(FloatList_underlying), r->rects.borderRadius.data);
glBindBuffer(GL_ARRAY_BUFFER, r->rects.borderThicknessBufferId);
glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.borderThickness.length * sizeof(FloatList_underlying), r->rects.borderThickness.data);
glBindBuffer(GL_ARRAY_BUFFER, r->rects.edgeSoftnessBufferId);
glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.edgeSoftness.length * sizeof(FloatList_underlying), r->rects.edgeSoftness.data);
GL_UpdateBuffer(r->rects.p0);
GL_UpdateBuffer(r->rects.p1);
GL_UpdateBuffer(r->rects.color);
GL_UpdateBuffer(r->rects.borderRadius);
GL_UpdateBuffer(r->rects.borderThickness);
GL_UpdateBuffer(r->rects.edgeSoftness);
}
Renderer createRenderer(Arena *arena, Scene *scene, Camera *cam, int32 light) {
@@ -90,16 +147,23 @@ Renderer createRenderer(Arena *arena, Scene *scene, Camera *cam, int32 light) {
.light = light,
.camera = cam,
.rects = createRectangleObjects(arena, 1024),
.chars = createCharObjects(arena, 10000),
};
}
void renderBegin(Renderer *r) {
r->rects.p0.length = 0;
r->rects.p1.length = 0;
r->rects.color.length = 0;
r->rects.borderRadius.length = 0;
r->rects.borderThickness.length = 0;
r->rects.edgeSoftness.length = 0;
r->rects.p0.buf.length = 0;
r->rects.p1.buf.length = 0;
r->rects.color.buf.length = 0;
r->rects.borderRadius.buf.length = 0;
r->rects.borderThickness.buf.length = 0;
r->rects.edgeSoftness.buf.length = 0;
r->chars.begin.buf.length = 0;
r->chars.glyph.buf.length = 0;
r->chars.lineHeight.buf.length = 0;
r->chars.color.buf.length = 0;
r->sceneWidth = 0;
r->sceneHeight = 0;
r->sceneX = 0;
@@ -107,10 +171,11 @@ void renderBegin(Renderer *r) {
}
void renderEnd(Renderer *r) {
// 3D Entities
// 3D Scene
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glViewport(r->sceneX, r->height - r->sceneY - r->sceneHeight, r->sceneWidth, r->sceneHeight);
cameraSetAspect(r->camera, r->sceneWidth, r->sceneHeight);
@@ -124,8 +189,6 @@ void renderEnd(Renderer *r) {
glBindVertexArray(r->cubeMesh->vao);
// TODO(djledda): sort by mesh, texture, etc.
int model_uniform = getUniformLocation(r->phongShader, "model");
int solid_color_uniform = getUniformLocation(r->phongShader, "solid_color");
for (EachIn(r->scene->entities, i)) {
@@ -141,29 +204,55 @@ void renderEnd(Renderer *r) {
// 2D overlay
glViewport(0, 0, r->width, r->height);
glUseProgram(r->solidShader->progId);
updateRectangleObjectBuffers(r);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Matrix ortho = MatrixOrtho(0.0, r->width, r->height, 0.0, -1.0, 1.0);
setUniformMat4fv(r->solidShader, "projection", &ortho);
// 1. Rects
updateRectangleObjectBuffers(r);
setUniformMat4fv(r->solidShader, "projection", &ortho);
glBindVertexArray(r->rects.vao);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, r->rects.p0.length);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, r->rects.p0.buf.length);
// 2. Text
updateCharObjectBuffers(r);
glUseProgram(r->textShader->progId);
setUniformMat4fv(r->textShader, "projection", &ortho);
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, r->activeFont->texId);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, r->activeFont->glyphTableTexId);
setUniform1i(r->textShader, "glyph_table", 1);
glBindVertexArray(r->chars.vao);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, r->chars.begin.buf.length);
glDisable(GL_BLEND);
}
void rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real32 height, RLVector4 color, real32 borderRadius, real32 borderThickness) {
ListAppend(r->rects.p0, ((RLVector2){ x, y }));
ListAppend(r->rects.p1, ((RLVector2){ x + width, y + height }));
ListAppend(r->rects.color, color);
ListAppend(r->rects.borderRadius, borderRadius);
ListAppend(r->rects.borderThickness, borderThickness);
ListAppend(r->rects.edgeSoftness, 0.15f);
ListAppend(r->rects.p0.buf, ((RLVector2){ x, y }));
ListAppend(r->rects.p1.buf, ((RLVector2){ x + width, y + height }));
ListAppend(r->rects.color.buf, color);
ListAppend(r->rects.borderRadius.buf, borderRadius);
ListAppend(r->rects.borderThickness.buf, borderThickness);
ListAppend(r->rects.edgeSoftness.buf, 0.15f);
}
void rendererPlaceString(Renderer *r, string s, real32 x, real32 y, RLVector4 color, real32 lineHeight) {
real32 ratio = lineHeight / r->activeFont->lineHeight;
real32 charWidth = ratio * r->activeFont->charWidth;
for (int i = 0; i < s.length; i++) {
ListAppend(r->chars.begin.buf, ((RLVector2){ .x=x + i*charWidth, .y=y }));
ListAppend(r->chars.glyph.buf, s.str[i] - 32);
ListAppend(r->chars.lineHeight.buf, ratio);
ListAppend(r->chars.color.buf, (color));
}
}

View File

@@ -4,42 +4,74 @@
#include "gfx/Shader.h"
#include "gfx/gfx.h"
#include "world/world.h"
#include "lib/djstdlib/core.h"
#include "common.h"
DefineList(RLVector2, RLVec2);
DefineList(RLVector4, RLVec4);
DefineList(real32, Float);
typedef struct RenderObject_BufferInt32 RenderObject_BufferInt32;
struct RenderObject_BufferInt32 {
IntList buf;
uint32 bufId;
IntList_underlying underlying;
};
typedef struct RenderObject_BufferFloat RenderObject_BufferFloat;
struct RenderObject_BufferFloat {
FloatList buf;
uint32 bufId;
FloatList_underlying underlying;
};
typedef struct RenderObject_BufferVec2 RenderObject_BufferVec2;
struct RenderObject_BufferVec2 {
RLVec2List buf;
uint32 bufId;
RLVec2List_underlying underlying;
};
typedef struct RenderObject_BufferVec4 RenderObject_BufferVec4;
struct RenderObject_BufferVec4 {
RLVec4List buf;
uint32 bufId;
RLVec4List_underlying underlying;
};
typedef struct RenderObjects_Rectangle RenderObjects_Rectangle;
struct RenderObjects_Rectangle {
typedef struct RenderObjects_Rect RenderObjects_Rect;
struct RenderObjects_Rect {
uint32 vao;
uint64 count;
RenderObject_BufferVec2 p0;
RenderObject_BufferVec2 p1;
RenderObject_BufferVec4 color;
RenderObject_BufferFloat borderRadius;
RenderObject_BufferFloat borderThickness;
RenderObject_BufferFloat edgeSoftness;
};
RLVec2List p0;
uint32 p0BufferId;
typedef struct RenderObjects_Char RenderObjects_Char;
struct RenderObjects_Char {
uint32 vao;
uint64 count;
RenderObject_BufferVec2 begin;
RenderObject_BufferInt32 glyph;
RenderObject_BufferFloat lineHeight;
RenderObject_BufferVec4 color;
};
RLVec2List p1;
uint32 p1BufferId;
RLVec4List color;
uint32 colorBufferId;
FloatList borderRadius;
uint32 borderRadiusBufferId;
FloatList borderThickness;
uint32 borderThicknessBufferId;
FloatList edgeSoftness;
uint32 edgeSoftnessBufferId;
typedef struct GlyphData GlyphData;
struct GlyphData {
real32 x0;
real32 y0;
real32 x1;
real32 y1;
};
typedef struct Renderer Renderer;
struct Renderer {
RenderObjects_Rectangle rects;
RenderObjects_Rect rects;
RenderObjects_Char chars;
Shader *phongShader;
Shader *solidShader;
Shader *textShader;
Font *activeFont;
int32 width;
int32 height;
@@ -60,7 +92,7 @@ Renderer createRenderer(Arena *arena, Scene *scene, Camera *cam, int32 light);
void renderBegin(Renderer *r);
void renderEnd(Renderer *r);
void rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real32 height, RLVector4 color, real32 borderRadius, real32 borderThickness);
void updateRectangleObjectBuffers(Renderer *r);
void rendererPlaceString(Renderer *r, string s, real32 x, real32 y, RLVector4 color, real32 fontSize);
#define Render(r) DeferLoop(renderBegin((r)), renderEnd((r)))
#endif

123
src/ui.c
View File

@@ -2,6 +2,9 @@
#include "GLFW/glfw3.h"
#include "lib/djstdlib/core.h"
#include <stdio.h>
#include <string.h>
UI_Context *__UI_current_ctx__ = NULL;
static bool glfwMouse(GLFWwindow *window, int mouseBtnCode) {
switch (glfwGetMouseButton(window, mouseBtnCode)) {
@@ -48,6 +51,7 @@ UI_Context ui_initContext(Arena *arena, Renderer *renderer) {
UI_RectList list = PushListZero(arena, UI_RectList, Thousand(10));
ListAppend(list, ((UI_Rect){0})); // empty item
return (UI_Context){
.arena=arena,
.rects=list,
.prevRects=prevList,
.hotNode = 0,
@@ -58,30 +62,16 @@ UI_Context ui_initContext(Arena *arena, Renderer *renderer) {
};
}
static void ui_playground(UI_Context *ui) {
UI(ui,
.width=ui->renderer->width,
.height=ui->renderer->height,
.color=COLOR_WHITE,
) {
UI(ui,
.color=COLOR_GREEN,
.childGap=10,
.flags=UI_Flag_HeightGrow,
.padding=UI_PadUniform(10),
) {
for (int i = 0; i < 4; i++) {
UI(ui,
.width=100,
.flags=UI_Flag_HeightGrow,
.color=COLOR_RED,
.borderRadius=10,
);
}
}
UI(ui, .color=COLOR_CYAN, .flags=UI_Flag_HeightGrow | UI_Flag_WidthGrow);
void ui_placeText(UI_Context *ui, UI_RectStr strData) {
UI_RectStr *strDataCopy = PushStruct(ui->arena, UI_RectStr);
*strDataCopy = strData;
if (strDataCopy->lineHeight == -1) {
strDataCopy->lineHeight = ui->renderer->activeFont->lineHeight;
}
strDataCopy->s = PushString(ui->arena, strData.s.length);
memcpy(strDataCopy->s.str, strData.s.str, strData.s.length);
UI_Rect *currRect = &ui->rects.data[ui->currRect];
currRect->stringData = strDataCopy;
}
void ui_sizingPass(UI_Context *ui, bool isXAxis, int32 rectHandle) {
@@ -116,19 +106,23 @@ void ui_sizingPass(UI_Context *ui, bool isXAxis, int32 rectHandle) {
while (childHandle) {
child = &ui->rects.data[childHandle];
if (isVertical && (child->flags & UI_Flag_WidthGrow) && isXAxis) {
if (isXAxis) {
if (child->flags & UI_Flag_WidthGrow) {
if (isVertical) {
child->resolvedWidth = rect->resolvedWidth - rect->padding.left - rect->padding.right;
}
if (!isVertical && (child->flags & UI_Flag_HeightGrow) && !isXAxis) {
child->resolvedHeight = rect->resolvedHeight - rect->padding.top - rect->padding.bottom;
}
if (child->flags & UI_Flag_WidthGrow && !isVertical && isXAxis) {
} else {
child->resolvedWidth += childBreadthInc;
}
if (child->flags & UI_Flag_HeightGrow && isVertical && !isXAxis) {
}
} else {
if (child->flags & UI_Flag_HeightGrow) {
if (!isVertical) {
child->resolvedHeight = rect->resolvedHeight - rect->padding.top - rect->padding.bottom;
} else {
child->resolvedHeight += childBreadthInc;
}
}
}
ui_sizingPass(ui, isXAxis, childHandle);
@@ -142,7 +136,9 @@ void ui_calcLayout(UI_Context *ui, bool isXAxis, int32 rectHandle) {
bool isVertical = (rect->flags & UI_Flag_Vertical);
real32 coord = isXAxis ? rect->x + rect->xOffset + rect->padding.left : rect->y + rect->yOffset + rect->padding.top;
real32 coord = isXAxis
? rect->x + rect->xOffset + rect->padding.left
: rect->y + rect->yOffset + rect->padding.top;
int32 childHandle = rect->firstChild;
UI_Rect *child;
@@ -150,9 +146,13 @@ void ui_calcLayout(UI_Context *ui, bool isXAxis, int32 rectHandle) {
child = &ui->rects.data[childHandle];
if (isXAxis) {
child->x = coord;
child->x = child->flags & UI_Flag_Center && isVertical
? coord + (rect->resolvedWidth - rect->padding.left - rect->padding.right)/2 - child->resolvedWidth/2
: coord;
} else {
child->y = coord;
child->y = child->flags & UI_Flag_Center && !isVertical
? coord + (rect->resolvedHeight - rect->padding.top - rect->padding.bottom)/2 - child->resolvedHeight/2
: coord;
}
ui_calcLayout(ui, isXAxis, childHandle);
@@ -167,6 +167,8 @@ void ui_calcLayout(UI_Context *ui, bool isXAxis, int32 rectHandle) {
}
void ui_begin(UI_Context *ui) {
__UI_current_ctx__ = ui;
arenaFreeFrom(ui->arena, 0);
ClearList(ui->prevRects);
ListAppendList(ui->prevRects, ui->rects);
ui->cursorIsPointer = false;
@@ -204,6 +206,17 @@ void ui_end(UI_Context *ui) {
rect->borderRadius,
rect->borderThickness
);
if (rect->stringData) {
UI_RectStr *str = rect->stringData;
rendererPlaceString(
ui->renderer,
str->s,
rect->x + str->xOffset,
rect->y + str->yOffset,
str->color,
str->lineHeight
);
}
}
if (ui->scene3DHandle) {
@@ -213,10 +226,12 @@ void ui_end(UI_Context *ui) {
ui->renderer->sceneWidth = (int32)scene3DRect->resolvedWidth;
ui->renderer->sceneHeight = (int32)scene3DRect->resolvedHeight;
}
__UI_current_ctx__ = NULL;
}
static bool pointInRect(real32 x, real32 y, UI_Rect rect) {
return x > rect.x && y > rect.y && x < (rect.x + rect.width) && y < (rect.y + rect.height);
return x > rect.x && y > rect.y && x < (rect.x + rect.resolvedWidth) && y < (rect.y + rect.resolvedHeight);
}
void ui_openElement(UI_Context *ui, UI_Rect rect) {
@@ -252,18 +267,35 @@ void ui_closeElement(UI_Context *ui) {
if (currRect->width != -1) {
currRect->resolvedWidth = currRect->width;
}
currRect->resolvedWidth += currRect->padding.left + currRect->padding.right;
if (currRect->minWidth == -1) {
if (currRect->stringData) {
real32 charWidth = currRect->stringData->lineHeight / ui->renderer->activeFont->lineHeight * ui->renderer->activeFont->charWidth;
currRect->minWidth = charWidth*currRect->stringData->s.length;
} else {
currRect->minWidth = 0;
}
}
currRect->resolvedWidth = currRect->padding.left + currRect->padding.right + (currRect->resolvedWidth < currRect->minWidth ? currRect->minWidth : currRect->resolvedWidth);
if (currRect->height != -1) {
currRect->resolvedHeight = currRect->height;
} else if (currRect->minHeight == -1) {
if (currRect->stringData) {
currRect->minHeight = currRect->stringData->lineHeight;
} else {
currRect->minHeight = 0;
}
currRect->resolvedHeight += currRect->padding.top + currRect->padding.bottom;
}
currRect->resolvedHeight = currRect->padding.top + currRect->padding.bottom + (currRect->resolvedHeight < currRect->minHeight ? currRect->minHeight : currRect->resolvedHeight);
bool vertical = parentRect->flags & UI_Flag_Vertical;
real32 currBreadth = vertical ? currRect->resolvedHeight : currRect->resolvedWidth;
real32 currCrossBreadth = vertical ? currRect->resolvedWidth : currRect->resolvedHeight;
real32 parentBreadth = vertical ? parentRect->height : parentRect->width;
real32 currParentBreadth = vertical ? parentRect->resolvedHeight : parentRect->resolvedWidth;
real32 parentCrossBreadth = vertical ? parentRect->width : parentRect->height;
real32 currParentCrossBreadth = vertical ? parentRect->resolvedWidth : parentRect->resolvedHeight;
real32 gap = parentRect->childGap;
real32 breadthInc = (parentRect->firstChild == ui->currRect ? 0 : gap) + currBreadth;
@@ -276,7 +308,7 @@ void ui_closeElement(UI_Context *ui) {
}
}
real32 newCrossBreadth = currCrossBreadth > parentCrossBreadth ? currCrossBreadth : parentCrossBreadth;
real32 newCrossBreadth = currCrossBreadth > currParentCrossBreadth ? currCrossBreadth : currParentCrossBreadth;
if (parentCrossBreadth == -1) {
if (vertical) {
parentRect->resolvedWidth = newCrossBreadth;
@@ -288,8 +320,8 @@ void ui_closeElement(UI_Context *ui) {
ui->currRect = ui->rects.data[ui->currRect].parent;
}
bool ui_Button(UI_Context *ui, UI_Rect rect) {
int32 id = UI_NextID(ui);
bool ui_Button(UI_Context *ui, UI_Rect rect, UI_RectStr textAttr) {
int32 id = UI_NextID();
bool clicked = false;
if (pointInRect(ui->input->mouse.point.x, ui->input->mouse.point.y, ui->prevRects.data[id])) {
@@ -302,8 +334,9 @@ bool ui_Button(UI_Context *ui, UI_Rect rect) {
}
rect.borderRadius = 5;
rect.borderThickness = 0;
UI_FromRect(ui, rect);
UI_FromRect(rect) {
ui_placeText(ui, textAttr);
}
return clicked;
}
@@ -312,7 +345,7 @@ bool ui_Button(UI_Context *ui, UI_Rect rect) {
* Returns whether the checkbox was clicked
*/
bool ui_CheckboxRect(UI_Context *ui, bool *value, UI_Rect rect) {
int32 id = UI_NextID(ui);
int32 id = UI_NextID();
bool clicked = false;
if (pointInRect(ui->input->mouse.point.x, ui->input->mouse.point.y, ui->prevRects.data[id])) {
ui->cursorIsPointer = true;
@@ -326,12 +359,12 @@ bool ui_CheckboxRect(UI_Context *ui, bool *value, UI_Rect rect) {
if (*value) {
rect.borderRadius = 5;
rect.borderThickness = 0;
UI_FromRect(ui, rect);
UI_FromRect(rect);
} else {
rect.borderRadius = 5;
rect.borderThickness = 2;
rect.color = COLOR_WHITE;
UI_FromRect(ui, rect);
UI_FromRect(rect);
}
return clicked;

View File

@@ -41,6 +41,7 @@ enum UI_Flag {
UI_Flag_HeightGrow=1<<1,
UI_Flag_Vertical=1<<2, // Default is horizontal
UI_Flag_3DScene=1<<3,
UI_Flag_Center=1<<4,
// ..
UI_Flag_COUNT,
};
@@ -53,9 +54,18 @@ struct UI_Padding {
real32 left;
};
typedef struct UI_RectStr UI_RectStr;
struct UI_RectStr {
string s;
real32 xOffset;
real32 yOffset;
real32 lineHeight;
RLVector4 color;
};
typedef struct UI_Rect UI_Rect;
struct UI_Rect {
// UI_Rect_LayoutFlag
/** UI_Rect_LayoutFlag */
uint64 flags;
int32 parent;
@@ -87,11 +97,14 @@ struct UI_Rect {
real32 y;
real32 resolvedWidth;
real32 resolvedHeight;
UI_RectStr *stringData;
};
DefineList(UI_Rect, UI_Rect);
typedef struct UI_Context UI_Context;
struct UI_Context {
Arena *arena;
UI_RectList prevRects;
UI_RectList rects;
int32 hotNode;
@@ -114,16 +127,21 @@ void ui_end(UI_Context *ui);
UI_Context ui_initContext(Arena *arena, Renderer *renderer);
void ui_resolve();
void ui_rect(UI_Context *ui, UI_Rect rect);
void ui_placeText(UI_Context *ui, UI_RectStr strData);
void ui_openElement(UI_Context *ui, UI_Rect rect);
void ui_closeElement(UI_Context *ui);
bool ui_CheckboxRect(UI_Context *ui, bool *value, UI_Rect rect);
#define UI(ui, ...) DeferLoop(ui_openElement((ui), UI_CreateRect(__VA_ARGS__)), ui_closeElement((ui)))
#define UI_FromRect(ui, rect) DeferLoop(ui_openElement((ui), (rect)), ui_closeElement((ui)))
#define UI_CreateRect(...) ((UI_Rect){.width=-1, .height=-1, __VA_ARGS__})
extern UI_Context *__UI_current_ctx__;
#define UI(...) DeferLoop(ui_openElement((__UI_current_ctx__), UI_CreateRect(__VA_ARGS__)), ui_closeElement((__UI_current_ctx__)))
#define UI_CreateRect(...) ((UI_Rect){.minHeight=-1, .minWidth=-1, .width=-1, .height=-1, __VA_ARGS__})
#define UI_FromRect(rect) DeferLoop(ui_openElement((__UI_current_ctx__), (rect)), ui_closeElement((__UI_current_ctx__)))
#define UI_Pass(ui) DeferLoop(ui_begin((ui)), ui_end((ui)))
#define UI_PadUniform(padding) ((UI_Padding){ (padding), (padding), (padding), (padding) })
#define UI_NextID(ui) ((ui)->rects.length)
#define UI_NextID() ((__UI_current_ctx__)->rects.length)
#define UI_Text(str, ...) (ui_placeText((__UI_current_ctx__), ((UI_RectStr){ .xOffset=0, .yOffset=0, .lineHeight=-1, .s=(str), __VA_ARGS__ })))
#define UI_CreateText(str, ...) ((UI_RectStr){ .xOffset=0, .yOffset=0, .lineHeight=-1, .s=(str), __VA_ARGS__ })
#endif