render 3d to tex

This commit is contained in:
Daniel Ledda
2026-05-11 23:30:35 +02:00
parent e768b38322
commit 66547b0f68
10 changed files with 279 additions and 92 deletions

View File

@@ -9,6 +9,9 @@ in float frag_softness;
in float frag_border_radius; in float frag_border_radius;
in float frag_border_thickness; in float frag_border_thickness;
in vec4 frag_border_color; in vec4 frag_border_color;
in vec2 uv;
uniform sampler2D rect_tex;
float roundedRectSDF(vec2 sample_pos, vec2 rect_center, vec2 rect_half_size, float r) { 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)); vec2 d2 = (abs(rect_center - sample_pos) - rect_half_size + vec2(r, r));
@@ -52,6 +55,7 @@ void main() {
float sdf_factor = 1 - smoothstep(0, 2*frag_softness, dist); float sdf_factor = 1 - smoothstep(0, 2*frag_softness, dist);
vec4 out_color = frag_color * texture(rect_tex, uv);
pixel_color = frag_border_color * sample * sdf_factor * border_factor pixel_color = frag_border_color * sample * sdf_factor * border_factor
+ frag_color * sample * sdf_factor; + out_color * sample * sdf_factor;
}; };

View File

@@ -17,6 +17,14 @@ out float frag_softness;
out float frag_border_radius; out float frag_border_radius;
out float frag_border_thickness; out float frag_border_thickness;
out vec4 frag_border_color; out vec4 frag_border_color;
out vec2 uv;
const vec2 rectangle_uv[4] = vec2[](
vec2(0, 1),
vec2(0, 0),
vec2(1, 1),
vec2(1, 0)
);
const vec2 rectangle_vertices[4] = vec2[]( const vec2 rectangle_vertices[4] = vec2[](
vec2(-1, -1), vec2(-1, -1),
@@ -44,4 +52,5 @@ void main() {
frag_border_thickness = border_thickness; frag_border_thickness = border_thickness;
frag_border_color = border_color; frag_border_color = border_color;
frag_softness = edge_softness; frag_softness = edge_softness;
uv = rectangle_uv[gl_VertexID];
} }

View File

@@ -1,5 +1,5 @@
#version 330 core #version 330 core
out vec4 frag_color; layout(location = 0) out vec4 frag_color;
uniform vec3 light_pos; uniform vec3 light_pos;
uniform vec4 solid_color; uniform vec4 solid_color;

View File

@@ -7,5 +7,5 @@ in vec2 frag_uv_position;
uniform sampler2D glyph_atlas; uniform sampler2D glyph_atlas;
void main() { void main() {
pixel_color = vec4(1,1,1,texture(glyph_atlas, frag_uv_position).r); pixel_color = vec4(frag_color.xyz,texture(glyph_atlas, frag_uv_position).r);
}; };

View File

@@ -99,6 +99,7 @@ Font createFont(Arena *arena, string ttfLocation, real32 lineHeight) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D); glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
glGenBuffers(1, &result.glyphTableBufId); glGenBuffers(1, &result.glyphTableBufId);
glBindBuffer(GL_TEXTURE_BUFFER, result.glyphTableBufId); glBindBuffer(GL_TEXTURE_BUFFER, result.glyphTableBufId);

View File

