From e768b3832244dd83020d2b679a204434bd0e6475 Mon Sep 17 00:00:00 2001 From: Daniel Ledda Date: Fri, 6 Mar 2026 01:36:29 +0100 Subject: [PATCH] update --- assets/shaders/2d-solid.fragment.glsl | 4 +- assets/shaders/2d-solid.vertex.glsl | 5 +- assets/shaders/text.vertex.glsl | 21 +---- build | 2 +- src/gfx/Mesh.c | 22 ++--- src/gfx/Shader.c | 72 ++++++++------- src/gfx/Texture.c | 20 ++--- src/gfx/geometry.c | 48 +++++----- src/gfx/geometry.h | 11 +-- src/main.c | 52 +++++++---- src/render.c | 41 +++++---- src/render.h | 5 +- src/ui.c | 121 +++++++++++++++++++++----- src/ui.h | 60 ++++++++++--- 14 files changed, 298 insertions(+), 186 deletions(-) diff --git a/assets/shaders/2d-solid.fragment.glsl b/assets/shaders/2d-solid.fragment.glsl index 9860582..41dc79b 100644 --- a/assets/shaders/2d-solid.fragment.glsl +++ b/assets/shaders/2d-solid.fragment.glsl @@ -8,6 +8,7 @@ in vec2 frag_dest_half_size; in float frag_softness; in float frag_border_radius; in float frag_border_thickness; +in vec4 frag_border_color; float roundedRectSDF(vec2 sample_pos, vec2 rect_center, vec2 rect_half_size, float r) { vec2 d2 = (abs(rect_center - sample_pos) - rect_half_size + vec2(r, r)); @@ -51,5 +52,6 @@ void main() { float sdf_factor = 1 - smoothstep(0, 2*frag_softness, dist); - pixel_color = frag_color * sample * sdf_factor * border_factor; + pixel_color = frag_border_color * sample * sdf_factor * border_factor + + frag_color * sample * sdf_factor; }; diff --git a/assets/shaders/2d-solid.vertex.glsl b/assets/shaders/2d-solid.vertex.glsl index 32420e2..7facc67 100644 --- a/assets/shaders/2d-solid.vertex.glsl +++ b/assets/shaders/2d-solid.vertex.glsl @@ -4,7 +4,8 @@ layout (location = 1) in vec2 p1; layout (location = 2) in vec4 color; layout (location = 3) in float border_radius; layout (location = 4) in float border_thickness; -layout (location = 5) in float edge_softness; +layout (location = 5) in vec4 border_color; +layout (location = 6) in float edge_softness; uniform mat4 projection; @@ -15,6 +16,7 @@ out vec2 frag_dest_half_size; out float frag_softness; out float frag_border_radius; out float frag_border_thickness; +out vec4 frag_border_color; const vec2 rectangle_vertices[4] = vec2[]( vec2(-1, -1), @@ -40,5 +42,6 @@ void main() { frag_dest_half_size = dest_half_size; frag_border_radius = border_radius; frag_border_thickness = border_thickness; + frag_border_color = border_color; frag_softness = edge_softness; } diff --git a/assets/shaders/text.vertex.glsl b/assets/shaders/text.vertex.glsl index 6a344eb..042d80a 100644 --- a/assets/shaders/text.vertex.glsl +++ b/assets/shaders/text.vertex.glsl @@ -1,24 +1,9 @@ #version 330 core layout (location = 0) in vec2 begin; layout (location = 1) in int glyph; -layout (location = 2) in float lineHeight; +layout (location = 2) in float fontSize; 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; @@ -57,8 +42,8 @@ void main() { vec2 offset = chunk2.xy; vec2 dims = chunk2.zw; - vec2 p0 = begin + offset*lineHeight; - vec2 p1 = begin + (offset + dims)*lineHeight; + vec2 p0 = begin + offset*fontSize; + vec2 p1 = begin + (offset + dims)*fontSize; vec2 dest_half_size = (p1 - p0) / 2; vec2 dest_center = (p1 + p0) / 2; diff --git a/build b/build index e5f68bb..32263d6 100755 --- a/build +++ b/build @@ -5,7 +5,7 @@ COMMON_FLAGS="-DOS_LINUX=1 -DCOMPOSITOR_WAYLAND=1 -xc -std=c99 -Wno-initializer- echo [Building target] if [ $DEBUG ]; then - time clang -O1 -g -g2 $COMMON_FLAGS -O1 -fsanitize=address,undefined -DDJSTDLIB_DEBUG=1 ./src/main.c -o ./target/somaesque $LIB_INCLUDE + time clang -O0 -g -g2 $COMMON_FLAGS -DDJSTDLIB_DEBUG=1 ./src/main.c -o ./target/somaesque $LIB_INCLUDE else time clang -O2 $COMMON_FLAGS ./src/main.c -o ./target/somaesque $LIB_INCLUDE fi diff --git a/src/gfx/Mesh.c b/src/gfx/Mesh.c index 473c454..706b963 100644 --- a/src/gfx/Mesh.c +++ b/src/gfx/Mesh.c @@ -68,18 +68,18 @@ Mesh createMesh(const char* obj_file) { glBindVertexArray(result.vao); glBindBuffer(GL_ARRAY_BUFFER, result.buffers.xyz); - glBufferData(GL_ARRAY_BUFFER, vertices.length * sizeof(float), vertices.data, GL_STATIC_DRAW); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glBufferData(GL_ARRAY_BUFFER, vertices.length * sizeof(real32), vertices.data, GL_STATIC_DRAW); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(real32), (void*)0); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, result.buffers.uv); - glBufferData(GL_ARRAY_BUFFER, texcoords.length * sizeof(float), texcoords.data, GL_STATIC_DRAW); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); + glBufferData(GL_ARRAY_BUFFER, texcoords.length * sizeof(real32), texcoords.data, GL_STATIC_DRAW); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(real32), (void*)0); glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, result.buffers.normals); - glBufferData(GL_ARRAY_BUFFER, normals.length * sizeof(float), normals.data, GL_STATIC_DRAW); - glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glBufferData(GL_ARRAY_BUFFER, normals.length * sizeof(real32), normals.data, GL_STATIC_DRAW); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(real32), (void*)0); glEnableVertexAttribArray(2); //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); @@ -92,24 +92,24 @@ Mesh createMesh(const char* obj_file) { Mesh createMeshFromShape(const Shape* shape) { Mesh result = {0}; - result.num_indices = shape->indices_size; + result.num_indices = shape->indices.length; glGenVertexArrays(1, &result.vao); glGenBuffers(3, result.vbos); glBindVertexArray(result.vao); glBindBuffer(GL_ARRAY_BUFFER, result.buffers.xyz); - glBufferData(GL_ARRAY_BUFFER, shape->xyz_size * sizeof(float), shape->xyz, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, shape->xyz.length * sizeof(float), shape->xyz.data, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, result.buffers.uv); - glBufferData(GL_ARRAY_BUFFER, shape->uv_size * sizeof(float), shape->uv, GL_STATIC_DRAW); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); + glBufferData(GL_ARRAY_BUFFER, shape->uv.length * sizeof(real32), shape->uv.data, GL_STATIC_DRAW); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(real32), (void*)0); glEnableVertexAttribArray(1); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, result.buffers.elements); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, shape->indices_size * sizeof(unsigned int), shape->indices, GL_STATIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, shape->indices.length * sizeof(uint32), shape->indices.data, GL_STATIC_DRAW); return result; } diff --git a/src/gfx/Shader.c b/src/gfx/Shader.c index 5d40cbf..66128f1 100644 --- a/src/gfx/Shader.c +++ b/src/gfx/Shader.c @@ -10,53 +10,51 @@ typedef enum { } ShaderType; uint32 createGlShader(string file_path, ShaderType shader_type) { - Scratch temp = scratchStart(0, 0); - string shader_code = os_readEntireFile(temp.arena, file_path); - GLuint shader = glCreateShader(shader_type); - const char *shader_code_cstr = cstring(temp.arena, shader_code); - glShaderSource(shader, 1, &shader_code_cstr, NULL); - glCompileShader(shader); - int success; - glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - if (!success) { - GLint info_log_length; - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length); - string info_log = PushString(temp.arena, (size_t)info_log_length + 1); - glGetShaderInfoLog(shader, info_log_length, NULL, info_log.str); - const char* shader_type_name = shader_type == ShaderType_fragment ? "FRAGMENT" : "VERTEX"; - print("%s shader compilation error (%S):\n%S", shader_type_name, file_path, info_log); + GLuint shader; + WithScratch(scratch) { + string shader_code = os_readEntireFile(scratch.arena, file_path); + shader = glCreateShader(shader_type); + const char *shader_code_cstr = cstring(scratch.arena, shader_code); + glShaderSource(shader, 1, &shader_code_cstr, NULL); + glCompileShader(shader); + int success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + GLint info_log_length; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length); + string info_log = PushString(scratch.arena, (size_t)info_log_length + 1); + glGetShaderInfoLog(shader, info_log_length, NULL, info_log.str); + string shader_type_name = shader_type == ShaderType_fragment ? s("FRAGMENT") : s("VERTEX"); + print("%S shader compilation error (%S):\n%S", shader_type_name, file_path, info_log); + } } - scratchEnd(temp); return shader; } Shader createShader(string vertex_path, string fragment_path) { - Scratch temp = scratchStart(0, 0); - Shader result = {0}; + WithScratch(temp) { + uint32 vertex_shader = createGlShader(vertex_path, ShaderType_vertex); + uint32 fragment_shader = createGlShader(fragment_path, ShaderType_fragment); - uint32 vertex_shader = createGlShader(vertex_path, ShaderType_vertex); - uint32 fragment_shader = createGlShader(fragment_path, ShaderType_fragment); + result.progId = glCreateProgram(); + glAttachShader(result.progId, vertex_shader); + glAttachShader(result.progId, fragment_shader); + glLinkProgram(result.progId); - result.progId = glCreateProgram(); - glAttachShader(result.progId, vertex_shader); - glAttachShader(result.progId, fragment_shader); - glLinkProgram(result.progId); + int success; + glGetProgramiv(result.progId, GL_LINK_STATUS, &success); + if (!success) { + GLint info_log_length; + glGetShaderiv(result.progId, GL_INFO_LOG_LENGTH, &info_log_length); + string info_log_prog = PushString(temp.arena, (size_t)info_log_length + 1); + glGetProgramInfoLog(result.progId, info_log_length, NULL, info_log_prog.str); + print("Shader program link error:\n%S", info_log_prog); + } - int success; - glGetProgramiv(result.progId, GL_LINK_STATUS, &success); - if (!success) { - GLint info_log_length; - glGetShaderiv(result.progId, GL_INFO_LOG_LENGTH, &info_log_length); - string info_log_prog = PushString(temp.arena, (size_t)info_log_length + 1); - glGetProgramInfoLog(result.progId, info_log_length, NULL, info_log_prog.str); - print("Shader program link error:\n%S", info_log_prog); + glDeleteShader(vertex_shader); + glDeleteShader(fragment_shader); } - - glDeleteShader(vertex_shader); - glDeleteShader(fragment_shader); - - scratchEnd(temp); return result; } diff --git a/src/gfx/Texture.c b/src/gfx/Texture.c index b6c0dc9..78f266b 100644 --- a/src/gfx/Texture.c +++ b/src/gfx/Texture.c @@ -1,6 +1,4 @@ #include "Texture.h" -#include -#include Texture createTexture(const char* bitmap, int32 width, int32 height) { Texture result = {0}; @@ -25,11 +23,11 @@ DefineList(stbtt_bakedchar, STBBakedChar); static void saveGrayscaleTarga(string location, const char *bytes, int width, int height) { char tgaHeader[18] = { // Image ID length - 0x0, + 0x00, // Color map type - 0x0, + 0x00, // Image type - 0x3, + 0x03, // Color map info (unused) 0x0, 0x0, 0x0, 0x0, 0x0, // Image spec @@ -38,7 +36,7 @@ static void saveGrayscaleTarga(string location, const char *bytes, int width, in width & 0xFF, (width >> 8) & 0xFF, // width height & 0xFF, (height >> 8) & 0xFF, // width 0x8, // bits per px (depth) - 0b00001000, // image descriptor + 0b00000000, // image descriptor }; os_writeEntireFile(location, tgaHeader, ArrayCount(tgaHeader)); os_fileAppend(location, bytes, width * height); @@ -48,8 +46,8 @@ Font createFont(Arena *arena, string ttfLocation, real32 lineHeight) { const int CODEPOINT_START = 32; const int CODEPOINT_END = 126; - int32 atlasWidth = 512;//16 * lineHeight; - int32 atlasHeight = 512;// * lineHeight; + int32 atlasWidth = ((int32)lineHeight)*(int32)sqrt(CODEPOINT_END - CODEPOINT_START); + int32 atlasHeight = ((int32)lineHeight)*(int32)sqrt(CODEPOINT_END - CODEPOINT_START); STBBakedCharList bakedCharlist = PushFullListZero(arena, STBBakedCharList, CODEPOINT_END - CODEPOINT_START); string fontFile = os_readEntireFile(arena, ttfLocation); @@ -58,7 +56,7 @@ Font createFont(Arena *arena, string ttfLocation, real32 lineHeight) { stbtt_InitFont(&info, (unsigned char *)fontFile.str, stbtt_GetFontOffsetForIndex((unsigned char *)fontFile.str, 0)); int ascent, descent, lineGap; stbtt_GetFontVMetrics(&info, &ascent, &descent, &lineGap); - real32 scale = stbtt_ScaleForPixelHeight(&info, 96); + real32 scale = stbtt_ScaleForPixelHeight(&info, lineHeight); char *bakedFontBitmap = PushArrayZero(arena, char, atlasWidth*atlasHeight); int32 bake_result = stbtt_BakeFontBitmap( @@ -68,8 +66,6 @@ Font createFont(Arena *arena, string ttfLocation, real32 lineHeight) { CODEPOINT_START, CODEPOINT_END - CODEPOINT_START, bakedCharlist.data); - saveGrayscaleTarga(s("bakedfont.tga"), bakedFontBitmap, atlasWidth, atlasHeight); - GlyphMetaList glyphMeta = PushFullListZero(arena, GlyphMetaList, bakedCharlist.length); real32 x, y; real32 lastX, lastY; @@ -93,6 +89,8 @@ Font createFont(Arena *arena, string ttfLocation, real32 lineHeight) { result.lineHeight = lineHeight; result.charWidth = bakedCharlist.data[0].xadvance; + //saveGrayscaleTarga(s("atlas.tga"), bakedFontBitmap, atlasWidth, atlasHeight); + glGenTextures(1, &result.texId); glBindTexture(GL_TEXTURE_2D, result.texId); glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, atlasWidth, atlasHeight, 0, GL_RED, GL_UNSIGNED_BYTE, bakedFontBitmap); diff --git a/src/gfx/geometry.c b/src/gfx/geometry.c index cad931f..6976188 100644 --- a/src/gfx/geometry.c +++ b/src/gfx/geometry.c @@ -1,20 +1,21 @@ #include "geometry.h" +#include "../common.h" #include "../lib/djstdlib/core.h" // Buffer layout: // X, Y, Z, U, V -real32 triangle_vertices[] = { - -0.5f, -0.5f, 0.0f, 1.0f, 1.0f, +const FloatList triangle_vertices = AsList(FloatList, { + -0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 0.5f, -0.5f, 0.0f, 0.5f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, -}; +}); -uint32 triangle_indices[] = { +const UInt32List triangle_indices = AsList(UInt32List, { 0, 1, 2 -}; +}); -real32 cube_vertices[] = { +const FloatList cube_vertices = AsList(FloatList, { -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, @@ -56,55 +57,46 @@ real32 cube_vertices[] = { 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f -}; +}); -uint32 cube_indices[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 -}; +const UInt32List cube_indices = AsList(UInt32List, { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 +}); -real32 square_xyz[] = { +const FloatList square_xyz = AsList(FloatList, { 200.0f, 200.0f, 0.0f, 200.0f, -200.0f, 0.0f, -200.0f, -200.0f, 0.0f, -200.0f, 200.0f, 0.0f, -}; +}); -real32 square_uv[] = { - 1.0f, 1.0f, +const FloatList square_uv = AsList(FloatList, { + 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, -}; +}); -uint32 square_indices[] = { - 0, 1, 3, +const UInt32List square_indices = AsList(UInt32List, { + 0, 1, 3, 1, 2, 3, -}; +}); const Shape TRIANGLE = { triangle_indices, - ArrayCount(triangle_indices), triangle_vertices, - ArrayCount(triangle_vertices), triangle_vertices, - ArrayCount(triangle_vertices), }; const Shape SQUARE = { square_indices, - ArrayCount(square_indices), square_uv, - ArrayCount(square_uv), square_xyz, - ArrayCount(square_xyz), }; const Shape CUBE = { cube_indices, - ArrayCount(cube_indices), triangle_vertices, - ArrayCount(triangle_vertices), triangle_vertices, - ArrayCount(triangle_vertices), }; diff --git a/src/gfx/geometry.h b/src/gfx/geometry.h index aad24f7..36f9396 100644 --- a/src/gfx/geometry.h +++ b/src/gfx/geometry.h @@ -1,15 +1,12 @@ #ifndef LEDDA_GEOMETRY_H #define LEDDA_GEOMETRY_H -#include "stddef.h" +#include "../common.h" typedef struct { - unsigned int* indices; - size_t indices_size; - float* uv; - size_t uv_size; - float* xyz; - size_t xyz_size; + UInt32List indices; + FloatList uv; + FloatList xyz; } Shape; extern const Shape TRIANGLE; diff --git a/src/main.c b/src/main.c index e1b213e..ceea7d6 100644 --- a/src/main.c +++ b/src/main.c @@ -143,7 +143,9 @@ GLFWwindow *initWindowAndGL(uint32 windowWidth, uint32 windowHeight) { if (!glfwInit()) { return NULL; } +#ifdef GLFW_WAYLAND_APP_ID glfwWindowHintString(GLFW_WAYLAND_APP_ID, "Somaesque"); +#endif glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); @@ -381,38 +383,49 @@ static void ui_Interaction(UI_Context *ui, Soma *soma) { } } -static void DJUI_Soma(UI_Context *ui, Soma *soma) { +static void ui_Soma(UI_Context *ui, Soma *soma) { RLVector4 darkgrey = {0.2, 0.2, 0.2, 1}; RLVector4 grey = {0.4, 0.4, 0.4, 1}; + RLVector4 lightgrey = {0.6, 0.6, 0.6, 1}; UI_Padding btnPad = {.left=10, .right=10, .top=5, .bottom=5}; + RLVector4 blueHighlight = {0.2, 0.2, 0.7, 1}; + RLVector4 lightblueHighlight = {0.3, 0.3, 0.8, 1}; + + UI_Rect btnRect = UI_RectAttr(.padding=btnPad, .color=grey, .borderRadius=5); + UI_Rect btnHoverRect = btnRect; + btnHoverRect.color = lightgrey; + + UI_Rect btnRectBlue = UI_RectAttr(.padding=btnPad, .color=blueHighlight, .borderRadius=5); + UI_Rect btnHoverRectBlue = btnRectBlue; + btnHoverRectBlue.color = lightblueHighlight; UI(.flags=UI_Flag_HeightGrow | UI_Flag_WidthGrow) { + UI_SetTxtAttr(.lineHeight=20, .fontSize=20); UI(.padding={.left=20, .right=20}, .color=darkgrey, .flags=UI_Flag_HeightGrow | UI_Flag_Vertical) { - UI(.padding=UI_PadUniform(10)) UI_Txt(s("Somaesque"), .lineHeight=26); + UI(.padding=UI_PadUniform(10)) UI_Txt(s("Somaesque"), .fontSize=26); ui_Interaction(ui, soma); } UI(.flags=UI_Flag_Vertical | UI_Flag_HeightGrow | UI_Flag_WidthGrow) { UI(.padding=UI_PadUniform(10), .color=darkgrey, .flags=UI_Flag_WidthGrow) { UI(.childGap=10) { - if (ui_Button(ui, UI_RectAttr(.padding=btnPad, .color=grey), UI_TxtAttr(s("Previous"), .lineHeight=20))) { + if (ui_ButtonWithHover(ui, btnRect, btnHoverRect, UI_TxtAttr(s("Previous")))) { advanceDisplayReverse(soma); } - if (ui_Button(ui, UI_RectAttr(.padding=btnPad, .color=grey), UI_TxtAttr(s("Next"), .lineHeight=20))) { + + if (ui_ButtonWithHover(ui, btnRect, btnHoverRect, UI_TxtAttr(s("Next")))) { advanceDisplay(soma); } } UI(.flags=UI_Flag_WidthGrow); UI(.childGap=10) { - if (ui_Button(ui, UI_RectAttr( - .padding=btnPad, - .color=soma->state.displayingSolutions ? grey : (RLVector4){0.2, 0.2, 0.7, 1}, - ), UI_TxtAttr(s("Design"), .lineHeight=20))) { + UI_Rect *rect = soma->state.displayingSolutions ? &btnRect : &btnRectBlue; + UI_Rect *hoverRect = soma->state.displayingSolutions ? &btnHoverRect : &btnHoverRectBlue; + if (ui_ButtonWithHover(ui, *rect, *hoverRect, UI_TxtAttr(s("Design")))) { soma->state.displayingSolutions = false; } - if (ui_Button(ui, UI_RectAttr( - .padding=btnPad, - .color=soma->state.displayingSolutions ? (RLVector4){0.2, 0.2, 0.7, 1} : grey, - ), UI_TxtAttr(s("Solve"), .lineHeight=20))) { + rect = soma->state.displayingSolutions ? &btnRectBlue : &btnRect; + hoverRect = soma->state.displayingSolutions ? &btnHoverRectBlue : &btnHoverRect; + if (ui_ButtonWithHover(ui, *rect, *hoverRect, UI_TxtAttr(s("Solve")))) { if (!soma->state.displayingSolutions) { tryScheduleSolve(soma); } else { @@ -422,18 +435,19 @@ static void DJUI_Soma(UI_Context *ui, Soma *soma) { } } UI(.flags=UI_Flag_Vertical | UI_Flag_WidthGrow | UI_Flag_HeightGrow | UI_Flag_3DScene) { + UI_SetTxtAttr(.fontSize=26, .alignment=UI_TxtAlign_Right); UI(.flags=UI_Flag_HeightGrow); - UI(.flags=UI_Flag_WidthGrow, .padding=UI_PadUniform(10)) { + UI(.padding=UI_PadUniform(10), .flags=UI_Flag_WidthGrow) { if (soma->solveTaskCtx.taskStatus == SolveTaskStatus_Solving) { - UI_Txt(s("Solving..."), .lineHeight=26); + UI_Txt(s("Solving...")); } else { bool soln = soma->state.displayingSolutions; - UI_TxtSeg(soln ? s("Solution") : s("Polycube"), .lineHeight=26); - UI_TxtSeg( - strPrintf(ui->arena, " #%d (%d total)", + UI_Txt( + strPrintf(ui->arena, "%S #%d (%d total)", + soln ? s("Solution") : s("Polycube"), (soln ? soma->state.displayedSolution : soma->state.displayedPolycube) + 1, soln ? soma->solutions.length : soma->polycubeInput.length), - .lineHeight=26); + ); } } } @@ -655,7 +669,7 @@ glDebugMessageCallback(glDebugCallback, NULL); Render(&renderer) { UI_Pass(&ui) { - DJUI_Soma(&ui, &soma); + ui_Soma(&ui, &soma); } if (ui.cursorIsPointer) { diff --git a/src/render.c b/src/render.c index f10c414..2bce095 100644 --- a/src/render.c +++ b/src/render.c @@ -7,14 +7,14 @@ static RenderObjects_Char createCharObjects(Arena *arena, size_t count) { result.begin.buf = PushFullList(arena, RLVec2List, count); result.glyph.buf = PushFullList(arena, IntList, count); - result.lineHeight.buf = PushFullList(arena, FloatList, count); + result.fontSize.buf = PushFullList(arena, FloatList, count); result.color.buf = PushFullList(arena, RLVec4List, count); glGenVertexArrays(1, &result.vao); glGenBuffers(1, &result.begin.bufId); glGenBuffers(1, &result.glyph.bufId); - glGenBuffers(1, &result.lineHeight.bufId); + glGenBuffers(1, &result.fontSize.bufId); glGenBuffers(1, &result.color.bufId); glBindVertexArray(result.vao); @@ -33,9 +33,9 @@ static RenderObjects_Char createCharObjects(Arena *arena, size_t count) { glVertexAttribDivisor(1, 1); glEnableVertexAttribArray(1); - 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); + bufItemSize = sizeof(result.fontSize.buf.data[0]); + glBindBuffer(GL_ARRAY_BUFFER, result.fontSize.bufId); + glBufferData(GL_ARRAY_BUFFER, result.fontSize.buf.length * bufItemSize, 0, GL_STREAM_DRAW); glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, bufItemSize, NULL); glVertexAttribDivisor(2, 1); glEnableVertexAttribArray(2); @@ -59,6 +59,7 @@ static RenderObjects_Rect createRectangleObjects(Arena *arena, size_t count) { result.color.buf = PushFullList(arena, RLVec4List, count); result.borderRadius.buf = PushFullList(arena, FloatList, count); result.borderThickness.buf = PushFullList(arena, FloatList, count); + result.borderColor.buf = PushFullList(arena, RLVec4List, count); result.edgeSoftness.buf = PushFullList(arena, FloatList, count); glGenVertexArrays(1, &result.vao); @@ -68,7 +69,7 @@ static RenderObjects_Rect createRectangleObjects(Arena *arena, size_t count) { glGenBuffers(1, &result.color.bufId); glGenBuffers(1, &result.borderRadius.bufId); glGenBuffers(1, &result.borderThickness.bufId); - glGenBuffers(1, &result.edgeSoftness.bufId); + glGenBuffers(1, &result.borderColor.bufId); glGenBuffers(1, &result.edgeSoftness.bufId); glBindVertexArray(result.vao); @@ -108,12 +109,19 @@ static RenderObjects_Rect createRectangleObjects(Arena *arena, size_t count) { glVertexAttribDivisor(4, 1); glEnableVertexAttribArray(4); + bufItemSize = sizeof(result.borderColor.buf.data[0]); + glBindBuffer(GL_ARRAY_BUFFER, result.borderColor.bufId); + glBufferData(GL_ARRAY_BUFFER, result.borderColor.buf.length * bufItemSize, 0, GL_STREAM_DRAW); + glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, bufItemSize, NULL); + glVertexAttribDivisor(5, 1); + glEnableVertexAttribArray(5); + 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); + glVertexAttribPointer(6, 1, GL_FLOAT, GL_FALSE, bufItemSize, NULL); + glVertexAttribDivisor(6, 1); + glEnableVertexAttribArray(6); return result; } @@ -126,7 +134,7 @@ 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.fontSize); GL_UpdateBuffer(r->chars.color); } @@ -137,6 +145,7 @@ static void updateRectangleObjectBuffers(Renderer *r) { GL_UpdateBuffer(r->rects.color); GL_UpdateBuffer(r->rects.borderRadius); GL_UpdateBuffer(r->rects.borderThickness); + GL_UpdateBuffer(r->rects.borderColor); GL_UpdateBuffer(r->rects.edgeSoftness); } @@ -156,11 +165,12 @@ void renderBegin(Renderer *r) { r->rects.color.buf.length = 0; r->rects.borderRadius.buf.length = 0; r->rects.borderThickness.buf.length = 0; + r->rects.borderColor.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.fontSize.buf.length = 0; r->chars.color.buf.length = 0; r->sceneWidth = 0; @@ -237,7 +247,7 @@ void renderEnd(Renderer *r) { glDisable(GL_BLEND); } -void rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real32 height, RLVector4 color, real32 borderRadius, real32 borderThickness) { +void rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real32 height, RLVector4 color, real32 borderRadius, real32 borderThickness, RLVector4 borderColor) { x = roundf(x); y = roundf(y); width = roundf(width); @@ -247,16 +257,17 @@ void rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real3 ListAppend(r->rects.color.buf, color); ListAppend(r->rects.borderRadius.buf, borderRadius); ListAppend(r->rects.borderThickness.buf, borderThickness); + ListAppend(r->rects.borderColor.buf, borderColor); 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; +void rendererPlaceString(Renderer *r, string s, real32 x, real32 y, RLVector4 color, real32 fontSize) { + real32 ratio = fontSize / 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.fontSize.buf, ratio); ListAppend(r->chars.color.buf, (color)); } } diff --git a/src/render.h b/src/render.h index 572a66d..6707341 100644 --- a/src/render.h +++ b/src/render.h @@ -41,6 +41,7 @@ struct RenderObjects_Rect { RenderObject_BufferVec4 color; RenderObject_BufferFloat borderRadius; RenderObject_BufferFloat borderThickness; + RenderObject_BufferVec4 borderColor; RenderObject_BufferFloat edgeSoftness; }; @@ -50,7 +51,7 @@ struct RenderObjects_Char { uint64 count; RenderObject_BufferVec2 begin; RenderObject_BufferInt32 glyph; - RenderObject_BufferFloat lineHeight; + RenderObject_BufferFloat fontSize; RenderObject_BufferVec4 color; }; @@ -91,7 +92,7 @@ struct Renderer { 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 rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real32 height, RLVector4 color, real32 borderRadius, real32 borderThickness, RLVector4 borderColor); void rendererPlaceString(Renderer *r, string s, real32 x, real32 y, RLVector4 color, real32 fontSize); #define Render(r) DeferLoop(renderBegin((r)), renderEnd((r))) diff --git a/src/ui.c b/src/ui.c index daf14f1..9a290ab 100644 --- a/src/ui.c +++ b/src/ui.c @@ -52,24 +52,50 @@ UI_Context ui_initContext(Arena *arena, Renderer *renderer) { .arena=arena, .rects=list, .prevRects=prevList, - .hotNode = 0, - .cursorIsPointer = false, - .input = NULL, - .prevInput = NULL, - .renderer = renderer, + .hotNode=0, + .prevHotNode=0, + .cursorIsPointer=false, + .scene3DHandle=0, + .input=NULL, + .prevInput=NULL, + .renderer=renderer, }; } void ui_placeText(UI_Context *ui, UI_RectStr strData) { + UI_Rect *currRect = &ui->rects.data[ui->currRect]; UI_RectStr *strDataCopy = PushStruct(ui->arena, UI_RectStr); *strDataCopy = strData; - if (strDataCopy->lineHeight == -1) { - strDataCopy->lineHeight = ui->renderer->activeFont->lineHeight; + if (strDataCopy->fontSize == -1) { + strDataCopy->fontSize = currRect->inheritedTextAttr->fontSize; + if (strDataCopy->fontSize == -1) { + strDataCopy->fontSize = ui->renderer->activeFont->lineHeight; + } + } + if (strDataCopy->lineHeight == -1) { + strDataCopy->lineHeight = currRect->inheritedTextAttr->lineHeight; + if (strDataCopy->lineHeight == -1) { + strDataCopy->lineHeight = strDataCopy->fontSize; + } + if (strDataCopy->fontSize > strDataCopy->lineHeight) { + strDataCopy->lineHeight = strDataCopy->fontSize; + } + } + if (strDataCopy->alignment < 0) { + if (currRect->inheritedTextAttr) { + strDataCopy->alignment = currRect->inheritedTextAttr->alignment; + } else { + strDataCopy->alignment = 0; + } + } + currRect->inheritedTextAttr = strDataCopy; + if (strData.s.str != NULL) { + strDataCopy->s = PushString(ui->arena, strData.s.length); + memcpy(strDataCopy->s.str, strData.s.str, strData.s.length); + currRect->stringData = strDataCopy; + } else { + // NOTE(djledda): Not real text data if empty string -> just style attributes. Ignore } - 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) { @@ -203,17 +229,28 @@ void ui_end(UI_Context *ui) { rect->resolvedHeight, rect->color, rect->borderRadius, - rect->borderThickness + rect->borderThickness, + rect->borderColor ); if (rect->stringData) { UI_RectStr *str = rect->stringData; + real32 alignmentXOffset = 0; + real32 alignmentYOffset = rect->padding.top; + real32 textWidth = str->s.length * str->fontSize / ui->renderer->activeFont->lineHeight * ui->renderer->activeFont->charWidth; + if (str->alignment & UI_TxtAlign_Right) { + alignmentXOffset = rect->resolvedWidth - rect->padding.right - textWidth; + } else if (str->alignment & UI_TxtAlign_Center) { + alignmentXOffset = rect->resolvedWidth/2.0 - textWidth/2.0; + } else { + alignmentXOffset = rect->padding.left; + } rendererPlaceString( ui->renderer, str->s, - rect->x + str->xOffset + rect->padding.left, - rect->y + str->yOffset + rect->padding.top, + rect->x + alignmentXOffset + str->xOffset, + rect->y + alignmentYOffset + str->yOffset, str->color, - str->lineHeight + str->fontSize ); } } @@ -257,6 +294,15 @@ void ui_openElement(UI_Context *ui, UI_Rect rect) { if (newRect->flags & UI_Flag_3DScene) { ui->scene3DHandle = newHandle; } + + if (currRect->stringData) { + UI_RectStr *inheritedTextAttr = PushStructZero(ui->arena, UI_RectStr); + *inheritedTextAttr = *currRect->stringData; + inheritedTextAttr->s = s(""); + newRect->inheritedTextAttr = inheritedTextAttr; + } else { + newRect->inheritedTextAttr = currRect->inheritedTextAttr; + } } void ui_closeElement(UI_Context *ui) { @@ -267,7 +313,7 @@ void ui_closeElement(UI_Context *ui) { currRect->resolvedWidth = currRect->width; } else if (currRect->minWidth == -1) { if (currRect->stringData) { - real32 charWidth = currRect->stringData->lineHeight / ui->renderer->activeFont->lineHeight * ui->renderer->activeFont->charWidth; + real32 charWidth = currRect->stringData->fontSize / ui->renderer->activeFont->lineHeight * ui->renderer->activeFont->charWidth; currRect->minWidth = charWidth*currRect->stringData->s.length; } else { currRect->minWidth = 0; @@ -318,11 +364,15 @@ void ui_closeElement(UI_Context *ui) { ui->currRect = ui->rects.data[ui->currRect].parent; } -bool ui_Button(UI_Context *ui, UI_Rect rect, UI_RectStr textAttr) { +/** + * Returns whether the checkbox was clicked + */ +bool ui_ButtonWithHover(UI_Context *ui, UI_Rect rect, UI_Rect hovered, 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])) { + bool inRect = pointInRect(ui->input->mouse.point.x, ui->input->mouse.point.y, ui->prevRects.data[id]); + if (inRect) { ui->cursorIsPointer = true; if (ui->prevHotNode == id && !ui->input->mouse.btnLeft) { clicked = true; @@ -331,21 +381,32 @@ bool ui_Button(UI_Context *ui, UI_Rect rect, UI_RectStr textAttr) { } } - rect.borderRadius = 5; - UI_FromRect(rect) { - ui_placeText(ui, textAttr); + bool useHover = inRect && !(hovered.flags & UI_Flag_Ignore); + UI_Rect *rectToRender = useHover ? &hovered : ▭ + if (rectToRender->borderRadius == -1) { + rectToRender->borderRadius = 5; } + UI_FromRect(*rectToRender) ui_placeText(ui, textAttr); + return clicked; } +/** + * Returns whether the checkbox was clicked + */ +bool ui_Button(UI_Context *ui, UI_Rect rect, UI_RectStr textAttr) { + return ui_ButtonWithHover(ui, rect, UI_RectAttr(.flags=UI_Flag_Ignore), textAttr); +} + /** * Returns whether the checkbox was clicked */ bool ui_CheckboxRect(UI_Context *ui, bool *value, UI_Rect rect) { int32 id = UI_NextID(); bool clicked = false; - if (pointInRect(ui->input->mouse.point.x, ui->input->mouse.point.y, ui->prevRects.data[id])) { + bool pointerInRect = pointInRect(ui->input->mouse.point.x, ui->input->mouse.point.y, ui->prevRects.data[id]); + if (pointerInRect) { ui->cursorIsPointer = true; if (ui->prevHotNode == id && !ui->input->mouse.btnLeft) { *value = !*value; @@ -357,11 +418,25 @@ bool ui_CheckboxRect(UI_Context *ui, bool *value, UI_Rect rect) { if (*value) { rect.borderRadius = 5; rect.borderThickness = 0; + if (pointerInRect) { + rect.color.x += 0.5; + rect.color.y += 0.5; + rect.color.z += 0.5; + rect.color.x = rect.color.x <= 1.0 ? rect.color.x : 1.0; + rect.color.y = rect.color.y <= 1.0 ? rect.color.y : 1.0; + rect.color.z = rect.color.z <= 1.0 ? rect.color.z : 1.0; + } UI_FromRect(rect); } else { rect.borderRadius = 5; rect.borderThickness = 2; - rect.color = COLOR_WHITE; + rect.borderColor = COLOR_WHITE; + if (pointerInRect) { + rect.color = COLOR_WHITE; + rect.color.w = 0.2; + } else { + rect.color = (RLVector4){0,0,0,0}; + } UI_FromRect(rect); } diff --git a/src/ui.h b/src/ui.h index d31ea30..2e6ac19 100644 --- a/src/ui.h +++ b/src/ui.h @@ -42,8 +42,7 @@ enum UI_Flag { UI_Flag_Vertical=1<<2, // Default is horizontal UI_Flag_3DScene=1<<3, UI_Flag_Center=1<<4, - UI_Flag_Textalign_Right=1<<5, // Default is left - UI_Flag_Textalign_Center=1<<6, + UI_Flag_Ignore=1<<7, // For optional parameters // .. UI_Flag_COUNT, }; @@ -56,19 +55,25 @@ struct UI_Padding { real32 left; }; +enum UI_TxtAlign { + UI_TxtAlign_Left=1<<0, // Default + UI_TxtAlign_Center=1<<1, + UI_TxtAlign_Right=1<<2, +}; + typedef struct UI_RectStr UI_RectStr; struct UI_RectStr { string s; real32 xOffset; real32 yOffset; real32 lineHeight; + real32 fontSize; RLVector4 color; - int8 alignment; + enum UI_TxtAlign alignment; }; typedef struct UI_Rect UI_Rect; struct UI_Rect { - /** UI_Rect_LayoutFlag */ enum UI_Flag flags; int32 parent; @@ -102,6 +107,13 @@ struct UI_Rect { real32 resolvedHeight; UI_RectStr *stringData; + /** + * Inherited from parent to propagate attributes. Actual string is ignored, only used for filling out defaults in + * the next stringData pointer that is set. + * This pointer is always set, as the global UI context will set it on the root not if it does not present its own + * text data. + */ + UI_RectStr *inheritedTextAttr; }; DefineList(UI_Rect, UI_Rect); @@ -135,23 +147,47 @@ 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); +bool ui_Button(UI_Context *ui, UI_Rect rect, UI_RectStr textAttr); +bool ui_ButtonWithHover(UI_Context *ui, UI_Rect rect, UI_Rect hovered, UI_RectStr textAttr); extern UI_Context *__UI_current_ctx__; #if 0 -#define UI_WIREFRAME .color=COLOR_RED, .borderThickness=1, +#define UI_DEBUG .borderColor=COLOR_RED, .borderThickness=1, #else -#define UI_WIREFRAME +#define UI_DEBUG #endif -#define UI(...) DeferLoop(ui_openElement((__UI_current_ctx__), UI_RectAttr(__VA_ARGS__)), ui_closeElement((__UI_current_ctx__))) -#define UI_RectAttr(...) ((UI_Rect){UI_WIREFRAME .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_current_ctx__)->rects.length) -#define UI_Txt(str, ...) (ui_placeText((__UI_current_ctx__), ((UI_RectStr){ .xOffset=0, .yOffset=0, .lineHeight=-1, .s=(str), __VA_ARGS__ }))) -#define UI_TxtSeg(str, ...) UI() (UI_Txt((str), __VA_ARGS__)) -#define UI_TxtAttr(str, ...) ((UI_RectStr){ .xOffset=0, .yOffset=0, .lineHeight=-1, .s=(str), __VA_ARGS__ }) + +#define UI(...)\ + /** Place a UI_Rect defined in-place in the current position in the UI tree */\ + DeferLoop(ui_openElement((__UI_current_ctx__), UI_RectAttr(__VA_ARGS__)), ui_closeElement((__UI_current_ctx__))) + +#define UI_RectAttr(...)\ + /** Generate a UI_Rect to be passed as a value */\ + ((UI_Rect){UI_DEBUG .minHeight=-1, .minWidth=-1, .width=-1, .height=-1, __VA_ARGS__}) + +#define UI_FromRect(rect)\ + /** Place a UI_Rect in the current position in the UI tree, as defined by an existing value */\ + DeferLoop(ui_openElement((__UI_current_ctx__), (rect)), ui_closeElement((__UI_current_ctx__))) + +#define UI_TxtAttr(str, ...)\ + /** Generate UI text to be used as a value */\ + ((UI_RectStr){ .alignment=-1, .xOffset=0, .yOffset=0, .fontSize=-1, .lineHeight=-1, .s=(str), __VA_ARGS__ }) + +#define UI_Txt(str, ...)\ + /** Place UI text in the current position in the UI tree. Attaches to the current UI_Rect */\ + (ui_placeText((__UI_current_ctx__), (UI_TxtAttr(str, __VA_ARGS__)))) + +#define UI_SetTxtAttr(...)\ + /** Attach "phantom" text to the current rect in order to set its heritable attributes for subsequent text in the UI subtrees */\ + (ui_placeText((__UI_current_ctx__), (UI_TxtAttr(((string){.str=NULL,.length=0}), __VA_ARGS__)))) + +#define UI_TxtSeg(str, ...)\ + /** Place a text fragment in its own box (useful for placing many text fragments in a row */\ + UI() (UI_Txt((str), __VA_ARGS__)) #endif