diff options
| author | iamcheeseman <[email protected]> | 2026-04-06 17:04:05 -0400 |
|---|---|---|
| committer | iamcheeseman <[email protected]> | 2026-04-06 17:06:53 -0400 |
| commit | 957c64c7b8b5e98d8a03dd84c7e27e7991fb9dbc (patch) | |
| tree | f5fc230703791cee8d8e7851fb87eaef07ae63a2 /micro | |
Initial commit
Diffstat (limited to 'micro')
| -rw-r--r-- | micro/context.c | 114 | ||||
| -rw-r--r-- | micro/context.h | 36 | ||||
| -rw-r--r-- | micro/mat4.c | 83 | ||||
| -rw-r--r-- | micro/mat4.h | 18 | ||||
| -rw-r--r-- | micro/micro.c | 38 | ||||
| -rw-r--r-- | micro/renderer.c | 360 | ||||
| -rw-r--r-- | micro/renderer.h | 77 | ||||
| -rw-r--r-- | micro/shader.frag | 12 | ||||
| -rw-r--r-- | micro/shader.vert | 18 | ||||
| -rw-r--r-- | micro/tex.c | 159 | ||||
| -rw-r--r-- | micro/tex.h | 37 |
11 files changed, 952 insertions, 0 deletions
diff --git a/micro/context.c b/micro/context.c new file mode 100644 index 0000000..226b942 --- /dev/null +++ b/micro/context.c @@ -0,0 +1,114 @@ +#include "context.h" +#include "renderer.h" + +#include <math.h> + +struct texture tex; +struct render_texture rtex; + +static +void init_glfw(struct ctx *ctx) +{ + if (glfwInit() < 0) { + log_fatal(ERR_GLFW_INIT, "could not init GLFW."); + } + + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + + ctx->win = glfwCreateWindow( + ctx->creation_hints.win_width, + ctx->creation_hints.win_height, + ctx->creation_hints.win_title, + NULL, + NULL + ); + + if (!ctx->win) { + log_fatal(ERR_GLFW_INIT, "could not create GLFW window."); + } + + glfwMakeContextCurrent(ctx->win); + + glfwSwapInterval(0); +} + +static +void deinit_glfw(struct ctx *ctx) +{ + glfwDestroyWindow(ctx->win); + glfwTerminate(); +} + +void init_ctx(struct ctx *ctx, struct ctx_hints creation_hints) +{ + ctx->creation_hints = creation_hints; + ctx->ticrate = 1.0 / creation_hints.ticrate; + ctx->prev_tic_ts = 0; + init_glfw(ctx); + init_renderer(&ctx->renderer, ctx); + + tex = load_texture("test_img.qoi"); + rtex = create_render_texture(320, 320, false); +} + +void deinit_ctx(struct ctx *ctx) +{ + free_texture(tex); + free_render_texture(rtex); + deinit_renderer(&ctx->renderer); + deinit_glfw(ctx); +} + +bool is_game_running(struct ctx *ctx) +{ + return !glfwWindowShouldClose(ctx->win); +} + +int game_tick(struct ctx *ctx) +{ + double current_time = glfwGetTime(); + double time_since_tic = current_time - ctx->prev_tic_ts; + + int tics = time_since_tic / ctx->ticrate; + if (tics > 0) { + ctx->prev_tic_ts = current_time; + glfwPollEvents(); + } + return tics; +} + +void game_draw(struct ctx *ctx) +{ + bind_render_texture(&ctx->renderer, NULL); + + draw_clear((struct color){0.2, 0.2, 0.2, 1}); + + bind_render_texture(&ctx->renderer, &rtex); + draw_clear((struct color){ + 1 - (cos(glfwGetTime()) + 1) / 2, + (sin(glfwGetTime()) + 1) / 2, + (cos(glfwGetTime()) + 1) / 2, + 1 + }); + + draw_rect(&ctx->renderer, 10, 10, 50, 50); + + set_blend_mode(&ctx->renderer, BLEND_ADD); + draw_texture(&ctx->renderer, tex, 100, 100); + draw_texture(&ctx->renderer, tex, 104, 104); + + set_blend_mode(&ctx->renderer, BLEND_ALPHA); + draw_texture(&ctx->renderer, tex, 100, 121); + bind_render_texture(&ctx->renderer, NULL); + + draw_texture( + &ctx->renderer, + rtex.color, + (400 - 320 * 0.5) + sin(glfwGetTime()) * 100, + 200 - 320 * 0.5 + ); + + end_draw(&ctx->renderer); + + glfwSwapBuffers(ctx->win); +} diff --git a/micro/context.h b/micro/context.h new file mode 100644 index 0000000..248c475 --- /dev/null +++ b/micro/context.h @@ -0,0 +1,36 @@ +#ifndef __MICRO_CONTEXT_H__ +#define __MICRO_CONTEXT_H__ + +#include <GLFW/glfw3.h> + +#include "common.h" +#include "renderer.h" + +struct ctx_hints { + int win_width; + int win_height; + const char *win_title; + int ticrate; +}; + +struct ctx { + struct ctx_hints creation_hints; + GLFWwindow *win; + double ticrate; + double prev_tic_ts; + + struct renderer renderer; +}; + +void init_ctx(struct ctx *ctx, struct ctx_hints creation_hints); +void deinit_ctx(struct ctx *ctx); + +// Whether or not the main loop should continue executing. +bool is_game_running(struct ctx *ctx); +// Handle events, tick game logic, etc. Returns the amount of times you should +// tic. +int game_tick(struct ctx *ctx); +// Draw stuff at an interpolated state. +void game_draw(struct ctx *ctx); + +#endif // __MICRO_CONTEXT_H__ diff --git a/micro/mat4.c b/micro/mat4.c new file mode 100644 index 0000000..804c204 --- /dev/null +++ b/micro/mat4.c @@ -0,0 +1,83 @@ +#include "mat4.h" + +#include <string.h> +#include <math.h> + +void mat4_identity(mat4 m) +{ + memset(m, 0, sizeof(mat4)); + m[0][0] = 1; + m[1][1] = 1; + m[2][2] = 1; + m[3][3] = 1; +} + +void mat4_translate(mat4 m, float x, float y) +{ + mat4_identity(m); + m[0][3] = x; + m[1][3] = y; +} + +void mat4_scale(mat4 m, float x, float y) +{ + mat4_identity(m); + m[0][0] = x; + m[1][1] = y; +} + +void mat4_ortho( + mat4 m, + float l, float r, + float t, float b, + float n, float f +) +{ + mat4_identity(m); + + m[0][0] = 2.0 / (r - l); + m[1][1] = 2.0 / (t - b); + m[2][2] = -2.0 / (f - n); + m[0][3] = -(r + l) / (r - l); + m[1][3] = -(t + b) / (t - b); + m[2][3] = -(f + n) / (f - n); +} + +void mat4_perspective(mat4 mat, float yfov, float a, float n, float f) +{ + memset(mat, 0, sizeof(mat4)); + + // 1000 + // 0100 + // 0010 + // 0001 + // + // 00 01 02 03 + // 10 11 12 13 + // 20 21 22 23 + // 30 31 32 33 + // + // 0 4 8 12 + // 1 5 9 14 + // 2 6 10 15 + // 3 7 11 16 + + float cotan = 1.0 / tanf(yfov * DEG2RAD * 0.5); + mat[0][0] = cotan / a; + mat[1][1] = cotan; + mat[2][2] = (f + n) / (n - f); + mat[3][2] = -1.0; + mat[2][3] = 2.0 * n * f / (n - f); +} + + +void mat4_mult(mat4 dst, mat4 a, mat4 b) +{ + mat4 res; + for (int r = 0; r < 4; r++) + for (int c = 0; c < 4; c++) + for (int k = 0; k < 4; k++) + res[r][c] = a[r][k] * b[k][c]; + // memcpy after so that dst can be one of a or b + memcpy(dst, res, sizeof(mat4)); +} diff --git a/micro/mat4.h b/micro/mat4.h new file mode 100644 index 0000000..3d99312 --- /dev/null +++ b/micro/mat4.h @@ -0,0 +1,18 @@ +#ifndef __MICRO_MAT4_H__ +#define __MICRO_MAT4_H__ + +#include "common.h" + +typedef float mat4[4][4]; + +void mat4_identity(mat4 m); +void mat4_translate(mat4 m, float x, float y); +void mat4_mult(mat4 dst, mat4 a, mat4 b); +void mat4_ortho( + mat4 m, + float l, float r, + float t, float b, + float n, float f +); + +#endif // __MICRO_MAT4_H__ diff --git a/micro/micro.c b/micro/micro.c new file mode 100644 index 0000000..0a87a6d --- /dev/null +++ b/micro/micro.c @@ -0,0 +1,38 @@ +#include <stdio.h> + +#include "common.h" +#include "context.h" +#include "uscript.h" + +int main(void) +{ + init_mem(); + + us_init(); + us_load_file("main.us"); + us_deinit(); + + // struct ctx ctx; + // struct ctx_hints hints = { + // .win_width = 800, + // .win_height = 400, + // .win_title = "microengine", + // .ticrate = 20, + // }; + // init_ctx(&ctx, hints); + // + // while (is_game_running(&ctx)) { + // int tics = game_tick(&ctx); + // for (int i = 0; i < tics; i++) { + // printf("tick\n"); + // } + // game_draw(&ctx); + // + // free_temp_allocs(); + // } + // + // deinit_ctx(&ctx); + + deinit_mem(); + return 0; +} diff --git a/micro/renderer.c b/micro/renderer.c new file mode 100644 index 0000000..d65c3dc --- /dev/null +++ b/micro/renderer.c @@ -0,0 +1,360 @@ +#include "renderer.h" + +#include <stdio.h> +#include <string.h> + +#include <glad/glad.h> +#include <GLFW/glfw3.h> + +#include "context.h" +#include "dyn_arr.h" + +char vert_shader[] = + "#version 320 es\n" + "\n" + "layout (location = 0) in vec3 v_pos;\n" + "layout (location = 1) in vec2 v_uv;\n" + "layout (location = 2) in vec4 v_col;\n" + "layout (location = 3) in int v_tex;\n" + "\n" + "out vec2 f_uv;\n" + "out vec4 f_col;\n" + "out flat int f_tex;\n" + "\n" + "uniform mat4 projection;" + "\n" + "void main()\n" + "{\n" + " gl_Position = projection * vec4(v_pos, 1.0);\n" + " f_uv = v_uv;\n" + " f_col = v_col;\n" + " f_tex = v_tex;\n" + "}\n" +; + +char frag_shader[] = + "#version 320 es\n" + "precision mediump float;\n" + "\n" + "in vec2 f_uv;\n" + "in vec4 f_col;\n" + "in flat int f_tex;\n" + "\n" + "out vec4 out_color;\n" + "\n" + "uniform sampler2D texs[16];" + "\n" + "void main()\n" + "{\n" + " out_color = texture(texs[f_tex], f_uv) * f_col;\n" + "}\n" +; + +static +u32 load_shader(const char *src, u32 type) +{ + u32 shader = glCreateShader(type); + glShaderSource(shader, 1, &src, NULL); + glCompileShader(shader); + + int ok; + glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); + if (!ok) { + char data[1024]; + glGetShaderInfoLog(shader, 1024, NULL, data); + if (type == GL_FRAGMENT_SHADER) + log_fatal(ERR_OPENGL_SHADER, "FRAG: %s", data); + else if (type == GL_VERTEX_SHADER) + log_fatal(ERR_OPENGL_SHADER, "VERT: %s", data); + } + + return shader; +} + +static +u32 load_shader_prog(const char *vert_src, const char *frag_src) +{ + u32 vert = load_shader(vert_src, GL_VERTEX_SHADER); + u32 frag = load_shader(frag_src, GL_FRAGMENT_SHADER); + + u32 prog = glCreateProgram(); + glAttachShader(prog, vert); + glAttachShader(prog, frag); + + glLinkProgram(prog); + + glDeleteShader(vert); + glDeleteShader(frag); + + int ok; + glGetProgramiv(prog, GL_LINK_STATUS, &ok); + if (!ok) { + char data[1024]; + glGetProgramInfoLog(prog, 1024, NULL, data); + log_fatal(ERR_OPENGL_SHADER, "%s", data); + } + + return prog; +} + +void init_renderer(struct renderer *r, struct ctx *ctx) +{ + r->ctx = ctx; + + glfwMakeContextCurrent(ctx->win); + + if (!gladLoadGLES2Loader((GLADloadproc)glfwGetProcAddress)) { + log_fatal(ERR_OPENGL_INIT, "could not load OpenGL ES"); + } + + u8 white_1x1_data[] = { + 255, 255, 255, 255, + }; + r->white_1x1 = load_texture_from_mem(white_1x1_data, 1, 1); + + r->shader_program = load_shader_prog(vert_shader, frag_shader); + glUseProgram(r->shader_program); + + r->dc.next_tex_bind = 0; + r->dc.cur_tex_bind = 0; + r->dc.mesh_start = 0; + r->dc.vert_count = 0; + r->dc.xyz = da_create(struct vec3, 0); + r->dc.uv = da_create(struct vec2, 0); + r->dc.col = da_create(struct color, 0); + r->dc.tex = da_create(int, 0); + r->dc.indices = da_create(u16, 0); + + r->cur_col = (struct color){1, 1, 1, 1}; + + glEnable(GL_BLEND); + + set_blend_mode(r, BLEND_ALPHA); + bind_render_texture(r, NULL); +} + +void deinit_renderer(struct renderer *r) +{ + free_texture(r->white_1x1); + glDeleteProgram(r->shader_program); + + da_free(r->dc.xyz); + da_free(r->dc.uv); + da_free(r->dc.col); + da_free(r->dc.tex); + da_free(r->dc.indices); +} + +void flush_pending(struct renderer *r) +{ + int handles[MAX_BOUND_TEX]; + memset(handles, 0, sizeof(handles)); + for (int i = 0; i < r->dc.next_tex_bind; i++) { + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_2D, r->dc.textures[i].handle); + handles[i] = i; + } + + // TODO: store this location + int loc = glGetUniformLocation(r->shader_program, "texs"); + glUniform1iv(loc, MAX_BOUND_TEX, handles); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, r->dc.xyz); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, r->dc.uv); + glEnableVertexAttribArray(1); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 0, r->dc.col); + glEnableVertexAttribArray(2); + glVertexAttribIPointer(3, 1, GL_INT, 0, r->dc.tex); + glEnableVertexAttribArray(3); + + glDrawElements( + GL_TRIANGLES, + da_len(r->dc.indices), + GL_UNSIGNED_SHORT, + r->dc.indices + ); + + da_clear(r->dc.xyz); + da_clear(r->dc.uv); + da_clear(r->dc.col); + da_clear(r->dc.tex); + da_clear(r->dc.indices); + r->dc.next_tex_bind = 0; + r->dc.cur_tex_bind = 0; + r->dc.mesh_start = 0; + r->dc.vert_count = 0; +} + +static +void start_new_mesh(struct renderer *r, struct texture tex) +{ + struct draw_call *dc = &r->dc; + int found_binding = -1; + for (int i = 0; i < dc->next_tex_bind; i++) { + if (dc->textures[i].handle == tex.handle) { + found_binding = i; + break; + } + } + + if (found_binding < 0) { + if (dc->next_tex_bind + 1 > MAX_BOUND_TEX) + flush_pending(r); + + dc->cur_tex_bind = dc->next_tex_bind++; + dc->textures[dc->cur_tex_bind] = tex; + } else { + dc->cur_tex_bind = found_binding; + } + + dc->mesh_start = dc->vert_count; +} + +static +void add_vertex(struct renderer *r, struct vertex vert) +{ + da_append(struct vec3, &r->dc.xyz, vert.xyz); + da_append(struct vec2, &r->dc.uv, vert.uv); + da_append(struct color, &r->dc.col, vert.col); + da_append(int, &r->dc.tex, r->dc.cur_tex_bind); + r->dc.vert_count++; +} + +static +void add_index(struct renderer *r, u16 idx) +{ + da_append(u16, &r->dc.indices, r->dc.mesh_start + idx); +} + +void draw_clear(struct color col) +{ + glClearColor(col.r, col.g, col.b, col.a); + glClear(GL_COLOR_BUFFER_BIT); +} + +static +void draw_quad( + struct renderer *r, + struct texture tex, + float x, float y, + float w, float h +) { + float brx = x + w; + float bry = y + h; + + start_new_mesh(r, tex); + + // tl + add_vertex(r, (struct vertex){ + .xyz = {x, y, 0}, + .uv = {0, 0}, + .col = r->cur_col, + }); + + // tr + add_vertex(r, (struct vertex){ + .xyz = {brx, y, 0}, + .uv = {1, 0}, + .col = r->cur_col, + }); + + // bl + add_vertex(r, (struct vertex){ + .xyz = {x, bry, 0}, + .uv = {0, 1}, + .col = r->cur_col, + }); + + // br + add_vertex(r, (struct vertex){ + .xyz = {brx, bry, 0}, + .uv = {1, 1}, + .col = r->cur_col, + }); + + add_index(r, 0); + add_index(r, 1); + add_index(r, 2); + + add_index(r, 2); + add_index(r, 1); + add_index(r, 3); +} + +void draw_rect(struct renderer *r, float x, float y, float w, float h) +{ + draw_quad(r, r->white_1x1, x, y, w, h); +} + +void draw_texture(struct renderer *r, struct texture tex, float x, float y) +{ + draw_quad(r, tex, x, y, tex.width, tex.height); +} + +static +void display_errors(void) +{ +#ifdef UE_DEBUG + u32 err; + while ((err = glGetError()) != GL_NO_ERROR) { + switch (err) { + case GL_INVALID_ENUM: printf("GL_INVALID_ENUM\n"); break; + case GL_INVALID_VALUE: printf("GL_INVALID_VALUE\n"); break; + case GL_INVALID_OPERATION: printf("GL_INVALID_OPERATION\n"); break; + case GL_OUT_OF_MEMORY: printf("GL_OUT_OF_MEMORY\n"); break; + default: printf("OPENGL ERROR: %x\n", err); break; + } + } +#endif +} + +void set_blend_mode(struct renderer *r, enum blend_mode mode) +{ + struct draw_call *dc = &r->dc; + + if (mode != dc->blend_mode) { + flush_pending(r); + dc->blend_mode = mode; + } + + switch (mode) { + case BLEND_ALPHA: + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + case BLEND_ADD: + glBlendFunc(GL_ONE, GL_ONE); + break; + } +} + +void bind_render_texture(struct renderer *r, struct render_texture *rtex) +{ + flush_pending(r); + + u32 handle = 0; + int width; + int height; + + if (rtex == NULL) { + glfwGetWindowSize(r->ctx->win, &width, &height); + mat4_ortho(r->projection, 0, width, 0, height, 0, 1); + } else { + handle = rtex->handle; + width = rtex->width; + height = rtex->height; + mat4_ortho(r->projection, 0, width, height, 0, 0, 1); + } + + glViewport(0, 0, width, height); + glBindFramebuffer(GL_FRAMEBUFFER, handle); + + int loc = glGetUniformLocation(r->shader_program, "projection"); + glUniformMatrix4fv(loc, 1, GL_TRUE, (float*)r->projection); +} + +void end_draw(struct renderer *r) +{ + flush_pending(r); + display_errors(); +} diff --git a/micro/renderer.h b/micro/renderer.h new file mode 100644 index 0000000..7ddddf5 --- /dev/null +++ b/micro/renderer.h @@ -0,0 +1,77 @@ +#ifndef __MICRO_RENDERER_H__ +#define __MICRO_RENDERER_H__ + +#include "common.h" +#include "mat4.h" +#include "tex.h" + +#define MAX_BOUND_TEX 16 + +enum blend_mode { + BLEND_ALPHA, + BLEND_ADD, +}; + +struct color { + float r; + float g; + float b; + float a; +}; + +struct vec3 { + float x; + float y; + float z; +}; + +struct vec2 { + float x; + float y; +}; + +struct vertex { + struct vec3 xyz; + struct vec2 uv; + struct color col; +}; + +struct draw_call { + struct texture textures[MAX_BOUND_TEX]; + int next_tex_bind; + int cur_tex_bind; + int mesh_start; + int vert_count; + enum blend_mode blend_mode; + + struct vec3 *xyz; + struct vec2 *uv; + struct color *col; + int *tex; + + u16 *indices; +}; + +struct renderer { + struct ctx *ctx; + struct texture white_1x1; + struct draw_call dc; + struct color cur_col; + u32 shader_program; + + mat4 projection; +}; + +void init_renderer(struct renderer *r, struct ctx *ctx); +void deinit_renderer(struct renderer *r); +void flush_pending(struct renderer *r); + +void draw_clear(struct color col); +void draw_rect(struct renderer *r, float x, float y, float w, float h); +void draw_texture(struct renderer *r, struct texture tex, float x, float y); +void set_blend_mode(struct renderer *r, enum blend_mode mode); +// NULL to unbind +void bind_render_texture(struct renderer *r, struct render_texture *rtex); +void end_draw(struct renderer *r); + +#endif // __MICRO_RENDERER_H__ diff --git a/micro/shader.frag b/micro/shader.frag new file mode 100644 index 0000000..b60ea2a --- /dev/null +++ b/micro/shader.frag @@ -0,0 +1,12 @@ +#version 320 es + +in vec2 f_uv; +in vec4 f_col; +in sampler2D f_tex; + +out vec4 out_color; + +void main() +{ + out_color = texture2D(f_tex, f_uv) * f_col; +} diff --git a/micro/shader.vert b/micro/shader.vert new file mode 100644 index 0000000..5fa99ec --- /dev/null +++ b/micro/shader.vert @@ -0,0 +1,18 @@ +#version 320 es + +layout (location = 0) in vec2 v_pos; +layout (location = 1) in vec2 v_uv; +layout (location = 2) in vec4 v_col; +layout (location = 3) in sampler2D v_tex; + +out vec2 f_uv; +out vec4 f_col; +out sampler2D f_tex; + +void main() +{ + gl_Position = vec4(v_pos, 0.0, 1.0); + f_uv = v_uv; + f_col = v_col; + f_tex = v_tex; +} diff --git a/micro/tex.c b/micro/tex.c new file mode 100644 index 0000000..25f0fe9 --- /dev/null +++ b/micro/tex.c @@ -0,0 +1,159 @@ +#include "tex.h" + +#include <glad/glad.h> +#include <GLFW/glfw3.h> + +// TODO: write my own qoi implemenation, so that this isn't just here +#define QOI_IMPLEMENTATION +#include <qoi.h> + +#include "renderer.h" +#include "context.h" + +static +struct texture load_texture_from_handle(u32 handle, int width, int height) +{ + struct texture tex; + tex.handle = handle; + tex.width = width; + tex.height = height; + return tex; +} + +static +u32 get_texture_handle(u8 *data, int width, int height, u32 type) +{ + u32 handle; + glGenTextures(1, &handle); + glBindTexture(GL_TEXTURE_2D, handle); + glTexImage2D( + GL_TEXTURE_2D, + 0, + type, + width, + height, + 0, + type, + GL_UNSIGNED_BYTE, + data + ); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + return handle; +} + +struct texture load_texture(const char *file_path) +{ + size_t size; + char *file_dat = read_file(file_path, &size); + + qoi_desc desc; + void *image_dat = qoi_decode(file_dat, size, &desc, 4); + + struct texture tex = load_texture_from_mem( + image_dat, + desc.width, + desc.height + ); + + mem_free(file_dat); + mem_free(image_dat); + + return tex; +} + +struct texture load_texture_from_mem(u8 *data, int width, int height) +{ + struct texture tex; + tex.width = width; + tex.height = height; + + tex.handle = get_texture_handle(data, width, height, GL_RGBA); + glGenerateMipmap(GL_TEXTURE_2D); + + return tex; +} + +void free_texture(struct texture tex) +{ + glDeleteTextures(1, &tex.handle); +} + +bool is_texture_init(struct texture tex) +{ + return tex.handle != 0 && tex.width > 0 && tex.height > 0; +} + +struct render_texture create_render_texture( + int width, + int height, + bool add_depth +) +{ + u32 fbo; + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + struct texture depth = {0}; + + u32 color_handle = get_texture_handle(NULL, width, height, GL_RGB); + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + color_handle, + 0 + ); + + struct texture color = load_texture_from_handle( + color_handle, + width, + height + ); + + if (add_depth) { + u32 depth_handle = get_texture_handle( + NULL, + width, + height, + GL_DEPTH_COMPONENT + ); + + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, + depth_handle, + 0 + ); + + depth = load_texture_from_handle( + depth_handle, + width, + height + ); + } + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + log_warn("framebuffer %d could not be completed", fbo); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + struct render_texture rtex; + rtex.handle = fbo; + rtex.color = color; + rtex.depth = depth; + rtex.width = width; + rtex.height = height; + return rtex; +} + +void free_render_texture(struct render_texture rtex) +{ + glDeleteFramebuffers(1, &rtex.handle); + free_texture(rtex.color); + if (is_texture_init(rtex.depth)) + free_texture(rtex.depth); +} diff --git a/micro/tex.h b/micro/tex.h new file mode 100644 index 0000000..3e58817 --- /dev/null +++ b/micro/tex.h @@ -0,0 +1,37 @@ +#ifndef __MICRO_TEX_H__ +#define __MICRO_TEX_H__ + +#include "common.h" + +struct renderer; + +struct texture { + u32 handle; + int width; + int height; +}; + +struct render_texture { + u32 handle; + struct texture color; + struct texture depth; + // struct texture stencil; TODO + + int width; + int height; +}; + +struct texture load_texture(const char *file_path); +struct texture load_texture_from_mem(u8 *data, int width, int height); +void free_texture(struct texture tex); + +bool is_texture_init(struct texture tex); + +struct render_texture create_render_texture( + int width, + int height, + bool add_depth +); +void free_render_texture(struct render_texture rtex); + +#endif // __MICRO_TEX_H__ |
