summaryrefslogtreecommitdiff
path: root/micro
diff options
context:
space:
mode:
Diffstat (limited to 'micro')
-rw-r--r--micro/context.c114
-rw-r--r--micro/context.h36
-rw-r--r--micro/mat4.c83
-rw-r--r--micro/mat4.h18
-rw-r--r--micro/micro.c38
-rw-r--r--micro/renderer.c360
-rw-r--r--micro/renderer.h77
-rw-r--r--micro/shader.frag12
-rw-r--r--micro/shader.vert18
-rw-r--r--micro/tex.c159
-rw-r--r--micro/tex.h37
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__