This commit is contained in:
2026-02-25 08:36:11 +01:00
parent 06f784d0b1
commit 6a1392b9b6
6 changed files with 128 additions and 63 deletions

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 -O0 -g -g2 $COMMON_FLAGS -DDJSTDLIB_DEBUG=1 ./src/main.c -o ./target/somaesque $LIB_INCLUDE
time clang -O1 -g -g2 $COMMON_FLAGS -O1 -fsanitize=address,undefined -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

@@ -1,4 +1,6 @@
#include "Texture.h"
#include <asm-generic/errno-base.h>
#include <errno.h>
Texture createTexture(const char* bitmap, int32 width, int32 height) {
Texture result = {0};
@@ -20,16 +22,44 @@ Texture createTexture(const char* bitmap, int32 width, int32 height) {
DefineList(stbtt_bakedchar, STBBakedChar);
static void saveGrayscaleTarga(string location, const char *bytes, int width, int height) {
char tgaHeader[18] = {
// Image ID length
0x0,
// Color map type
0x0,
// Image type
0x3,
// Color map info (unused)
0x0, 0x0, 0x0, 0x0, 0x0,
// Image spec
0x0, 0x0, // x origin
0x0, 0x0, // y origin
width & 0xFF, (width >> 8) & 0xFF, // width
height & 0xFF, (height >> 8) & 0xFF, // width
0x8, // bits per px (depth)
0b00001000, // image descriptor
};
os_writeEntireFile(location, tgaHeader, ArrayCount(tgaHeader));
os_fileAppend(location, bytes, width * height);
}
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;
int32 atlasWidth = 512;//16 * lineHeight;
int32 atlasHeight = 512;// * lineHeight;
STBBakedCharList bakedCharlist = PushFullListZero(arena, STBBakedCharList, CODEPOINT_END - CODEPOINT_START);
string fontFile = os_readEntireFile(arena, ttfLocation);
stbtt_fontinfo info;
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);
char *bakedFontBitmap = PushArrayZero(arena, char, atlasWidth*atlasHeight);
int32 bake_result = stbtt_BakeFontBitmap(
(unsigned char *)fontFile.str, 0,
@@ -38,6 +68,8 @@ 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;
@@ -48,7 +80,7 @@ Font createFont(Arena *arena, string ttfLocation, real32 lineHeight) {
.uv0=(RLVector2){q.s0, q.t1},
.uv1=(RLVector2){q.s1, q.t0},
.xOffset=q.x0 - lastX,
.yOffset=q.y0 + lineHeight,
.yOffset=q.y0 + lineHeight + scale * descent,
.width=q.x1-q.x0,
.height=q.y1-q.y0,
};
@@ -63,7 +95,6 @@ Font createFont(Arena *arena, string ttfLocation, real32 lineHeight) {
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);

View File

@@ -140,16 +140,15 @@ void framebufferSizeCallback(GLFWwindow *window, int width, int height) {
}
GLFWwindow *initWindowAndGL(uint32 windowWidth, uint32 windowHeight) {
#ifdef OS_LINUX
glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11);
#endif
glfwInit();
//glfwWindowHintString(GLFW_WAYLAND_APP_ID, "Somaesque");
if (!glfwInit()) {
return NULL;
}
glfwWindowHintString(GLFW_WAYLAND_APP_ID, "Somaesque");
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_SAMPLES, 4);
GLFWwindow *window = glfwCreateWindow(
windowWidth,
windowHeight,
@@ -174,11 +173,11 @@ GLFWwindow *initWindowAndGL(uint32 windowWidth, uint32 windowHeight) {
}
glViewport(0, 0, windowWidth, windowHeight);
glfwSwapInterval(0);
glfwSetFramebufferSizeCallback(window, framebufferSizeCallback);
glfwSwapInterval(1);
glfwSetInputMode(window, GLFW_CURSOR | GLFW_RAW_MOUSE_MOTION, GLFW_CURSOR_NORMAL);
glfwSetWindowSize(window, windowWidth, windowHeight);
glEnable(GL_DEPTH_TEST);
glEnable(GL_MULTISAMPLE);
return window;
}
@@ -365,7 +364,7 @@ static void ui_Interaction(UI_Context *ui, Soma *soma) {
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(
if (ui_CheckboxRect(ui, &cellActive, UI_RectAttr(
.width = boxSize,
.height = boxSize,
.borderRadius = 2,
@@ -385,39 +384,35 @@ static void ui_Interaction(UI_Context *ui, Soma *soma) {
static void DJUI_Soma(UI_Context *ui, Soma *soma) {
RLVector4 darkgrey = {0.2, 0.2, 0.2, 1};
RLVector4 grey = {0.4, 0.4, 0.4, 1};
UI_Padding btnPad = {.left=10, .right=10, .top=5, .bottom=5};
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(.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_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(.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(.height=30, .padding={.left=10, .right=10}, .color=grey), UI_CreateText(s("Next"), .lineHeight=20))) {
advanceDisplay(soma);
}
UI(.childGap=10) {
if (ui_Button(ui, UI_RectAttr(.padding=btnPad, .color=grey), UI_TxtAttr(s("Previous"), .lineHeight=20))) {
advanceDisplayReverse(soma);
}
if (ui_Button(ui, UI_RectAttr(.padding=btnPad, .color=grey), UI_TxtAttr(s("Next"), .lineHeight=20))) {
advanceDisplay(soma);
}
}
UI(.flags=UI_Flag_WidthGrow);
UI(.childGap=10) {
if (ui_Button(ui, UI_CreateRect(
.width=30,
.height=30,
.padding={.left=10, .right=10},
if (ui_Button(ui, UI_RectAttr(
.padding=btnPad,
.color=soma->state.displayingSolutions ? grey : (RLVector4){0.2, 0.2, 0.7, 1},
), UI_CreateText(s("Design"), .lineHeight=20))) {
), UI_TxtAttr(s("Design"), .lineHeight=20))) {
soma->state.displayingSolutions = false;
}
if (ui_Button(ui, UI_CreateRect(
.width=30,
.height=30,
.padding={.left=10, .right=10},
if (ui_Button(ui, UI_RectAttr(
.padding=btnPad,
.color=soma->state.displayingSolutions ? (RLVector4){0.2, 0.2, 0.7, 1} : grey,
), UI_CreateText(s("Solve"), .lineHeight=20))) {
), UI_TxtAttr(s("Solve"), .lineHeight=20))) {
if (!soma->state.displayingSolutions) {
tryScheduleSolve(soma);
} else {
@@ -426,7 +421,22 @@ static void DJUI_Soma(UI_Context *ui, Soma *soma) {
}
}
}
UI(.flags=UI_Flag_WidthGrow | UI_Flag_HeightGrow | UI_Flag_3DScene);
UI(.flags=UI_Flag_Vertical | UI_Flag_WidthGrow | UI_Flag_HeightGrow | UI_Flag_3DScene) {
UI(.flags=UI_Flag_HeightGrow);
UI(.flags=UI_Flag_WidthGrow, .padding=UI_PadUniform(10)) {
if (soma->solveTaskCtx.taskStatus == SolveTaskStatus_Solving) {
UI_Txt(s("Solving..."), .lineHeight=26);
} else {
bool soln = soma->state.displayingSolutions;
UI_TxtSeg(soln ? s("Solution") : s("Polycube"), .lineHeight=26);
UI_TxtSeg(
strPrintf(ui->arena, " #%d (%d total)",
(soln ? soma->state.displayedSolution : soma->state.displayedPolycube) + 1,
soln ? soma->solutions.length : soma->polycubeInput.length),
.lineHeight=26);
}
}
}
}
}
}
@@ -499,6 +509,13 @@ static void updateWindow(Soma *soma) {
const int32 MAX_POLYCUBE_INPUT = 64;
const int32 TARGET_FPS = 144;
void APIENTRY glDebugCallback(GLenum source, GLenum type, GLuint id,
GLenum severity, GLsizei length,
const GLchar* message, const void* userParam)
{
fprintf(stderr, "GL: %s\n", message);
}
int mainGfx() {
Arena *arena = arenaAlloc(Megabytes(128));
Arena *solutionsArena = arenaAlloc(Megabytes(128));
@@ -510,6 +527,10 @@ int mainGfx() {
return 1;
}
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(glDebugCallback, NULL);
Scene mainScene = createScene(arena);
Camera cam = createCamera(winWidth, winHeight);
Renderer renderer = createRenderer(arena, &mainScene, &cam, createSceneGraphNode(&mainScene));
@@ -564,6 +585,7 @@ int mainGfx() {
};
glfwSetWindowUserPointer(windowHandle, &soma);
glfwSetFramebufferSizeCallback(windowHandle, framebufferSizeCallback);
glfwSetScrollCallback(windowHandle, onMouseScroll);
VoxelSpaceReprList stdSoma = AsList(VoxelSpaceReprList, STD_SOMA);

View File

@@ -130,7 +130,6 @@ static void updateCharObjectBuffers(Renderer *r) {
GL_UpdateBuffer(r->chars.color);
}
static void updateRectangleObjectBuffers(Renderer *r) {
glBindVertexArray(r->rects.vao);
GL_UpdateBuffer(r->rects.p0);
@@ -170,6 +169,8 @@ void renderBegin(Renderer *r) {
r->sceneY = 0;
}
#define CHECK() do{ GLenum e=glGetError(); if(e) printf("GL err 0x%x at %s:%d\n",e,__FILE__,__LINE__); }while(0)
void renderEnd(Renderer *r) {
// 3D Scene
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
@@ -222,22 +223,25 @@ void renderEnd(Renderer *r) {
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_TEXTURE0); CHECK();
glBindTexture(GL_TEXTURE_2D, r->activeFont->texId); CHECK();
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, r->activeFont->glyphTableTexId);
setUniform1i(r->textShader, "glyph_table", 1);
glActiveTexture(GL_TEXTURE1); CHECK();
glBindTexture(GL_TEXTURE_BUFFER, r->activeFont->glyphTableTexId); CHECK();
setUniform1i(r->textShader, "glyph_table", 1); CHECK();
glBindVertexArray(r->chars.vao);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, r->chars.begin.buf.length);
glBindVertexArray(r->chars.vao); CHECK();
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, r->chars.begin.buf.length); CHECK();
glDisable(GL_BLEND);
}
void rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real32 height, RLVector4 color, real32 borderRadius, real32 borderThickness) {
x = roundf(x);
y = roundf(y);
width = roundf(width);
height = roundf(height);
ListAppend(r->rects.p0.buf, ((RLVector2){ x, y }));
ListAppend(r->rects.p1.buf, ((RLVector2){ x + width, y + height }));
ListAppend(r->rects.color.buf, color);

View File

@@ -1,8 +1,6 @@
#include "ui.h"
#include "GLFW/glfw3.h"
#include "lib/djstdlib/core.h"
#include <stdio.h>
#include <string.h>
UI_Context *__UI_current_ctx__ = NULL;
@@ -186,14 +184,15 @@ void ui_begin(UI_Context *ui) {
void ui_end(UI_Context *ui) {
ui_closeElement(ui);
// 1. Calculate layout
ui_sizingPass(ui, true, ui->rootRect);
ui_sizingPass(ui, false, ui->rootRect);
// 1) a. Calculate layout
ui_sizingPass(ui, true, ui->rootRect); // x
ui_sizingPass(ui, false, ui->rootRect); // y
ui_calcLayout(ui, true, ui->rootRect);
ui_calcLayout(ui, false, ui->rootRect);
// 1) b.
ui_calcLayout(ui, true, ui->rootRect); // x
ui_calcLayout(ui, false, ui->rootRect); // y
// 2. Create render commands:
// 2) Create render commands:
for (EachEl(ui->rects, UI_Rect, rect)) {
rendererPlaceRectangle(
@@ -211,8 +210,8 @@ void ui_end(UI_Context *ui) {
rendererPlaceString(
ui->renderer,
str->s,
rect->x + str->xOffset,
rect->y + str->yOffset,
rect->x + str->xOffset + rect->padding.left,
rect->y + str->yOffset + rect->padding.top,
str->color,
str->lineHeight
);
@@ -266,8 +265,7 @@ void ui_closeElement(UI_Context *ui) {
if (currRect->width != -1) {
currRect->resolvedWidth = currRect->width;
}
if (currRect->minWidth == -1) {
} else 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;
@@ -278,7 +276,7 @@ void ui_closeElement(UI_Context *ui) {
currRect->resolvedWidth = currRect->padding.left + currRect->padding.right + (currRect->resolvedWidth < currRect->minWidth ? currRect->minWidth : currRect->resolvedWidth);
if (currRect->height != -1) {
currRect->resolvedHeight = currRect->height;
currRect->resolvedHeight = currRect->height - currRect->padding.top - currRect->padding.bottom;
} else if (currRect->minHeight == -1) {
if (currRect->stringData) {
currRect->minHeight = currRect->stringData->lineHeight;

View File

@@ -38,10 +38,12 @@ struct Input {
enum UI_Flag {
UI_Flag_WidthGrow=1<<0, // Default is fixed
UI_Flag_HeightGrow=1<<1,
UI_Flag_HeightGrow=1<<1, // Default is fixed
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_COUNT,
};
@@ -61,12 +63,13 @@ struct UI_RectStr {
real32 yOffset;
real32 lineHeight;
RLVector4 color;
int8 alignment;
};
typedef struct UI_Rect UI_Rect;
struct UI_Rect {
/** UI_Rect_LayoutFlag */
uint64 flags;
enum UI_Flag flags;
int32 parent;
int32 firstChild;
@@ -135,13 +138,20 @@ bool ui_CheckboxRect(UI_Context *ui, bool *value, UI_Rect rect);
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__})
#if 0
#define UI_WIREFRAME .color=COLOR_RED, .borderThickness=1,
#else
#define UI_WIREFRAME
#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_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__ })
#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__ })
#endif