diff --git a/build b/build index e2bd750..e5f68bb 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 -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 diff --git a/src/gfx/Texture.c b/src/gfx/Texture.c index d4fec4b..b6c0dc9 100644 --- a/src/gfx/Texture.c +++ b/src/gfx/Texture.c @@ -1,4 +1,6 @@ #include "Texture.h" +#include +#include 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); diff --git a/src/main.c b/src/main.c index d92dfc8..e1b213e 100644 --- a/src/main.c +++ b/src/main.c @@ -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(¤tPolycube->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); diff --git a/src/render.c b/src/render.c index ca4ddae..f10c414 100644 --- a/src/render.c +++ b/src/render.c @@ -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); diff --git a/src/ui.c b/src/ui.c index f948b37..daf14f1 100644 --- a/src/ui.c +++ b/src/ui.c @@ -1,8 +1,6 @@ #include "ui.h" #include "GLFW/glfw3.h" #include "lib/djstdlib/core.h" -#include -#include 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; diff --git a/src/ui.h b/src/ui.h index f836161..d31ea30 100644 --- a/src/ui.h +++ b/src/ui.h @@ -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