@@ -78,6 +78,7 @@ struct Soma {
struct { struct {
GLFWcursor *pointer; GLFWcursor *pointer;
GLFWcursor *arrow; GLFWcursor *arrow;
GLFWcursor *forbidden;
} cursors; } cursors;
} window; } window;
@@ -110,6 +111,7 @@ void tryScheduleSolve(Soma *soma) {
for (EachEl(soma->polycubeInput, PolycubeInput, polycubeInput)) { for (EachEl(soma->polycubeInput, PolycubeInput, polycubeInput)) {
ListAppend(mappedInputs, polycubeInput->repr.space); ListAppend(mappedInputs, polycubeInput->repr.space);
} }
ClearList(soma->solveTaskCtx.solutions);
soma->solveTaskCtx.input = mappedInputs; soma->solveTaskCtx.input = mappedInputs;
soma->solveTaskCtx.taskStatus = SolveTaskStatus_Solving; soma->solveTaskCtx.taskStatus = SolveTaskStatus_Solving;
soma->solveTaskCtx.dims = PushFullList(soma->solveTaskCtx.arena, IntList, 3); soma->solveTaskCtx.dims = PushFullList(soma->solveTaskCtx.arena, IntList, 3);
@@ -399,8 +401,19 @@ static void ui_Soma(UI_Context *ui, Soma *soma) {
UI_Rect btnHoverRectBlue = btnRectBlue; UI_Rect btnHoverRectBlue = btnRectBlue;
btnHoverRectBlue.color = lightblueHighlight; btnHoverRectBlue.color = lightblueHighlight;
int32 solveBtnId = 0;
int32 totalUnitsPlaced = 0;
for (EachIn(soma->polycubeInput, i)) {
totalUnitsPlaced += size(soma->polycubeInput.data[i].repr.space);
}
int32 requiredUnits = soma->puzzleDims[0] * soma->puzzleDims[1] * soma->puzzleDims[2];
bool canSolve = requiredUnits == totalUnitsPlaced;
UI(.flags=UI_Flag_WidthGrow | UI_Flag_HeightGrow) {
UI_SetTxtAttr(.lineHeight=20, .fontSize=20, .color={1,1,1,1});
UI(.flags=UI_Flag_HeightGrow | UI_Flag_WidthGrow) { 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={.left=20, .right=20}, .color=darkgrey, .flags=UI_Flag_HeightGrow | UI_Flag_Vertical) {
UI(.padding=UI_PadUniform(10)) UI_Txt(s("Somaesque"), .fontSize=26); UI(.padding=UI_PadUniform(10)) UI_Txt(s("Somaesque"), .fontSize=26);
ui_Interaction(ui, soma); ui_Interaction(ui, soma);
@@ -423,9 +436,19 @@ static void ui_Soma(UI_Context *ui, Soma *soma) {
if (ui_ButtonWithHover(ui, *rect, *hoverRect, UI_TxtAttr(s("Design")))) { if (ui_ButtonWithHover(ui, *rect, *hoverRect, UI_TxtAttr(s("Design")))) {
soma->state.displayingSolutions = false; soma->state.displayingSolutions = false;
} }
solveBtnId = UI_NextID();
rect = soma->state.displayingSolutions ? &btnRectBlue : &btnRect; rect = soma->state.displayingSolutions ? &btnRectBlue : &btnRect;
hoverRect = soma->state.displayingSolutions ? &btnHoverRectBlue : &btnHoverRect; hoverRect = soma->state.displayingSolutions ? &btnHoverRectBlue : &btnHoverRect;
if (ui_ButtonWithHover(ui, *rect, *hoverRect, UI_TxtAttr(s("Solve")))) { RLVector4 txtColor = {1,1,1,1};
if (!canSolve) {
hoverRect = rect;
txtColor = lightgrey;
}
if (ui_ButtonWithHover(ui, *rect, *hoverRect, UI_TxtAttr(s("Solve"), .color=txtColor))) {
if (canSolve) {
if (!soma->state.displayingSolutions) { if (!soma->state.displayingSolutions) {
tryScheduleSolve(soma); tryScheduleSolve(soma);
} else { } else {
@@ -433,9 +456,22 @@ static void ui_Soma(UI_Context *ui, Soma *soma) {
} }
} }
} }
if (ui->hoveredNode == solveBtnId && !canSolve) {
ui->cursorType = UI_Cursor_Forbidden;
}
}
} }
UI(.flags=UI_Flag_Vertical | UI_Flag_WidthGrow | UI_Flag_HeightGrow | UI_Flag_3DScene) { UI(.flags=UI_Flag_Vertical | UI_Flag_WidthGrow | UI_Flag_HeightGrow | UI_Flag_3DScene) {
UI_SetTxtAttr(.fontSize=26, .alignment=UI_TxtAlign_Right); UI_SetTxtAttr(.fontSize=26, .alignment=UI_TxtAlign_Right);
if (soma->state.displayingSolutions && soma->solutions.length == 0) {
UI(.flags=UI_Flag_WidthGrow | UI_Flag_HeightGrow | UI_Flag_Vertical) {
UI(.flags=UI_Flag_HeightGrow);
UI(.flags=UI_Flag_WidthGrow) {
UI_Txt(s("No solutions!"), .alignment=UI_TxtAlign_Center, .fontSize=50);
}
UI(.flags=UI_Flag_HeightGrow);
}
} else {
UI(.flags=UI_Flag_HeightGrow); UI(.flags=UI_Flag_HeightGrow);
UI(.padding=UI_PadUniform(10), .flags=UI_Flag_WidthGrow) { UI(.padding=UI_PadUniform(10), .flags=UI_Flag_WidthGrow) {
if (soma->solveTaskCtx.taskStatus == SolveTaskStatus_Solving) { if (soma->solveTaskCtx.taskStatus == SolveTaskStatus_Solving) {
@@ -455,10 +491,22 @@ static void ui_Soma(UI_Context *ui, Soma *soma) {
} }
} }
// Tooltip
if (ui->hoveredNode == solveBtnId && requiredUnits != totalUnitsPlaced) {
string tooltipStr = strPrintf(ui->arena, "Required units: %d - Placed: %d", requiredUnits, totalUnitsPlaced);
UI(.flags=UI_Flag_Pos_Absolute, .color={0,0,0,1}, .xOffset=ui->input->mouse.x - tooltipStr.length*10, .yOffset=ui->input->mouse.y, .width=tooltipStr.length * 10, .height=22, .padding=UI_PadUniform(1)) {
UI(.flags=UI_Flag_HeightGrow | UI_Flag_WidthGrow, .color={1,1,1,1}) {
UI_Txt(tooltipStr, .alignment=UI_TxtAlign_Center, .color={0,0,0,1});
}
}
}
}
}
static void updatePolycubeDisplay(Soma *soma) { static void updatePolycubeDisplay(Soma *soma) {
Scene *s = soma->scene; Scene *s = soma->scene;
if (soma->state.displayingSolutions && soma->state.displayedSolution != soma->prevState.displayedSolution) { if (soma->state.displayingSolutions && soma->solutions.length > 0 && soma->state.displayedSolution != soma->prevState.displayedSolution) {
removeSceneGraphNode(s, soma->solutionNode); removeSceneGraphNode(s, soma->solutionNode);
if (soma->state.displayedSolution >= 0 && soma->state.displayedSolution < soma->solutions.length) { if (soma->state.displayedSolution >= 0 && soma->state.displayedSolution < soma->solutions.length) {
SomaSolution soln = soma->solutions.data[soma->state.displayedSolution]; SomaSolution soln = soma->solutions.data[soma->state.displayedSolution];
@@ -476,7 +524,9 @@ static void updatePolycubeDisplay(Soma *soma) {
} }
if (soma->state.displayingSolutions) { if (soma->state.displayingSolutions) {
if (soma->solutions.length > 0) {
show(s, soma->solutionNode); show(s, soma->solutionNode);
}
if (soma->prevState.displayedPolycube >= 0 && soma->prevState.displayedPolycube < soma->polycubes.length) { if (soma->prevState.displayedPolycube >= 0 && soma->prevState.displayedPolycube < soma->polycubes.length) {
hide(s, soma->polycubes.data[soma->prevState.displayedPolycube]); hide(s, soma->polycubes.data[soma->prevState.displayedPolycube]);
} }
@@ -574,6 +624,7 @@ glDebugMessageCallback(glDebugCallback, NULL);
.cursors = { .cursors = {
.pointer = glfwCreateStandardCursor(GLFW_HAND_CURSOR), .pointer = glfwCreateStandardCursor(GLFW_HAND_CURSOR),
.arrow = glfwCreateStandardCursor(GLFW_ARROW_CURSOR), .arrow = glfwCreateStandardCursor(GLFW_ARROW_CURSOR),
.forbidden = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR),
}, },
}, },
.scene = &mainScene, .scene = &mainScene,
@@ -636,6 +687,76 @@ glDebugMessageCallback(glDebugCallback, NULL);
Shader textShader = createShader(s("./assets/shaders/text.vertex.glsl"), s("./assets/shaders/text.fragment.glsl")); Shader textShader = createShader(s("./assets/shaders/text.vertex.glsl"), s("./assets/shaders/text.fragment.glsl"));
renderer.textShader = &textShader; renderer.textShader = &textShader;
// --- Offscreen framebuffer setup
// GL
int32 offscreenWidth = 256;
int32 offscreenHeight = 256;
uint32 offscreenFrameBufHandle;
glGenFramebuffers(1, &offscreenFrameBufHandle);
glBindFramebuffer(GL_FRAMEBUFFER, offscreenFrameBufHandle);
uint32 offscreenTexHandle;
glGenTextures(1, &offscreenTexHandle);
glBindTexture(GL_TEXTURE_2D, offscreenTexHandle);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, offscreenWidth, offscreenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
uint32 depthRenderBufferHandle;
glGenRenderbuffers(1, &depthRenderBufferHandle);
glBindRenderbuffer(GL_RENDERBUFFER, depthRenderBufferHandle);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, offscreenWidth, offscreenHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderBufferHandle);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, offscreenTexHandle, 0);
uint32 DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, DrawBuffers);
// Camera and scene
Scene offscreenScene = createScene(arena);
Camera offscreenCam = createCamera(offscreenWidth, offscreenHeight);
int32 offscreenLight = createSceneGraphNode(&offscreenScene);
offscreenCam.pos = (RLVector3){0.0f, 0.0f, 8.0f};
cameraLookAt(&offscreenCam, 0.0f, 0.0f, 0.0f);
getSceneGraphNode(&offscreenScene, offscreenLight)->translation = (RLVector3){4.0f, 6.0f, 24.0f};
VoxelSpace offscreenPolycube = (VoxelSpace){
.space=stdSoma.data[0],
.dim_x=soma.puzzleDims[0],
.dim_y=soma.puzzleDims[1],
.dim_z=soma.puzzleDims[2],
};
cullEmptySpace(&offscreenPolycube);
uint32 offscreenPolycubeHandle = createPolycubeFromRepr(&offscreenScene, &offscreenPolycube, (RLVector4){1,1,1,1});
sceneNodeAddNode(&offscreenScene, offscreenScene.sceneRoot, offscreenPolycubeHandle);
renderer.testTexId = offscreenTexHandle;
show(&offscreenScene, offscreenPolycubeHandle);
recalcScene(&offscreenScene);
// 3D overlay
{
glBindFramebuffer(GL_FRAMEBUFFER, offscreenFrameBufHandle);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glViewport(0, 0, offscreenWidth, offscreenHeight);
cameraSetAspect(&offscreenCam, offscreenWidth, offscreenHeight);
glUseProgram(phongShader.progId);
setUniformMat4fv(&phongShader, "projection", &offscreenCam.proj);
setUniformMat4fv(&phongShader, "view", &offscreenCam.view);
SceneGraphNode *lightGraphNode = getSceneGraphNode(&offscreenScene, offscreenLight);
setUniform3fv(&phongShader, "light_pos", &lightGraphNode->translation);
setUniform3fv(&phongShader, "camera", &offscreenCam.pos);
glBindVertexArray(cubeMesh.vao);
int model_uniform = getUniformLocation(&phongShader, "model");
int solid_color_uniform = getUniformLocation(&phongShader, "solid_color");
for (EachEl(offscreenScene.entities, Entity, entity)) {
if (entity->flags & EntityFlags_Render && entity->flags & EntityFlags_Visible) {
setUniform4fvByLoc(solid_color_uniform, &entity->color);
setUniformMat4fvByLoc(model_uniform, &getSceneGraphNode(&offscreenScene, entity->graphNodeHandle)->world);
glBindTexture(GL_TEXTURE_2D, entity->tex->tex_id);
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)entity->mesh->num_indices);
}
}
};
// Render loop // Render loop
real64 lastFrame = glfwGetTime(); real64 lastFrame = glfwGetTime();
real64 timeDelta = 1.0f / TARGET_FPS; real64 timeDelta = 1.0f / TARGET_FPS;
@@ -657,7 +778,8 @@ glDebugMessageCallback(glDebugCallback, NULL);
processInput(&soma); processInput(&soma);
if (soma.solveTaskCtx.taskStatus == SolveTaskStatus_Complete) { if (soma.solveTaskCtx.taskStatus == SolveTaskStatus_Complete) {
soma.solutions = PushList(solutionsArena, SomaSolutionList, soma.solveTaskCtx.solutions.length); solutionsArena->head = 0;
soma.solutions = PushListCopy(solutionsArena, SomaSolutionList, soma.solveTaskCtx.solutions);
for (EachIn(soma.solveTaskCtx.solutions, i)) { for (EachIn(soma.solveTaskCtx.solutions, i)) {
ListAppend(soma.solutions, PushListCopy(solutionsArena, SomaSolution, soma.solveTaskCtx.solutions.data[i])); ListAppend(soma.solutions, PushListCopy(solutionsArena, SomaSolution, soma.solveTaskCtx.solutions.data[i]));
} }
@@ -672,10 +794,19 @@ glDebugMessageCallback(glDebugCallback, NULL);
ui_Soma(&ui, &soma); ui_Soma(&ui, &soma);
} }
if (ui.cursorIsPointer) { switch (ui.cursorType) {
glfwSetCursor(soma.window.handle, soma.window.cursors.pointer); case UI_Cursor_Arrow:
} else {
glfwSetCursor(soma.window.handle, soma.window.cursors.arrow); glfwSetCursor(soma.window.handle, soma.window.cursors.arrow);
break;
case UI_Cursor_Pointer:
glfwSetCursor(soma.window.handle, soma.window.cursors.pointer);
break;
case UI_Cursor_Forbidden:
glfwSetCursor(soma.window.handle, soma.window.cursors.forbidden);
break;
default:
glfwSetCursor(soma.window.handle, soma.window.cursors.arrow);
break;
} }
} }

View File

@@ -182,11 +182,13 @@ void renderBegin(Renderer *r) {
#define CHECK() do{ GLenum e=glGetError(); if(e) printf("GL err 0x%x at %s:%d\n",e,__FILE__,__LINE__); }while(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) { void renderEnd(Renderer *r) {
// 3D Scene glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
// --- 3D Scene ---
glViewport(r->sceneX, r->height - r->sceneY - r->sceneHeight, r->sceneWidth, r->sceneHeight); glViewport(r->sceneX, r->height - r->sceneY - r->sceneHeight, r->sceneWidth, r->sceneHeight);
cameraSetAspect(r->camera, r->sceneWidth, r->sceneHeight); cameraSetAspect(r->camera, r->sceneWidth, r->sceneHeight);
@@ -213,9 +215,11 @@ void renderEnd(Renderer *r) {
} }
} }
// 2D overlay // --- UI overlay ---
glViewport(0, 0, r->width, r->height); glViewport(0, 0, r->width, r->height);
glUseProgram(r->solidShader->progId); glUseProgram(r->solidShader->progId);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, r->testTexId);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND); glEnable(GL_BLEND);
@@ -223,28 +227,29 @@ void renderEnd(Renderer *r) {
Matrix ortho = MatrixOrtho(0.0, r->width, r->height, 0.0, -1.0, 1.0); Matrix ortho = MatrixOrtho(0.0, r->width, r->height, 0.0, -1.0, 1.0);
// 1. Rects // - 1. Rects
updateRectangleObjectBuffers(r); updateRectangleObjectBuffers(r);
setUniformMat4fv(r->solidShader, "projection", &ortho); setUniformMat4fv(r->solidShader, "projection", &ortho);
glBindVertexArray(r->rects.vao); glBindVertexArray(r->rects.vao);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, r->rects.p0.buf.length); glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, r->rects.p0.buf.length);
// 2. Text // - 2. Text
updateCharObjectBuffers(r); updateCharObjectBuffers(r);
glUseProgram(r->textShader->progId); glUseProgram(r->textShader->progId);
setUniformMat4fv(r->textShader, "projection", &ortho); setUniformMat4fv(r->textShader, "projection", &ortho);
glActiveTexture(GL_TEXTURE0); CHECK(); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, r->activeFont->texId); CHECK(); glBindTexture(GL_TEXTURE_2D, r->activeFont->texId);
glActiveTexture(GL_TEXTURE1); CHECK(); glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, r->activeFont->glyphTableTexId); CHECK(); glBindTexture(GL_TEXTURE_BUFFER, r->activeFont->glyphTableTexId);
setUniform1i(r->textShader, "glyph_table", 1); CHECK(); setUniform1i(r->textShader, "glyph_table", 1);
glBindVertexArray(r->chars.vao); CHECK(); glBindVertexArray(r->chars.vao);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, r->chars.begin.buf.length); CHECK(); glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, r->chars.begin.buf.length);
glDisable(GL_BLEND); glDisable(GL_BLEND);
glActiveTexture(GL_TEXTURE0);
} }
void rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real32 height, RLVector4 color, real32 borderRadius, real32 borderThickness, RLVector4 borderColor) { void rendererPlaceRectangle(Renderer *r, real32 x, real32 y, real32 width, real32 height, RLVector4 color, real32 borderRadius, real32 borderThickness, RLVector4 borderColor) {

View File

@@ -87,6 +87,8 @@ struct Renderer {
/** SceneGraphNode handle */ /** SceneGraphNode handle */
int32 light; int32 light;
Mesh *cubeMesh; Mesh *cubeMesh;
int32 testTexId;
}; };
Renderer createRenderer(Arena *arena, Scene *scene, Camera *cam, int32 light); Renderer createRenderer(Arena *arena, Scene *scene, Camera *cam, int32 light);

View File

@@ -54,7 +54,7 @@ UI_Context ui_initContext(Arena *arena, Renderer *renderer) {
.prevRects=prevList, .prevRects=prevList,
.hotNode=0, .hotNode=0,
.prevHotNode=0, .prevHotNode=0,
.cursorIsPointer=false, .cursorType=UI_Cursor_Arrow,
.scene3DHandle=0, .scene3DHandle=0,
.input=NULL, .input=NULL,
.prevInput=NULL, .prevInput=NULL,
@@ -72,6 +72,9 @@ void ui_placeText(UI_Context *ui, UI_RectStr strData) {
strDataCopy->fontSize = ui->renderer->activeFont->lineHeight; strDataCopy->fontSize = ui->renderer->activeFont->lineHeight;
} }
} }
if (strDataCopy->color.x == -1) {
strDataCopy->color = currRect->inheritedTextAttr->color;
}
if (strDataCopy->lineHeight == -1) { if (strDataCopy->lineHeight == -1) {
strDataCopy->lineHeight = currRect->inheritedTextAttr->lineHeight; strDataCopy->lineHeight = currRect->inheritedTextAttr->lineHeight;
if (strDataCopy->lineHeight == -1) { if (strDataCopy->lineHeight == -1) {
@@ -115,11 +118,13 @@ void ui_sizingPass(UI_Context *ui, bool isXAxis, int32 rectHandle) {
if (isVertical != isXAxis) { if (isVertical != isXAxis) {
while (childHandle) { while (childHandle) {
child = &ui->rects.data[childHandle]; child = &ui->rects.data[childHandle];
if (!(child->flags & UI_Flag_Pos_Absolute)) {
if (child->flags & UI_Flag_HeightGrow && !isXAxis || child->flags & UI_Flag_WidthGrow && isXAxis) { if (child->flags & UI_Flag_HeightGrow && !isXAxis || child->flags & UI_Flag_WidthGrow && isXAxis) {
growableChildrenCount++; growableChildrenCount++;
} }
real32 childBreadth = isXAxis ? child->resolvedWidth : child->resolvedHeight; real32 childBreadth = isXAxis ? child->resolvedWidth : child->resolvedHeight;
remainingSpace -= childBreadth + rect->childGap; remainingSpace -= childBreadth + rect->childGap;
}
childHandle = child->nextSibling; childHandle = child->nextSibling;
} }
} }
@@ -161,14 +166,23 @@ void ui_calcLayout(UI_Context *ui, bool isXAxis, int32 rectHandle) {
bool isVertical = (rect->flags & UI_Flag_Vertical); bool isVertical = (rect->flags & UI_Flag_Vertical);
real32 coord = isXAxis real32 coord = isXAxis
? rect->x + rect->xOffset + rect->padding.left ? rect->x + rect->padding.left
: rect->y + rect->yOffset + rect->padding.top; : rect->y + rect->padding.top;
real32 parentCoord = coord;
int32 childHandle = rect->firstChild; int32 childHandle = rect->firstChild;
UI_Rect *child; UI_Rect *child;
while (childHandle) { while (childHandle) {
child = &ui->rects.data[childHandle]; child = &ui->rects.data[childHandle];
if (child->flags & UI_Flag_Pos_Absolute) {
if (isXAxis) {
child->x = parentCoord;
} else {
child->y = parentCoord;
}
} else {
if (isXAxis) { if (isXAxis) {
child->x = child->flags & UI_Flag_Center && isVertical child->x = child->flags & UI_Flag_Center && isVertical
? coord + (rect->resolvedWidth - rect->padding.left - rect->padding.right)/2 - child->resolvedWidth/2 ? coord + (rect->resolvedWidth - rect->padding.left - rect->padding.right)/2 - child->resolvedWidth/2
@@ -178,10 +192,17 @@ void ui_calcLayout(UI_Context *ui, bool isXAxis, int32 rectHandle) {
? coord + (rect->resolvedHeight - rect->padding.top - rect->padding.bottom)/2 - child->resolvedHeight/2 ? coord + (rect->resolvedHeight - rect->padding.top - rect->padding.bottom)/2 - child->resolvedHeight/2
: coord; : coord;
} }
}
if (isXAxis) {
child->x += child->xOffset;
} else {
child->y += child->yOffset;
}
ui_calcLayout(ui, isXAxis, childHandle); ui_calcLayout(ui, isXAxis, childHandle);
if (!isVertical == isXAxis) { if (!(child->flags & UI_Flag_Pos_Absolute) && !isVertical == isXAxis) {
coord += isXAxis ? child->resolvedWidth : child->resolvedHeight; coord += isXAxis ? child->resolvedWidth : child->resolvedHeight;
coord += rect->childGap; coord += rect->childGap;
} }
@@ -195,9 +216,11 @@ void ui_begin(UI_Context *ui) {
arenaFreeFrom(ui->arena, 0); arenaFreeFrom(ui->arena, 0);
ClearList(ui->prevRects); ClearList(ui->prevRects);
ListAppendList(ui->prevRects, ui->rects); ListAppendList(ui->prevRects, ui->rects);
ui->cursorIsPointer = false; ui->cursorType = UI_Cursor_Arrow;
ui->prevHotNode = ui->hotNode; ui->prevHotNode = ui->hotNode;
ui->prevHoveredNode = ui->hoveredNode;
ui->hotNode = 0; ui->hotNode = 0;
ui->hoveredNode = 0;
ui->scene3DHandle = 0; ui->scene3DHandle = 0;
ui->rootRect = 0; ui->rootRect = 0;
ui->currRect = 0; ui->currRect = 0;
@@ -205,6 +228,7 @@ void ui_begin(UI_Context *ui) {
ListAppend(ui->rects, (UI_Rect){0}); ListAppend(ui->rects, (UI_Rect){0});
ui_openElement(ui, (UI_Rect){ .width=ui->renderer->width, .height=ui->renderer->height, .color=(RLVector4){0,0,0,0}}); ui_openElement(ui, (UI_Rect){ .width=ui->renderer->width, .height=ui->renderer->height, .color=(RLVector4){0,0,0,0}});
ui_placeText(ui, (UI_RectStr){ .s={.str=NULL, .length=0}, .fontSize=10, .color={1,1,1,1}, .alignment=UI_TxtAlign_Left, .lineHeight=10});
} }
void ui_end(UI_Context *ui) { void ui_end(UI_Context *ui) {
@@ -298,9 +322,9 @@ void ui_openElement(UI_Context *ui, UI_Rect rect) {
if (currRect->stringData) { if (currRect->stringData) {
UI_RectStr *inheritedTextAttr = PushStructZero(ui->arena, UI_RectStr); UI_RectStr *inheritedTextAttr = PushStructZero(ui->arena, UI_RectStr);
*inheritedTextAttr = *currRect->stringData; *inheritedTextAttr = *currRect->stringData;
inheritedTextAttr->s = s(""); *newRect->inheritedTextAttr = *currRect->stringData;
newRect->inheritedTextAttr = inheritedTextAttr; newRect->inheritedTextAttr->s = (string){ .str=NULL, .length=0 };
} else { } else if (currRect) {
newRect->inheritedTextAttr = currRect->inheritedTextAttr; newRect->inheritedTextAttr = currRect->inheritedTextAttr;
} }
} }
@@ -373,12 +397,13 @@ bool ui_ButtonWithHover(UI_Context *ui, UI_Rect rect, UI_Rect hovered, UI_RectSt
bool inRect = 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) { if (inRect) {
ui->cursorIsPointer = true; ui->cursorType = UI_Cursor_Pointer;
if (ui->prevHotNode == id && !ui->input->mouse.btnLeft) { if (ui->prevHotNode == id && !ui->input->mouse.btnLeft) {
clicked = true; clicked = true;
} else if (ui->input->mouse.btnLeft && (!ui->prevInput->mouse.btnLeft || ui->prevHotNode == id)) { } else if (ui->input->mouse.btnLeft && (!ui->prevInput->mouse.btnLeft || ui->prevHotNode == id)) {
ui->hotNode = id; ui->hotNode = id;
} }
ui->hoveredNode = id;
} }
bool useHover = inRect && !(hovered.flags & UI_Flag_Ignore); bool useHover = inRect && !(hovered.flags & UI_Flag_Ignore);
@@ -407,7 +432,7 @@ bool ui_CheckboxRect(UI_Context *ui, bool *value, UI_Rect rect) {
bool clicked = false; bool clicked = false;
bool pointerInRect = 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) { if (pointerInRect) {
ui->cursorIsPointer = true; ui->cursorType = UI_Cursor_Pointer;
if (ui->prevHotNode == id && !ui->input->mouse.btnLeft) { if (ui->prevHotNode == id && !ui->input->mouse.btnLeft) {
*value = !*value; *value = !*value;
clicked = true; clicked = true;

View File

@@ -43,6 +43,7 @@ enum UI_Flag {
UI_Flag_3DScene=1<<3, UI_Flag_3DScene=1<<3,
UI_Flag_Center=1<<4, UI_Flag_Center=1<<4,
UI_Flag_Ignore=1<<7, // For optional parameters UI_Flag_Ignore=1<<7, // For optional parameters
UI_Flag_Pos_Absolute=1<<8, // Default is relative
// .. // ..
UI_Flag_COUNT, UI_Flag_COUNT,
}; };
@@ -55,6 +56,13 @@ struct UI_Padding {
real32 left; real32 left;
}; };
enum UI_Cursor {
UI_Cursor_Arrow=1<<0,
UI_Cursor_Pointer=1<<1,
UI_Cursor_Forbidden=1<<2,
UI_Cursor_Count=1<<3,
};
enum UI_TxtAlign { enum UI_TxtAlign {
UI_TxtAlign_Left=1<<0, // Default UI_TxtAlign_Left=1<<0, // Default
UI_TxtAlign_Center=1<<1, UI_TxtAlign_Center=1<<1,
@@ -110,7 +118,7 @@ struct UI_Rect {
/** /**
* Inherited from parent to propagate attributes. Actual string is ignored, only used for filling out defaults in * Inherited from parent to propagate attributes. Actual string is ignored, only used for filling out defaults in
* the next stringData pointer that is set. * 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 * This pointer is always set, as the global UI context will set it on the root node if it does not present its own
* text data. * text data.
*/ */
UI_RectStr *inheritedTextAttr; UI_RectStr *inheritedTextAttr;
@@ -123,13 +131,15 @@ struct UI_Context {
UI_RectList prevRects; UI_RectList prevRects;
UI_RectList rects; UI_RectList rects;
int32 hotNode; int32 hotNode;
int32 hoveredNode;
int32 prevHoveredNode;
int32 scene3DHandle; int32 scene3DHandle;
int32 prevHotNode; int32 prevHotNode;
Renderer *renderer; Renderer *renderer;
Input *prevInput; Input *prevInput;
Input *input; Input *input;
bool cursorIsPointer; enum UI_Cursor cursorType;
int32 rootRect; int32 rootRect;
int32 currRect; int32 currRect;
@@ -176,7 +186,7 @@ extern UI_Context *__UI_current_ctx__;
#define UI_TxtAttr(str, ...)\ #define UI_TxtAttr(str, ...)\
/** Generate UI text to be used as a value */\ /** 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__ }) ((UI_RectStr){ .color={-1,-1,-1,-1}, .alignment=-1, .xOffset=0, .yOffset=0, .fontSize=-1, .lineHeight=-1, .s=(str), __VA_ARGS__ })
#define UI_Txt(str, ...)\ #define UI_Txt(str, ...)\
/** Place UI text in the current position in the UI tree. Attaches to the current UI_Rect */\ /** Place UI text in the current position in the UI tree. Attaches to the current UI_Rect */\