From ff41c8ace44624e8e84ee713e9c3a52249bbf0bd Mon Sep 17 00:00:00 2001 From: Daniel Ledda Date: Sun, 23 Nov 2025 15:30:05 +0100 Subject: [PATCH] big progress --- .clangd | 2 + build | 5 +- src/SomaSolve.c | 1 + src/main.c | 490 +++++++++++++++++++++++++++++++--------------- src/world/scene.c | 57 +++--- src/world/scene.h | 5 +- 6 files changed, 372 insertions(+), 188 deletions(-) diff --git a/.clangd b/.clangd index 5c3112b..75a5926 100644 --- a/.clangd +++ b/.clangd @@ -4,6 +4,8 @@ CompileFlags: - -xc # LINUX FLAGS - -DOS_LINUX + - -DCOMPOSITOR_WAYLAND + - -DDJSTDLIB_DEBUG # WINDOW FLAGS # - -DOS_WINDOWS # - "-IC:\\source\\libs\\include" diff --git a/build b/build index fbc481b..e365d9c 100755 --- a/build +++ b/build @@ -1,12 +1,13 @@ #!/bin/bash LIB_INCLUDE="-lglfw -lGL -lm" +COMMON_FLAGS="-DOS_LINUX=1 -DCOMPOSITOR_WAYLAND=1 -xc -std=c99" echo [Building target] if [ $DEBUG ]; then - time clang -O0 -g -g2 -DOS_LINUX=1 -DDJSTDLIB_DEBUG=1 -xc -std=c99 ./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 -DOS_LINUX=1 -xc -std=c99 ./src/main.c -o ./target/somaesque $LIB_INCLUDE + time clang -O2 $COMMON_FLAGS ./src/main.c -o ./target/somaesque $LIB_INCLUDE fi echo [Target built] ./target/somaesque diff --git a/src/SomaSolve.c b/src/SomaSolve.c index 099505c..3e47035 100644 --- a/src/SomaSolve.c +++ b/src/SomaSolve.c @@ -3,6 +3,7 @@ #include "SomaSolve.h" #include "math.h" #include +#include /* void get_dims_input(int dims[3]) { diff --git a/src/main.c b/src/main.c index fdcaa25..8131541 100644 --- a/src/main.c +++ b/src/main.c @@ -10,6 +10,7 @@ #include "VoxelSpace.c" #include "./tests.c" #include "lib/djstdlib/core.c" +#include "pthread.h" // Graphics bindings and libs #include "lib/raymath.h" @@ -114,6 +115,7 @@ struct Input { bool x; bool y; bool z; + bool w; } keyboard; struct { union { @@ -142,8 +144,46 @@ struct PolycubeInput { }; DefineList(PolycubeInput, PolycubeInput); +typedef struct UI_Context UI_Context; +struct UI_Context { + uint32 nextId; + uint32 hotNode; + uint32 prevHotNode; + Renderer *renderer; + Input *prevInput; + Input *input; + bool cursorIsPointer; +}; + +typedef struct UI_Rect UI_Rect; +struct UI_Rect { + real32 x; + real32 y; + real32 width; + real32 height; + real32 borderRadius; + RLVector4 color; +}; + +typedef enum SolveTaskStatus SolveTaskStatus; +enum SolveTaskStatus { + SolveTaskStatus_Ready, + SolveTaskStatus_Solving, + SolveTaskStatus_Complete, + SolveTaskStatus_Error, +}; +typedef struct SolveTaskCtx SolveTaskCtx; +struct SolveTaskCtx { + Arena *arena; + SomaSolutionList solutions; + VoxelSpaceReprList input; + int dims[3]; + SolveTaskStatus taskStatus; +}; + typedef struct SomaState SomaState; struct SomaState { + bool isInitialState; bool wireframe; bool polycubeDirty; uint32 displayedPolycube; @@ -164,6 +204,10 @@ struct Soma { GLFWwindow *handle; uint32 width; uint32 height; + struct { + GLFWcursor *pointer; + GLFWcursor *arrow; + } cursors; } window; SomaState prevState; @@ -172,9 +216,42 @@ struct Soma { PolycubeInputList polycubeInput; HandleList polycubes; SomaSolutionList solutions; + Arena *solveArena; + SolveTaskCtx *solveTaskCtx; + uint32 puzzleDims[3]; HandleList solutionEntities; }; +void *executeSolve(void *ctx) { + SolveTaskCtx *solveTaskCtx = (SolveTaskCtx *)ctx; + solveTaskCtx->taskStatus = SolveTaskStatus_Solving; + solveTaskCtx->solutions = solveSoma(solveTaskCtx->arena, solveTaskCtx->input, solveTaskCtx->dims); + solveTaskCtx->taskStatus = SolveTaskStatus_Complete; + return NULL; +} + +SolveTaskCtx *scheduleSolve(Soma *soma) { + SolveTaskCtx *threadCtx = PushStructZero(soma->solveArena, SolveTaskCtx); + VoxelSpaceReprList mappedInputs = PushList(soma->solveArena, VoxelSpaceReprList, soma->polycubeInput.length); + for (EachEl(soma->polycubeInput, PolycubeInput, polycubeInput)) { + AppendList(&mappedInputs, polycubeInput->repr.space); + } + *threadCtx = (SolveTaskCtx){ + .arena = soma->solveArena, + .solutions = EmptyList(), + .input = mappedInputs, + .taskStatus = SolveTaskStatus_Ready, + .dims = { soma->puzzleDims[0], soma->puzzleDims[1], soma->puzzleDims[2] }, + }; + soma->solveTaskCtx = threadCtx; + pthread_t threadId; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_create(&threadId, &attr, &executeSolve, threadCtx); + pthread_attr_destroy(&attr); + return threadCtx; +} + void show(Scene *s, uint32 graphNodeHandle) { SceneGraphNode *node = getSceneGraphNode(s, graphNodeHandle); if (node->entityHandle) { @@ -223,10 +300,16 @@ 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"); 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); GLFWwindow *window = glfwCreateWindow( windowWidth, windowHeight, @@ -242,6 +325,7 @@ GLFWwindow *initWindowAndGL(uint32 windowWidth, uint32 windowHeight) { glfwTerminate(); return NULL; } + glfwMakeContextCurrent(window); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { @@ -253,7 +337,13 @@ GLFWwindow *initWindowAndGL(uint32 windowWidth, uint32 windowHeight) { glfwSwapInterval(0); glfwSetFramebufferSizeCallback(window, framebufferSizeCallback); glfwSetInputMode(window, GLFW_CURSOR | GLFW_RAW_MOUSE_MOTION, GLFW_CURSOR_NORMAL); + glfwSetWindowSize(window, windowWidth, windowHeight); glEnable(GL_DEPTH_TEST); + + glfwSetWindowAttrib(window, GLFW_DECORATED, GLFW_FALSE); + glfwSetWindowAttrib(window, GLFW_DECORATED, GLFW_TRUE); + glfwSetWindowAttrib(window, GLFW_DECORATED, GLFW_FALSE); + return window; } @@ -261,24 +351,6 @@ void updateViewportFromFrame(uint32 windowWidth, uint32 windowHeight, Frame* fra glViewport(frame->x, windowHeight - frame->y - frame->height, frame->width, frame->height); } -typedef struct UI_Context UI_Context; -struct UI_Context { - Renderer *renderer; - Input *prevInput; - Input *input; - bool cursorIsPointer; -}; - -typedef struct UI_Rect UI_Rect; -struct UI_Rect { - real32 x; - real32 y; - real32 width; - real32 height; - real32 borderRadius; - RLVector4 color; -}; - Mesh cubeMesh = {0}; Texture wallTex = {0}; @@ -308,6 +380,7 @@ Input getCurrentInput(GLFWwindow *window) { input.keyboard.enter = glfwKey(window, GLFW_KEY_ENTER); input.keyboard.space = glfwKey(window, GLFW_KEY_SPACE); input.keyboard.lshift = glfwKey(window, GLFW_KEY_LEFT_SHIFT); + input.keyboard.w = glfwKey(window, GLFW_KEY_W); input.keyboard.x = glfwKey(window, GLFW_KEY_X); input.keyboard.y = glfwKey(window, GLFW_KEY_Y); input.keyboard.z = glfwKey(window, GLFW_KEY_Z); @@ -324,7 +397,7 @@ Input getCurrentInput(GLFWwindow *window) { return input; } -void processInput(Soma *soma) { +void processInput(Soma *soma, UI_Context *ui) { Input *input = &soma->state.input; Input *prevInput = &soma->prevState.input; @@ -332,18 +405,18 @@ void processInput(Soma *soma) { glfwSetWindowShouldClose(soma->window.handle, true); } - if (input->keyboard.space && !prevInput->keyboard.space) { + if (input->keyboard.w && !prevInput->keyboard.w) { glPolygonMode(GL_FRONT_AND_BACK, !soma->state.wireframe ? GL_LINE : GL_FILL); soma->state.wireframe = !soma->state.wireframe; } - SceneGraphNode *node = getSceneGraphNode(soma->scene, getEntity(soma->scene, soma->state.light)->graphNodeHandle); + SceneGraphNode *node = getSceneGraphNode(soma->scene, soma->state.light); int shiftMultiplier = input->keyboard.lshift ? -1 : 1; node->translation.x += 1.0 * input->keyboard.x * shiftMultiplier; node->translation.y += 1.0 * input->keyboard.y * shiftMultiplier; node->translation.z += 1.0 * input->keyboard.z * shiftMultiplier; - if (input->keyboard.enter && !prevInput->keyboard.enter) { + if (input->keyboard.space && !prevInput->keyboard.space) { if (soma->state.displayingSolutions) { if (soma->state.displayedSolution == soma->solutions.length - 1) { soma->state.displayedSolution = 0; @@ -359,8 +432,15 @@ void processInput(Soma *soma) { } } - bool dragScene = false; - if (input->mouse.btnLeft) { + if (input->keyboard.enter && !prevInput->keyboard.enter) { + if (soma->state.displayingSolutions) { + soma->state.displayingSolutions = false; + } else { + scheduleSolve(soma); + } + } + + if (input->mouse.btnLeft && ui->hotNode == 0) { uint32 currentObject = soma->state.displayingSolutions ? soma->solutionEntities.data[soma->state.displayedSolution] : soma->polycubes.data[soma->state.displayedPolycube]; @@ -378,13 +458,13 @@ void processInput(Soma *soma) { } } -uint32 createPolycubeFromRepr(Arena *arena, Scene *s, VoxelSpace *repr, RLVector4 color) { - uint32 mainGraphNode = createSceneGraphNode(arena, s); +uint32 createPolycubeFromRepr(Scene *s, VoxelSpace *repr, RLVector4 color) { + uint32 mainGraphNode = createSceneGraphNode(s); for (int x = 0; x < repr->dim_x; x++) { for (int y = 0; y < repr->dim_y; y++) { for (int z = 0; z < repr->dim_z; z++) { if (filledAt(repr, x, y, z)) { - uint32 segmentEntityHandle = createEntity(arena, s); + uint32 segmentEntityHandle = createEntity(s); Entity *polycubeSegment = getEntity(s, segmentEntityHandle); polycubeSegment->color = color; polycubeSegment->mesh = &cubeMesh; @@ -429,72 +509,72 @@ RenderObjects_Rectangle createRectangleObjects(Arena *arena, size_t count) { glBindVertexArray(result.vao); glBindBuffer(GL_ARRAY_BUFFER, result.p0BufferId); - glBufferData(GL_ARRAY_BUFFER, result.p0.length * sizeof(RLVector2), 0, GL_DYNAMIC_DRAW); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(RLVector2), (void*)0); + glBufferData(GL_ARRAY_BUFFER, result.p0.length * sizeof(RLVec2List_underlying), 0, GL_DYNAMIC_DRAW); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(RLVec2List_underlying), NULL); glVertexAttribDivisor(0, 1); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, result.p1BufferId); - glBufferData(GL_ARRAY_BUFFER, result.p1.length * sizeof(RLVector2), 0, GL_DYNAMIC_DRAW); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(RLVector2), (void*)0); + glBufferData(GL_ARRAY_BUFFER, result.p1.length * sizeof(RLVec2List_underlying), 0, GL_DYNAMIC_DRAW); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(RLVec2List_underlying), NULL); glVertexAttribDivisor(1, 1); glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, result.colorBufferId); - glBufferData(GL_ARRAY_BUFFER, result.color.length * sizeof(RLVector4), 0, GL_DYNAMIC_DRAW); - glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(RLVector4), (void*)0); + glBufferData(GL_ARRAY_BUFFER, result.color.length * sizeof(RLVec4List_underlying), 0, GL_DYNAMIC_DRAW); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(RLVec4List_underlying), NULL); glVertexAttribDivisor(2, 1); glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, result.borderRadiusBufferId); glBufferData(GL_ARRAY_BUFFER, result.borderRadius.length * sizeof(FloatList_underlying), 0, GL_DYNAMIC_DRAW); - glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, sizeof(FloatList_underlying), (void*)0); + glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, sizeof(FloatList_underlying), NULL); glVertexAttribDivisor(3, 1); glEnableVertexAttribArray(3); glBindBuffer(GL_ARRAY_BUFFER, result.borderThicknessBufferId); glBufferData(GL_ARRAY_BUFFER, result.borderThickness.length * sizeof(FloatList_underlying), 0, GL_DYNAMIC_DRAW); - glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(FloatList_underlying), (void*)0); + glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(FloatList_underlying), NULL); glVertexAttribDivisor(4, 1); glEnableVertexAttribArray(4); glBindBuffer(GL_ARRAY_BUFFER, result.edgeSoftnessBufferId); glBufferData(GL_ARRAY_BUFFER, result.edgeSoftness.length * sizeof(FloatList_underlying), 0, GL_DYNAMIC_DRAW); - glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(FloatList_underlying), (void*)0); + glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(FloatList_underlying), NULL); glVertexAttribDivisor(5, 1); glEnableVertexAttribArray(5); return result; } -void reinitRectangleObjectBuffers(Renderer *r) { +void updateRectangleObjectBuffers(Renderer *r) { glBindVertexArray(r->rects.vao); glBindBuffer(GL_ARRAY_BUFFER, r->rects.p0BufferId); - glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.p0.length * sizeof(RLVec2List), r->rects.p0.data); + glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.p0.length * sizeof(RLVec2List_underlying), r->rects.p0.data); glBindBuffer(GL_ARRAY_BUFFER, r->rects.p1BufferId); - glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.p1.length * sizeof(RLVec2List), r->rects.p1.data); + glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.p1.length * sizeof(RLVec2List_underlying), r->rects.p1.data); glBindBuffer(GL_ARRAY_BUFFER, r->rects.colorBufferId); - glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.color.length * sizeof(RLVec4List), r->rects.color.data); + glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.color.length * sizeof(RLVec4List_underlying), r->rects.color.data); glBindBuffer(GL_ARRAY_BUFFER, r->rects.borderRadiusBufferId); - glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.borderRadius.length * sizeof(FloatList), r->rects.borderRadius.data); + glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.borderRadius.length * sizeof(FloatList_underlying), r->rects.borderRadius.data); glBindBuffer(GL_ARRAY_BUFFER, r->rects.borderThicknessBufferId); - glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.borderThickness.length * sizeof(FloatList), r->rects.borderThickness.data); + glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.borderThickness.length * sizeof(FloatList_underlying), r->rects.borderThickness.data); glBindBuffer(GL_ARRAY_BUFFER, r->rects.edgeSoftnessBufferId); - glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.edgeSoftness.length * sizeof(FloatList), r->rects.edgeSoftness.data); + glBufferSubData(GL_ARRAY_BUFFER, 0, r->rects.edgeSoftness.length * sizeof(FloatList_underlying), r->rects.edgeSoftness.data); } Renderer createRenderer(Arena *arena, Scene *scene) { - Renderer result = {0}; - result.scene = scene; - result.scene->sceneRoot = createSceneGraphNode(arena, scene); - result.rects = createRectangleObjects(arena, 100); - return result; + scene->sceneRoot = createSceneGraphNode(scene); + return (Renderer){ + .scene = scene, + .rects = createRectangleObjects(arena, 100), + }; } void renderBegin(Renderer *r) { @@ -515,19 +595,19 @@ void renderEnd(Soma *soma, Renderer *renderer) { setUniformMat4fv(&phongShader, "projection", &soma->state.camera->proj); setUniformMat4fv(&phongShader, "view", &soma->state.camera->view); - SceneGraphNode *lightGraphNode = getSceneGraphNode(soma->scene, getEntity(soma->scene, soma->state.light)->graphNodeHandle); + SceneGraphNode *lightGraphNode = getSceneGraphNode(soma->scene, soma->state.light); setUniform3fv(&phongShader, "light_pos", &lightGraphNode->translation); setUniform3fv(&phongShader, "camera", &soma->state.camera->pos); uint32 currentPolycube = soma->polycubes.data[soma->state.displayedPolycube]; glBindVertexArray(cubeMesh.vao); - int model_uniform = getUniformLocation(&phongShader, "model"); + int solid_color_uniform = getUniformLocation(&phongShader, "solid_color"); for (EachIn(renderer->scene->entities, i)) { Entity *entity = &renderer->scene->entities.data[i]; if (entity->flags & EntityFlags_Render && entity->flags & EntityFlags_Visible) { - setUniform4fv(&phongShader, "solid_color", &entity->color); + setUniform4fvByLoc(solid_color_uniform, &entity->color); setUniformMat4fvByLoc(model_uniform, &getSceneGraphNode(renderer->scene, entity->graphNodeHandle)->world); glBindTexture(GL_TEXTURE_2D, entity->tex->tex_id); glDrawArrays(GL_TRIANGLES, 0, (GLsizei)entity->mesh->num_indices); @@ -538,7 +618,7 @@ void renderEnd(Soma *soma, Renderer *renderer) { // 2D overlay glUseProgram(solidColorShader.progId); - reinitRectangleObjectBuffers(renderer); + updateRectangleObjectBuffers(renderer); Matrix ortho = MatrixOrtho(0.0, 800.0, 600.0, 0.0, -1.0, 1.0); setUniformMat4fv(&solidColorShader, "projection", &ortho); @@ -548,10 +628,8 @@ void renderEnd(Soma *soma, Renderer *renderer) { } void rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real32 height, RLVector4 color, real32 borderRadius, real32 borderThickness) { - RLVector2 p0 = {x, y}; - AppendList(&r->rects.p0, p0); - RLVector2 p1 = {x + width, y + height}; - AppendList(&r->rects.p1, p1); + AppendList(&r->rects.p0, ((RLVector2){ x, y })); + AppendList(&r->rects.p1, ((RLVector2){ x + width, y + height })); AppendList(&r->rects.color, color); AppendList(&r->rects.borderRadius, borderRadius); AppendList(&r->rects.borderThickness, borderThickness); @@ -563,12 +641,15 @@ bool pointInRect(RLVector2 point, UI_Rect rect) { } bool ui_checkboxRect(UI_Context *ui, bool *value, UI_Rect rect) { + uint32 id = ui->nextId++; bool clicked = false; if (pointInRect(ui->input->mouse.point, rect)) { ui->cursorIsPointer = true; - if (ui->prevInput->mouse.btnLeft && !ui->input->mouse.btnLeft) { + if (ui->prevHotNode == id && !ui->input->mouse.btnLeft) { *value = !*value; clicked = true; + } else if (ui->input->mouse.btnLeft && (!ui->prevInput->mouse.btnLeft || ui->prevHotNode == id)) { + ui->hotNode = id; } } if (*value) { @@ -576,7 +657,7 @@ bool ui_checkboxRect(UI_Context *ui, bool *value, UI_Rect rect) { rect.x, rect.y, rect.width, rect.height, rect.color, - 5, 2); + 5, 0); } else { rendererPlaceRectangle(ui->renderer, rect.x, rect.y, @@ -588,77 +669,197 @@ bool ui_checkboxRect(UI_Context *ui, bool *value, UI_Rect rect) { return clicked; } -void uiPass(Soma *soma, UI_Context *ui, Renderer *renderer) { - PolycubeInput *currentPolycube = &soma->polycubeInput.data[soma->state.displayedPolycube]; +void uiPass(Soma *soma, UI_Context *ui) { + ui->cursorIsPointer = false; + ui->nextId = 1; + ui->prevInput = &soma->prevState.input; + ui->input = &soma->state.input; + ui->prevHotNode = ui->hotNode; + ui->hotNode = 0; real32 boxSize = 30; real32 padding = 20; real32 paddingBetween = 5; real32 currY = padding; - for (int x = 0; x < currentPolycube->repr.dim_x; x++) { - for (int y = 0; y < currentPolycube->repr.dim_y; y++) { - real32 currX = padding; - for (int z = 0; z < currentPolycube->repr.dim_z; z++) { - bool cellActive = filledAt(¤tPolycube->repr, x, y, z); - UI_Rect rect = { - .x = currX, - .y = currY, - .width = boxSize, - .height = boxSize, - .borderRadius = 0, - .color = currentPolycube->color, - }; - if (ui_checkboxRect(ui, &cellActive, rect)) { - soma->state.polycubeDirty = true; - spaceSet(¤tPolycube->repr, cellActive, x, y, z); + + if (soma->state.displayingSolutions) { + if (soma->solutions.length > 0) { + SomaSolution *currentSolution = &soma->solutions.data[soma->state.displayedSolution]; + for (EachIn(*currentSolution, i)) { + uint64 spaceRepr = currentSolution->data[i]; + for (int x = 0; x < soma->puzzleDims[0]; x++) { + for (int y = 0; y < soma->puzzleDims[1]; y++) { + real32 currX = padding; + for (int z = 0; z < soma->puzzleDims[2]; z++) { + bool cellActive = filledAt(&(VoxelSpace){ + .space = spaceRepr, + .dim_x = soma->puzzleDims[0], + .dim_y = soma->puzzleDims[1], + .dim_z = soma->puzzleDims[2], + }, x, y, z); + if (cellActive) { + rendererPlaceRectangle(ui->renderer, + currX, currY, + boxSize, boxSize, + soma->polycubeInput.data[i].color, + 5, 0); + } + currX += paddingBetween + boxSize; + } + currY += paddingBetween + boxSize; + } + currY += padding; } - currX += paddingBetween + boxSize; + currY = padding; } - currY += paddingBetween + boxSize; } - currY += padding; + } else { + PolycubeInput *currentPolycube = &soma->polycubeInput.data[soma->state.displayedPolycube]; + for (int x = 0; x < currentPolycube->repr.dim_x; x++) { + for (int y = 0; y < currentPolycube->repr.dim_y; y++) { + real32 currX = padding; + for (int z = 0; z < currentPolycube->repr.dim_z; z++) { + bool cellActive = filledAt(¤tPolycube->repr, x, y, z); + UI_Rect rect = { + .x = currX, + .y = currY, + .width = boxSize, + .height = boxSize, + .borderRadius = 2, + .color = currentPolycube->color, + }; + if (ui_checkboxRect(ui, &cellActive, rect)) { + soma->state.polycubeDirty = true; + spaceSet(¤tPolycube->repr, cellActive, x, y, z); + } + currX += paddingBetween + boxSize; + } + currY += paddingBetween + boxSize; + } + currY += padding; + } } } -int mainCmd() { - interactiveCmdLineSolveSoma(); - return 0; +void updatePolycubeDisplay(Arena *arena, Soma *soma) { + if (soma->prevState.displayingSolutions) { + if (soma->prevState.displayedPolycube < soma->polycubes.length) { + hide(soma->scene, soma->polycubes.data[soma->prevState.displayedPolycube]); + show(soma->scene, soma->solutionEntities.data[soma->state.displayedSolution]); + } + } else { + if (soma->prevState.displayedSolution < soma->solutionEntities.length) { + hide(soma->scene, soma->solutionEntities.data[soma->prevState.displayedSolution]); + show(soma->scene, soma->polycubes.data[soma->state.displayedPolycube]); + } + } + + if (soma->state.displayingSolutions) { + if (soma->solutions.length > 0) { + if (soma->state.displayedSolution != soma->prevState.displayedSolution) { + show(soma->scene, soma->solutionEntities.data[soma->state.displayedSolution]); + hide(soma->scene, soma->solutionEntities.data[soma->prevState.displayedSolution]); + } + } + } else { + if (soma->state.isInitialState) { + show(soma->scene, soma->polycubes.data[soma->state.displayedPolycube]); + } else if (soma->state.displayedPolycube != soma->prevState.displayedPolycube) { + show(soma->scene, soma->polycubes.data[soma->state.displayedPolycube]); + hide(soma->scene, soma->polycubes.data[soma->prevState.displayedPolycube]); + } + } + + if (soma->state.polycubeDirty) { + if (soma->state.displayedPolycube >= 0 && soma->state.displayedPolycube < soma->polycubes.length) { + uint32 currentPolycubeHandle = soma->polycubes.data[soma->state.displayedPolycube]; + PolycubeInput *pinput = &soma->polycubeInput.data[soma->state.displayedPolycube]; + VoxelSpace culledRepr = pinput->repr; + cullEmptySpace(&culledRepr); + uint32 newPolycubeHandle = createPolycubeFromRepr(soma->scene, &culledRepr, pinput->color); + SceneGraphNode *newPolycubeGraphNode = getSceneGraphNode(soma->scene, newPolycubeHandle); + newPolycubeGraphNode->rotation = getSceneGraphNode(soma->scene, currentPolycubeHandle)->rotation; + removeEntity(soma->scene, currentPolycubeHandle); + soma->polycubes.data[soma->state.displayedPolycube] = newPolycubeHandle; + sceneNodeAddNode(soma->scene, soma->scene->sceneRoot, newPolycubeHandle); + soma->state.polycubeDirty = false; + hide(soma->scene, currentPolycubeHandle); + show(soma->scene, newPolycubeHandle); + } + } +} + +void createSolutionEntities(Arena *arena, Soma *soma) { + if (soma->solutions.length > 0) { + soma->solutionEntities = PushList(arena, HandleList, soma->solutions.length); + for (EachEl(soma->solutions, SomaSolution, solution)) { + uint32 solutionGraphNodeHandle = createSceneGraphNode(soma->scene); + AppendList(&soma->solutionEntities, solutionGraphNodeHandle); + sceneNodeAddNode(soma->renderer->scene, soma->renderer->scene->sceneRoot, solutionGraphNodeHandle); + for (EachIn(*solution, soln_i)) { + uint32 polycubeGraphNodeHandle = createPolycubeFromRepr( + soma->scene, + &(VoxelSpace){ solution->data[soln_i], 3, 3, 3 }, + colorFromIndex(soln_i) + ); + sceneNodeAddNode(soma->renderer->scene, solutionGraphNodeHandle, polycubeGraphNodeHandle); + } + } + show(soma->scene, soma->solutionEntities.data[soma->state.displayedSolution]); + } } int mainGfx() { Arena *arena = arenaAlloc(Megabytes(128)); + Arena *solutionsArena = arenaAlloc(Megabytes(128)); - Scene mainScene = createScene(arena); - - Soma soma = {}; - soma.window.width = 800; - soma.window.height = 600; - soma.window.handle = initWindowAndGL(soma.window.width, soma.window.height); - soma.scene = &mainScene; - Renderer renderer = createRenderer(arena, &mainScene); - soma.renderer = &renderer; - - if (!soma.window.handle) { - return -1; + int winWidth = 800, winHeight = 600; + GLFWwindow *windowHandle = initWindowAndGL(winWidth, winHeight); + if (!windowHandle) { + return 1; } - Frame mainFrame = createFrame(arena, soma.window.width, soma.window.height, 0, 0); - UI_Context ui = {}; + Scene mainScene = createScene(arena); + Renderer renderer = createRenderer(arena, &mainScene); + Frame mainFrame = createFrame(arena, winWidth, winHeight, 0, 0); - GLFWcursor *pointerCursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR); - GLFWcursor *arrowCursor = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + UI_Context ui = { + .hotNode = 0, + .nextId = 1, + .cursorIsPointer = false, + .input = NULL, + .prevInput = NULL, + .renderer = &renderer, + }; - soma.polycubeInput = PushListZero(arena, PolycubeInputList, 64); - soma.polycubes = PushListZero(arena, HandleList, 64); + Soma soma = { + .window = { + .width = 800, + .height = 600, + .handle = windowHandle, + .cursors = { + .pointer = glfwCreateStandardCursor(GLFW_HAND_CURSOR), + .arrow = glfwCreateStandardCursor(GLFW_ARROW_CURSOR), + }, + }, + .scene = &mainScene, + .renderer = &renderer, + .polycubeInput = PushListZero(arena, PolycubeInputList, 64), + .polycubes = PushListZero(arena, HandleList, 64), + .puzzleDims = {3, 3, 3}, + .solveTaskCtx = NULL, + .solveArena = arenaAlloc(Megabytes(128)), + .state = { + .displayingSolutions = false, + .displayedPolycube = 0, + .displayedSolution = 0, + .isInitialState = true, + .light = createEntity(&mainScene), + .camera = mainFrame.cam, + }, + }; - soma.state.displayingSolutions = true; - soma.state.displayedPolycube = 0; - soma.state.displayedSolution = 0; - soma.state.light = createEntity(arena, &mainScene); - soma.state.camera = mainFrame.cam; - - SceneGraphNode *light = getSceneGraphNode(&mainScene, getEntity(&mainScene, soma.state.light)->graphNodeHandle); - light->translation = (RLVector3){4.0f, 6.0f, 24.0f}; + getSceneGraphNode(&mainScene, soma.state.light)->translation = (RLVector3){4.0f, 6.0f, 24.0f}; /* Shader solid_texture_shader = createShader( @@ -684,31 +885,12 @@ int mainGfx() { PolycubeInput input = (PolycubeInput){ voxelSpace, color }; AppendList(&soma.polycubeInput, input); cullEmptySpace(&voxelSpace); - uint32 polycubeGraphNodeHandle = createPolycubeFromRepr(arena, soma.scene, &voxelSpace, color); + uint32 polycubeGraphNodeHandle = createPolycubeFromRepr(soma.scene, &voxelSpace, color); AppendList(&soma.polycubes, polycubeGraphNodeHandle); sceneNodeAddNode(renderer.scene, renderer.scene->sceneRoot, polycubeGraphNodeHandle); } - soma.solutions = solveSoma(arena, AsList(VoxelSpaceReprList, STD_SOMA), (int[]){ 3, 3, 3 }); - soma.solutionEntities = PushList(arena, HandleList, soma.solutions.length); - for (EachEl(soma.solutions, SomaSolution, solution)) { - uint32 solutionGraphNodeHandle = createSceneGraphNode(arena, soma.scene); - AppendList(&soma.solutionEntities, solutionGraphNodeHandle); - sceneNodeAddNode(renderer.scene, renderer.scene->sceneRoot, solutionGraphNodeHandle); - for (EachIn(*solution, soln_i)) { - uint32 polycubeGraphNodeHandle = createPolycubeFromRepr( - arena, - soma.scene, - &(VoxelSpace){ solution->data[soln_i], 3, 3, 3 }, - colorFromIndex(soln_i) - ); - sceneNodeAddNode(renderer.scene, solutionGraphNodeHandle, polycubeGraphNodeHandle); - } - } - show(soma.scene, soma.solutionEntities.data[soma.state.displayedSolution]); - - print("%zu\n", soma.scene->entities.length); - print("%zu\n", soma.scene->graphNodes.length); + // TODO(dledda): only actually create and render one solution/polycube at a time, save lots of space! soma.state.camera->pos = (RLVector3){0.0f, 0.0f, 8.0f}; cameraLookAt(soma.state.camera, 0.0f, 0.0f, 0.0f); @@ -727,34 +909,21 @@ int mainGfx() { glfwPollEvents(); soma.state.input = getCurrentInput(soma.window.handle); - processInput(&soma); - /* - if (soma.state.lastPolycubeVisible != soma.state.currentPolycube) { - hide(soma.scene, soma.state.polycubes.data[soma.state.lastPolycubeVisible].graphNodeHandle); - show(soma.scene, soma.state.polycubes.data[soma.state.currentPolycube].graphNodeHandle); - soma.state.lastPolycubeVisible = soma.state.currentPolycube; - } - */ + processInput(&soma, &ui); + updatePolycubeDisplay(arena, &soma); - if (soma.state.displayedSolution != soma.prevState.displayedSolution) { - show(soma.scene, soma.solutionEntities.data[soma.state.displayedSolution]); - hide(soma.scene, soma.solutionEntities.data[soma.prevState.displayedSolution]); - } - - if (soma.state.polycubeDirty) { - uint32 currentPolycube = soma.polycubes.data[soma.state.displayedPolycube]; - PolycubeInput *pinput = &soma.polycubeInput.data[soma.state.displayedPolycube]; - removeEntity(soma.scene, currentPolycube); - VoxelSpace culledRepr = pinput->repr; - cullEmptySpace(&culledRepr); - uint32 newPolycube = createPolycubeFromRepr(arena, soma.scene, &culledRepr, pinput->color); - SceneGraphNode *graphNode = getSceneGraphNode(soma.scene, newPolycube); - graphNode->rotation = getSceneGraphNode(soma.scene, currentPolycube)->rotation; - soma.polycubes.data[soma.state.displayedPolycube] = newPolycube; - sceneNodeAddNode(soma.scene, soma.scene->sceneRoot, newPolycube); - soma.state.polycubeDirty = false; - show(soma.scene, newPolycube); + if (soma.solveTaskCtx != NULL && soma.solveTaskCtx->taskStatus == SolveTaskStatus_Complete) { + soma.solutions = PushFullList(solutionsArena, SomaSolutionList, soma.solveTaskCtx->solutions.length); + memcpy(soma.solutions.data, soma.solveTaskCtx->solutions.data, soma.solutions.length*sizeof(soma.solutions.data[0])); + for (EachIn(soma.solutions, i)) { + soma.solutions.data[i] = PushFullList(solutionsArena, SomaSolution, soma.solveTaskCtx->input.length); + memcpy(&soma.solutions.data[i], &soma.solveTaskCtx->solutions.data[i], soma.solveTaskCtx->input.length*sizeof(uint64)); + } + createSolutionEntities(soma.solveTaskCtx->arena, &soma); + soma.solveTaskCtx->taskStatus = SolveTaskStatus_Ready; + soma.state.displayingSolutions = true; + arenaFreeFrom(soma.solveTaskCtx->arena, 0); } updateViewportFromFrame(soma.window.width, soma.window.height, &mainFrame); @@ -762,15 +931,11 @@ int mainGfx() { renderBegin(&renderer); - ui.cursorIsPointer = false; - ui.prevInput = &soma.prevState.input; - ui.input = &soma.state.input; - ui.renderer = &renderer; - uiPass(&soma, &ui, &renderer); + uiPass(&soma, &ui); if (ui.cursorIsPointer) { - glfwSetCursor(soma.window.handle, pointerCursor); + glfwSetCursor(soma.window.handle, soma.window.cursors.pointer); } else { - glfwSetCursor(soma.window.handle, arrowCursor); + glfwSetCursor(soma.window.handle, soma.window.cursors.arrow); } renderEnd(&soma, &renderer); @@ -783,6 +948,7 @@ int mainGfx() { glfwSwapBuffers(soma.window.handle); soma.prevState = soma.state; + soma.state.isInitialState = false; } glfwTerminate(); diff --git a/src/world/scene.c b/src/world/scene.c index e7c7c62..d7d0044 100644 --- a/src/world/scene.c +++ b/src/world/scene.c @@ -1,29 +1,39 @@ #include "string.h" #include "scene.h" -Entity *getEntity(Scene *s, uint32 id) { - return &s->entities.data[id - 1]; -} - -SceneGraphNode *getSceneGraphNodeForEntity(Scene *s, uint32 entityHandle) { - return &s->graphNodes.data[s->entities.data[entityHandle - 1].graphNodeHandle - 1]; +Entity *getEntity(Scene *s, uint32 entityHandle) { + if (entityHandle) { + return &s->entities.data[entityHandle - 1]; + } + return NULL; } SceneGraphNode *getSceneGraphNode(Scene *s, uint32 sceneGraphNodeHandle) { - return &s->graphNodes.data[sceneGraphNodeHandle - 1]; + if (sceneGraphNodeHandle) { + return &s->graphNodes.data[sceneGraphNodeHandle - 1]; + } + return NULL; } -uint32 createSceneGraphNode(Arena *arena, Scene *s) { +SceneGraphNode *getSceneGraphNodeForEntity(Scene *s, uint32 entityHandle) { + Entity *e = getEntity(s, entityHandle); + if (e) { + return getSceneGraphNode(s, e->graphNodeHandle); + } + return NULL; +} + +uint32 createSceneGraphNode(Scene *s) { AppendList(&s->graphNodes, (SceneGraphNode){0}); SceneGraphNode *node = &s->graphNodes.data[s->graphNodes.length - 1]; - node->children = PushList(arena, HandleList, 1000); + node->children = PushList(s->arena, HandleList, 1000); initGraphNode(node); return (uint32)s->graphNodes.length; } -uint32 createEntity(Arena *arena, Scene *s) { +uint32 createEntity(Scene *s) { AppendList(&s->entities, (Entity){0}); - uint32 graphNodeId = createSceneGraphNode(arena, s); + uint32 graphNodeId = createSceneGraphNode(s); s->entities.data[s->entities.length - 1].graphNodeHandle = graphNodeId; getSceneGraphNode(s, graphNodeId)->entityHandle = (uint32)s->entities.length; uint32 handle = (uint32)s->entities.length; @@ -44,10 +54,12 @@ void recalcGraphNode(SceneGraphNode *n) { } Scene createScene(Arena *arena) { - Scene result = {}; - result.entities = PushList(arena, EntityList, 100000); - result.graphNodes = PushList(arena, SceneGraphNodeList, 100000); - result.sceneRoot = createSceneGraphNode(arena, &result); + Scene result = { + .arena = arena, + .entities = PushList(arena, EntityList, 100000), + .graphNodes = PushList(arena, SceneGraphNodeList, 100000), + }; + result.sceneRoot = createSceneGraphNode(&result); return result; } @@ -74,14 +86,15 @@ void removeEntity(Scene *s, uint32 entityHandle) { Entity *entity = getEntity(s, entityHandle); entity->flags |= EntityFlags_Dead; SceneGraphNode *graphNode = getSceneGraphNode(s, entity->graphNodeHandle); - if (graphNode->parentHandle) { + if (graphNode != NULL && graphNode->parentHandle) { SceneGraphNode *parentNode = getSceneGraphNode(s, graphNode->parentHandle); - for (int i = 0; i < parentNode->children.length; i++) { - if (parentNode->children.data[i] == entity->graphNodeHandle) { - memcpy(&parentNode->children.data[i], &parentNode->children.data[i + 1], parentNode->children.length - i); - parentNode->children.length -= 1; - graphNode->parentHandle = 0; - break; + if (parentNode != NULL) { + for (EachIn(parentNode->children, i)) { + if (parentNode->children.data[i] == entity->graphNodeHandle) { + ListRemove(&parentNode->children, i); + graphNode->parentHandle = 0; + break; + } } } } diff --git a/src/world/scene.h b/src/world/scene.h index 29c03dd..812e7ee 100644 --- a/src/world/scene.h +++ b/src/world/scene.h @@ -37,12 +37,13 @@ struct Scene { uint32 sceneRoot; EntityList entities; SceneGraphNodeList graphNodes; + Arena *arena; }; -uint32 createEntity(Arena *arena, Scene *s); +uint32 createEntity(Scene *s); Entity *getEntity(Scene *s, uint32 id); SceneGraphNode *getSceneGraphNode(Scene *s, uint32 id); -uint32 createSceneGraphNode(Arena *arena, Scene *s); +uint32 createSceneGraphNode(Scene *s); Scene createScene(Arena *arena); void initGraphNode(SceneGraphNode *n); void recalcGraphNode(SceneGraphNode *n);