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_thickness;
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) {
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);
vec4 out_color = frag_color * texture(rect_tex, uv);
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_thickness;
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[](
vec2(-1, -1),
@@ -44,4 +52,5 @@ void main() {
frag_border_thickness = border_thickness;
frag_border_color = border_color;
frag_softness = edge_softness;
uv = rectangle_uv[gl_VertexID];
}

View File

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

View File

@@ -7,5 +7,5 @@ in vec2 frag_uv_position;
uniform sampler2D glyph_atlas;
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_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
glGenBuffers(1, &result.glyphTableBufId);
glBindBuffer(GL_TEXTURE_BUFFER, result.glyphTableBufId);

View File

@@ -78,6 +78,7 @@ struct Soma {
struct {
GLFWcursor *pointer;
GLFWcursor *arrow;
GLFWcursor *forbidden;
} cursors;
} window;
@@ -110,6 +111,7 @@ void tryScheduleSolve(Soma *soma) {
for (EachEl(soma->polycubeInput, PolycubeInput, polycubeInput)) {
ListAppend(mappedInputs, polycubeInput->repr.space);
}
ClearList(soma->solveTaskCtx.solutions);
soma->solveTaskCtx.input = mappedInputs;
soma->solveTaskCtx.taskStatus = SolveTaskStatus_Solving;
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;
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_SetTxtAttr(.lineHeight=20, .fontSize=20);
UI(.padding={.left=20, .right=20}, .color=darkgrey, .flags=UI_Flag_HeightGrow | UI_Flag_Vertical) {
UI(.padding=UI_PadUniform(10)) UI_Txt(s("Somaesque"), .fontSize=26);
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")))) {
soma->state.displayingSolutions = false;
}
solveBtnId = UI_NextID();
rect = soma->state.displayingSolutions ? &btnRectBlue : &btnRect;
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) {
tryScheduleSolve(soma);
} 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_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(.padding=UI_PadUniform(10), .flags=UI_Flag_WidthGrow) {
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) {
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);
if (soma->state.displayedSolution >= 0 && soma->state.displayedSolution < soma->solutions.length) {
SomaSolution soln = soma->solutions.data[soma->state.displayedSolution];
@@ -476,7 +524,9 @@ static void updatePolycubeDisplay(Soma *soma) {
}
if (soma->state.displayingSolutions) {
if (soma->solutions.length > 0) {
show(s, soma->solutionNode);
}
if (soma->prevState.displayedPolycube >= 0 && soma->prevState.displayedPolycube < soma->polycubes.length) {
hide(s, soma->polycubes.data[soma->prevState.displayedPolycube]);
}
@@ -574,6 +624,7 @@ glDebugMessageCallback(glDebugCallback, NULL);
.cursors = {
.pointer = glfwCreateStandardCursor(GLFW_HAND_CURSOR),
.arrow = glfwCreateStandardCursor(GLFW_ARROW_CURSOR),
.forbidden = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR),
},
},
.scene = &mainScene,
@@ -636,6 +687,76 @@ glDebugMessageCallback(glDebugCallback, NULL);
Shader textShader = createShader(s("./assets/shaders/text.vertex.glsl"), s("./assets/shaders/text.fragment.glsl"));
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
real64 lastFrame = glfwGetTime();
real64 timeDelta = 1.0f / TARGET_FPS;
@@ -657,7 +778,8 @@ glDebugMessageCallback(glDebugCallback, NULL);
processInput(&soma);
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)) {
ListAppend(soma.solutions, PushListCopy(solutionsArena, SomaSolution, soma.solveTaskCtx.solutions.data[i]));
}
@@ -672,10 +794,19 @@ glDebugMessageCallback(glDebugCallback, NULL);
ui_Soma(&ui, &soma);
}
if (ui.cursorIsPointer) {
glfwSetCursor(soma.window.handle, soma.window.cursors.pointer);
} else {
switch (ui.cursorType) {
case UI_Cursor_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)
void renderEnd(Renderer *r) {
// 3D Scene
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
// --- 3D Scene ---
glViewport(r->sceneX, r->height - r->sceneY - r->sceneHeight, 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);
glUseProgram(r->solidShader->progId);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, r->testTexId);
glDisable(GL_DEPTH_TEST);
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);
// 1. Rects
// - 1. Rects
updateRectangleObjectBuffers(r);
setUniformMat4fv(r->solidShader, "projection", &ortho);
glBindVertexArray(r->rects.vao);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, r->rects.p0.buf.length);
// 2. Text
// - 2. Text
updateCharObjectBuffers(r);
glUseProgram(r->textShader->progId);
setUniformMat4fv(r->textShader, "projection", &ortho);
glActiveTexture(GL_TEXTURE0); CHECK();
glBindTexture(GL_TEXTURE_2D, r->activeFont->texId); CHECK();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, r->activeFont->texId);
glActiveTexture(GL_TEXTURE1); CHECK();
glBindTexture(GL_TEXTURE_BUFFER, r->activeFont->glyphTableTexId); CHECK();
setUniform1i(r->textShader, "glyph_table", 1); CHECK();
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_BUFFER, r->activeFont->glyphTableTexId);
setUniform1i(r->textShader, "glyph_table", 1);
glBindVertexArray(r->chars.vao); CHECK();
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, r->chars.begin.buf.length); CHECK();
glBindVertexArray(r->chars.vao);
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, r->chars.begin.buf.length);
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) {

View File

@@ -87,6 +87,8 @@ struct Renderer {
/** SceneGraphNode handle */
int32 light;
Mesh *cubeMesh;
int32 testTexId;
};
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,
.hotNode=0,
.prevHotNode=0,
.cursorIsPointer=false,
.cursorType=UI_Cursor_Arrow,
.scene3DHandle=0,
.input=NULL,
.prevInput=NULL,
@@ -72,6 +72,9 @@ void ui_placeText(UI_Context *ui, UI_RectStr strData) {
strDataCopy->fontSize = ui->renderer->activeFont->lineHeight;
}
}
if (strDataCopy->color.x == -1) {
strDataCopy->color = currRect->inheritedTextAttr->color;
}
if (strDataCopy->lineHeight == -1) {
strDataCopy->lineHeight = currRect->inheritedTextAttr->lineHeight;
if (strDataCopy->lineHeight == -1) {
@@ -115,11 +118,13 @@ void ui_sizingPass(UI_Context *ui, bool isXAxis, int32 rectHandle) {
if (isVertical != isXAxis) {
while (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) {
growableChildrenCount++;
}
real32 childBreadth = isXAxis ? child->resolvedWidth : child->resolvedHeight;
remainingSpace -= childBreadth + rect->childGap;
}
childHandle = child->nextSibling;
}
}
@@ -161,14 +166,23 @@ void ui_calcLayout(UI_Context *ui, bool isXAxis, int32 rectHandle) {
bool isVertical = (rect->flags & UI_Flag_Vertical);
real32 coord = isXAxis
? rect->x + rect->xOffset + rect->padding.left
: rect->y + rect->yOffset + rect->padding.top;
? rect->x + rect->padding.left
: rect->y + rect->padding.top;
real32 parentCoord = coord;
int32 childHandle = rect->firstChild;
UI_Rect *child;
while (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) {
child->x = child->flags & UI_Flag_Center && isVertical
? 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;
}
}
if (isXAxis) {
child->x += child->xOffset;
} else {
child->y += child->yOffset;
}
ui_calcLayout(ui, isXAxis, childHandle);
if (!isVertical == isXAxis) {
if (!(child->flags & UI_Flag_Pos_Absolute) && !isVertical == isXAxis) {
coord += isXAxis ? child->resolvedWidth : child->resolvedHeight;
coord += rect->childGap;
}
@@ -195,9 +216,11 @@ void ui_begin(UI_Context *ui) {
arenaFreeFrom(ui->arena, 0);
ClearList(ui->prevRects);
ListAppendList(ui->prevRects, ui->rects);
ui->cursorIsPointer = false;
ui->cursorType = UI_Cursor_Arrow;
ui->prevHotNode = ui->hotNode;
ui->prevHoveredNode = ui->hoveredNode;
ui->hotNode = 0;
ui->hoveredNode = 0;
ui->scene3DHandle = 0;
ui->rootRect = 0;
ui->currRect = 0;
@@ -205,6 +228,7 @@ void ui_begin(UI_Context *ui) {
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_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) {
@@ -298,9 +322,9 @@ void ui_openElement(UI_Context *ui, UI_Rect rect) {
if (currRect->stringData) {
UI_RectStr *inheritedTextAttr = PushStructZero(ui->arena, UI_RectStr);
*inheritedTextAttr = *currRect->stringData;
inheritedTextAttr->s = s("");
newRect->inheritedTextAttr = inheritedTextAttr;
} else {
*newRect->inheritedTextAttr = *currRect->stringData;
newRect->inheritedTextAttr->s = (string){ .str=NULL, .length=0 };
} else if (currRect) {
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]);
if (inRect) {
ui->cursorIsPointer = true;
ui->cursorType = UI_Cursor_Pointer;
if (ui->prevHotNode == id && !ui->input->mouse.btnLeft) {
clicked = true;
} else if (ui->input->mouse.btnLeft && (!ui->prevInput->mouse.btnLeft || ui->prevHotNode == id)) {
ui->hotNode = id;
}
ui->hoveredNode = id;
}
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 pointerInRect = pointInRect(ui->input->mouse.point.x, ui->input->mouse.point.y, ui->prevRects.data[id]);
if (pointerInRect) {
ui->cursorIsPointer = true;
ui->cursorType = UI_Cursor_Pointer;
if (ui->prevHotNode == id && !ui->input->mouse.btnLeft) {
*value = !*value;
clicked = true;

View File

@@ -43,6 +43,7 @@ enum UI_Flag {
UI_Flag_3DScene=1<<3,
UI_Flag_Center=1<<4,
UI_Flag_Ignore=1<<7, // For optional parameters
UI_Flag_Pos_Absolute=1<<8, // Default is relative
// ..
UI_Flag_COUNT,
};
@@ -55,6 +56,13 @@ struct UI_Padding {
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 {
UI_TxtAlign_Left=1<<0, // Default
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
* 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.
*/
UI_RectStr *inheritedTextAttr;
@@ -123,13 +131,15 @@ struct UI_Context {
UI_RectList prevRects;
UI_RectList rects;
int32 hotNode;
int32 hoveredNode;
int32 prevHoveredNode;
int32 scene3DHandle;
int32 prevHotNode;
Renderer *renderer;
Input *prevInput;
Input *input;
bool cursorIsPointer;
enum UI_Cursor cursorType;
int32 rootRect;
int32 currRect;
@@ -176,7 +186,7 @@ extern UI_Context *__UI_current_ctx__;
#define UI_TxtAttr(str, ...)\
/** Generate UI text to be used as a value */\
((UI_RectStr){ .alignment=-1, .xOffset=0, .yOffset=0, .fontSize=-1, .lineHeight=-1, .s=(str), __VA_ARGS__ })
((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, ...)\
/** Place UI text in the current position in the UI tree. Attaches to the current UI_Rect */\