render 3d to tex
This commit is contained in:
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
145
src/main.c
145
src/main.c
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
27
src/render.c
27
src/render.c
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
45
src/ui.c
45
src/ui.c
@@ -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;
|
||||||
|
|||||||
16
src/ui.h
16
src/ui.h
@@ -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 */\
|
||||||
|
|||||||
Reference in New Issue
Block a user