This commit is contained in:
Daniel Ledda
2026-03-06 01:36:29 +01:00
parent aba462447d
commit e768b38322
14 changed files with 298 additions and 186 deletions

View File

@@ -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;
};

View File

@@ -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;
}

View File

@@ -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;

2
build
View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -1,6 +1,4 @@
#include "Texture.h"
#include <asm-generic/errno-base.h>
#include <errno.h>
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);

View File

@@ -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),
};

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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));
}
}

View File

@@ -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)))

121
src/ui.c
View File

@@ -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 : &rect;
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);
}

View File

@@ -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