#include "renderer.h" #include #include #include #include #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(); }