diff options
Diffstat (limited to 'micro/renderer.c')
| -rw-r--r-- | micro/renderer.c | 360 |
1 files changed, 360 insertions, 0 deletions
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(); +} |
