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_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;
|
||||
};
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -7,7 +7,7 @@ uniform vec3 camera;
|
||||
|
||||
in vec3 normal;
|
||||
in vec3 frag_position;
|
||||
|
||||
|
||||
void main() {
|
||||
vec4 light_color = vec4(1, 1, 1, 1);
|
||||
vec3 normal_norm = normalize(normal);
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
237
src/main.c
237
src/main.c
@@ -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,56 +401,102 @@ static void ui_Soma(UI_Context *ui, Soma *soma) {
|
||||
UI_Rect btnHoverRectBlue = btnRectBlue;
|
||||
btnHoverRectBlue.color = lightblueHighlight;
|
||||
|
||||
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);
|
||||
}
|
||||
UI(.flags=UI_Flag_Vertical | UI_Flag_HeightGrow | UI_Flag_WidthGrow) {
|
||||
UI(.padding=UI_PadUniform(10), .color=darkgrey, .flags=UI_Flag_WidthGrow) {
|
||||
UI(.childGap=10) {
|
||||
if (ui_ButtonWithHover(ui, btnRect, btnHoverRect, UI_TxtAttr(s("Previous")))) {
|
||||
advanceDisplayReverse(soma);
|
||||
}
|
||||
int32 solveBtnId = 0;
|
||||
|
||||
if (ui_ButtonWithHover(ui, btnRect, btnHoverRect, UI_TxtAttr(s("Next")))) {
|
||||
advanceDisplay(soma);
|
||||
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(.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);
|
||||
}
|
||||
UI(.flags=UI_Flag_Vertical | UI_Flag_HeightGrow | UI_Flag_WidthGrow) {
|
||||
UI(.padding=UI_PadUniform(10), .color=darkgrey, .flags=UI_Flag_WidthGrow) {
|
||||
UI(.childGap=10) {
|
||||
if (ui_ButtonWithHover(ui, btnRect, btnHoverRect, UI_TxtAttr(s("Previous")))) {
|
||||
advanceDisplayReverse(soma);
|
||||
}
|
||||
|
||||
if (ui_ButtonWithHover(ui, btnRect, btnHoverRect, UI_TxtAttr(s("Next")))) {
|
||||
advanceDisplay(soma);
|
||||
}
|
||||
}
|
||||
UI(.flags=UI_Flag_WidthGrow);
|
||||
UI(.childGap=10) {
|
||||
UI_Rect *rect = soma->state.displayingSolutions ? &btnRect : &btnRectBlue;
|
||||
UI_Rect *hoverRect = soma->state.displayingSolutions ? &btnHoverRect : &btnHoverRectBlue;
|
||||
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;
|
||||
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 {
|
||||
soma->state.displayingSolutions = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ui->hoveredNode == solveBtnId && !canSolve) {
|
||||
ui->cursorType = UI_Cursor_Forbidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
UI(.flags=UI_Flag_WidthGrow);
|
||||
UI(.childGap=10) {
|
||||
UI_Rect *rect = soma->state.displayingSolutions ? &btnRect : &btnRectBlue;
|
||||
UI_Rect *hoverRect = soma->state.displayingSolutions ? &btnHoverRect : &btnHoverRectBlue;
|
||||
if (ui_ButtonWithHover(ui, *rect, *hoverRect, UI_TxtAttr(s("Design")))) {
|
||||
soma->state.displayingSolutions = false;
|
||||
}
|
||||
rect = soma->state.displayingSolutions ? &btnRectBlue : &btnRect;
|
||||
hoverRect = soma->state.displayingSolutions ? &btnHoverRectBlue : &btnHoverRect;
|
||||
if (ui_ButtonWithHover(ui, *rect, *hoverRect, UI_TxtAttr(s("Solve")))) {
|
||||
if (!soma->state.displayingSolutions) {
|
||||
tryScheduleSolve(soma);
|
||||
} else {
|
||||
soma->state.displayingSolutions = false;
|
||||
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) {
|
||||
UI_Txt(s("Solving..."));
|
||||
} else {
|
||||
bool soln = soma->state.displayingSolutions;
|
||||
UI_Txt(
|
||||
strPrintf(ui->arena, "%S #%d (%d total)",
|
||||
soln ? s("Solution") : s("Polycube"),
|
||||
(soln ? soma->state.displayedSolution : soma->state.displayedPolycube) + 1,
|
||||
soln ? soma->solutions.length : soma->polycubeInput.length),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
UI(.flags=UI_Flag_Vertical | UI_Flag_WidthGrow | UI_Flag_HeightGrow | UI_Flag_3DScene) {
|
||||
UI_SetTxtAttr(.fontSize=26, .alignment=UI_TxtAlign_Right);
|
||||
UI(.flags=UI_Flag_HeightGrow);
|
||||
UI(.padding=UI_PadUniform(10), .flags=UI_Flag_WidthGrow) {
|
||||
if (soma->solveTaskCtx.taskStatus == SolveTaskStatus_Solving) {
|
||||
UI_Txt(s("Solving..."));
|
||||
} else {
|
||||
bool soln = soma->state.displayingSolutions;
|
||||
UI_Txt(
|
||||
strPrintf(ui->arena, "%S #%d (%d total)",
|
||||
soln ? s("Solution") : s("Polycube"),
|
||||
(soln ? soma->state.displayedSolution : soma->state.displayedPolycube) + 1,
|
||||
soln ? soma->solutions.length : soma->polycubeInput.length),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 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});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -458,7 +506,7 @@ static void ui_Soma(UI_Context *ui, Soma *soma) {
|
||||
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) {
|
||||
show(s, soma->solutionNode);
|
||||
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]);
|
||||
}
|
||||
@@ -541,9 +591,9 @@ int mainGfx() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
glDebugMessageCallback(glDebugCallback, NULL);
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
glDebugMessageCallback(glDebugCallback, NULL);
|
||||
|
||||
Scene mainScene = createScene(arena);
|
||||
Camera cam = createCamera(winWidth, winHeight);
|
||||
@@ -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 {
|
||||
glfwSetCursor(soma.window.handle, soma.window.cursors.arrow);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
|
||||
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) {
|
||||
|
||||
@@ -87,6 +87,8 @@ struct Renderer {
|
||||
/** SceneGraphNode handle */
|
||||
int32 light;
|
||||
Mesh *cubeMesh;
|
||||
|
||||
int32 testTexId;
|
||||
};
|
||||
|
||||
Renderer createRenderer(Arena *arena, Scene *scene, Camera *cam, int32 light);
|
||||
|
||||
67
src/ui.c
67
src/ui.c
@@ -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_HeightGrow && !isXAxis || child->flags & UI_Flag_WidthGrow && isXAxis) {
|
||||
growableChildrenCount++;
|
||||
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;
|
||||
}
|
||||
real32 childBreadth = isXAxis ? child->resolvedWidth : child->resolvedHeight;
|
||||
remainingSpace -= childBreadth + rect->childGap;
|
||||
childHandle = child->nextSibling;
|
||||
}
|
||||
}
|
||||
@@ -161,27 +166,43 @@ 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 (isXAxis) {
|
||||
child->x = child->flags & UI_Flag_Center && isVertical
|
||||
? coord + (rect->resolvedWidth - rect->padding.left - rect->padding.right)/2 - child->resolvedWidth/2
|
||||
: coord;
|
||||
if (child->flags & UI_Flag_Pos_Absolute) {
|
||||
if (isXAxis) {
|
||||
child->x = parentCoord;
|
||||
} else {
|
||||
child->y = parentCoord;
|
||||
}
|
||||
} else {
|
||||
child->y = child->flags & UI_Flag_Center && !isVertical
|
||||
? coord + (rect->resolvedHeight - rect->padding.top - rect->padding.bottom)/2 - child->resolvedHeight/2
|
||||
: coord;
|
||||
if (isXAxis) {
|
||||
child->x = child->flags & UI_Flag_Center && isVertical
|
||||
? coord + (rect->resolvedWidth - rect->padding.left - rect->padding.right)/2 - child->resolvedWidth/2
|
||||
: coord;
|
||||
} else {
|
||||
child->y = child->flags & UI_Flag_Center && !isVertical
|
||||
? 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;
|
||||
|
||||
16
src/ui.h
16
src/ui.h
@@ -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 */\
|
||||
|
||||
Reference in New Issue
Block a user