#include "2d.h" #include "state.h" #include "../debug/logger.h" static const u32 PIXELS_PER_SEGMENT = 3; // Internal use functions static void set_texture_in_shader(r_shader *shader, r_texture *texture, v4 color = {1,1,1,1}, u32 texture_index = 0); // Immediate functions void r_2d_immediate_segment(v2 a, v2 b, v4 a_color, v4 b_color, f32 thickness) { glDisable(GL_DEPTH_TEST); // Shader glUseProgram(r_render_state.shader_2d.id); glUniform1i(r_render_state.shader_2d.has_texture[0], 0); // Vertex buffer data static GLuint gl_VAO, gl_VBO; static bool init = false; if(!init) { init = true; glGenVertexArrays(1, &gl_VAO); glBindVertexArray(gl_VAO); glGenBuffers(1, &gl_VBO); glBindBuffer(GL_ARRAY_BUFFER, gl_VBO); } glBindVertexArray(gl_VAO); glBindBuffer(GL_ARRAY_BUFFER, gl_VBO); f32 data[12] = { a.x, a.y, b.x, b.y, a_color.r, a_color.g, a_color.b, a_color.a, b_color.r, b_color.g, b_color.b, b_color.a }; glBufferData(GL_ARRAY_BUFFER, 2*sizeof(v2)+2*sizeof(v4), data, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(v4), (void*)(2*sizeof(v2))); // Draw glLineWidth(thickness); glDrawArrays(GL_LINES, 0, 2); // Deinit // glDeleteBuffers(1, &gl_VBO); // glDeleteVertexArrays(1, &gl_VAO); glEnable(GL_DEPTH_TEST); } void r_2d_immediate_polygonal_chain(u64 count, v2 *vertices, v4 color, f32 thickness) { glDisable(GL_DEPTH_TEST); // Shader glUseProgram(r_render_state.shader_2d.id); glUniform1i(r_render_state.shader_2d.has_texture[0], 0); // Vertex buffer data static GLuint gl_VAO, gl_VBO; static bool init = false; if(!init) { init = true; glGenVertexArrays(1, &gl_VAO); glBindVertexArray(gl_VAO); glGenBuffers(1, &gl_VBO); glBindBuffer(GL_ARRAY_BUFFER, gl_VBO); } glBindVertexArray(gl_VAO); glBindBuffer(GL_ARRAY_BUFFER, gl_VBO); glBufferData(GL_ARRAY_BUFFER, count*sizeof(v2), vertices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); //glDisableVertexAttribArray(1); glVertexAttrib4f(1, color.r, color.g, color.b, color.a); // Draw glLineWidth(thickness); glDrawArrays(GL_LINE_STRIP, 0, count); // Deinit // glDeleteBuffers(1, &gl_VBO); // glDeleteVertexArrays(1, &gl_VAO); glEnable(GL_DEPTH_TEST); } void r_2d_immediate_triangle(v2 a, v2 b, v2 c, v4 a_color, v4 b_color, v4 c_color, v2 a_uv, v2 b_uv, v2 c_uv, r_texture *texture) { glDisable(GL_DEPTH_TEST); // Shader glUseProgram(r_render_state.shader_2d.id); // Texture set_texture_in_shader(&r_render_state.shader_2d, texture); // Vertex buffer data static GLuint gl_VAO, gl_VBO; static bool init = false; if(!init) { init = true; glGenVertexArrays(1, &gl_VAO); glBindVertexArray(gl_VAO); glGenBuffers(1, &gl_VBO); glBindBuffer(GL_ARRAY_BUFFER, gl_VBO); } glBindVertexArray(gl_VAO); glBindBuffer(GL_ARRAY_BUFFER, gl_VBO); f32 data[24] = { a.x, a.y, b.x, b.y, c.x, c.y, a_color.r, a_color.g, a_color.b, a_color.a, b_color.r, b_color.g, b_color.b, b_color.a, c_color.r, c_color.g, c_color.b, c_color.a, a_uv.x, a_uv.y, b_uv.x, b_uv.y, c_uv.x, c_uv.y }; glBufferData(GL_ARRAY_BUFFER, 3*sizeof(v2)+3*sizeof(v4)+3*sizeof(v2), data, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(v4), (void*)(3*sizeof(v2))); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)(3*sizeof(v2)+3*sizeof(v4))); // Draw glDrawArrays(GL_TRIANGLES, 0, 3); // Deinit // glDeleteBuffers(1, &gl_VBO); // glDeleteVertexArrays(1, &gl_VAO); glEnable(GL_DEPTH_TEST); } void r_2d_immediate_quad(v2 a, v2 b, v2 c, v2 d, v4 color, v2 a_uv, v2 b_uv, v2 c_uv, v2 d_uv, r_texture *texture) { glDisable(GL_DEPTH_TEST); // Shader glUseProgram(r_render_state.shader_2d.id); // Texture set_texture_in_shader(&r_render_state.shader_2d, texture); // Vertex buffer data static GLuint gl_VAO, gl_VBO; static bool init = false; if(!init) { init = true; glGenVertexArrays(1, &gl_VAO); glBindVertexArray(gl_VAO); glGenBuffers(1, &gl_VBO); glBindBuffer(GL_ARRAY_BUFFER, gl_VBO); } glBindVertexArray(gl_VAO); glBindBuffer(GL_ARRAY_BUFFER, gl_VBO); f32 data[16] = { a.x, a.y, b.x, b.y, d.x, d.y, c.x, c.y, a_uv.x, a_uv.y, b_uv.x, b_uv.y, d_uv.x, d_uv.y, c_uv.x, c_uv.y }; glBufferData(GL_ARRAY_BUFFER, 4*sizeof(v2)+4*sizeof(v2), data, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); //glDisableVertexAttribArray(1); glVertexAttrib4f(1, color.r, color.g, color.b, color.a); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)(4*sizeof(v2))); // Draw glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // Deinit // glDeleteBuffers(1, &gl_VBO); // glDeleteVertexArrays(1, &gl_VAO); glEnable(GL_DEPTH_TEST); } void r_2d_immediate_rectangle(Rect r, v4 color, Rect uv, r_texture *texture) { v2 a = r.position + v2{r.w, 0 }; v2 b = r.position + v2{0 , 0 }; v2 c = r.position + v2{0 , r.h}; v2 d = r.position + v2{r.w, r.h}; v2 a_uv = uv.position + v2{uv.w, 0 }; v2 b_uv = uv.position + v2{0 , 0 }; v2 c_uv = uv.position + v2{0 , uv.h}; v2 d_uv = uv.position + v2{uv.w, uv.h}; r_2d_immediate_quad(a, b, c, d, color, a_uv, b_uv, c_uv, d_uv, texture); } void r_2d_immediate_rounded_rectangle(Rect r, f32 radius, v4 color) { radius = clamp(0, minimum(abs(r.w), abs(r.h)) / 2, radius); // Should compute PIXELS_PER_SEGMENT from the size of the radius u32 num_of_segments = floor(0.5 + radius * TAU * 0.25 / PIXELS_PER_SEGMENT); // Split into 9 sections: // - 5 quads (center, top, bottom, left, right) // - 4 semicircles (corners) // Inner vertices (CCW starting from 1st quadrant/upper-right) v2 i0, i1, i2, i3; i0.x = r.x + r.w - radius; i0.y = r.y + radius; i1.x = r.x + radius; i1.y = r.y + radius; i2.x = r.x + radius; i2.y = r.y + r.h - radius; i3.x = r.x + r.w - radius; i3.y = r.y + r.h - radius; // Outer vertices v2 o0, o1, o2, o3, o4, o5, o6, o7; o0.x = i0.x; o0.y = i0.y - radius; o1.x = i1.x; o1.y = i1.y - radius; o2.x = i1.x - radius; o2.y = i1.y; o3.x = i2.x - radius; o3.y = i2.y; o4.x = i2.x; o4.y = i2.y + radius; o5.x = i3.x; o5.y = i3.y + radius; o6.x = i3.x + radius; o6.y = i3.y; o7.x = i0.x + radius; o7.y = i0.y; // Reserve space for vertices u32 vertices_count = 30; // 5 quads, specified by 2 triangles each = 5 quads * 2 triangles * 3 vertices = 30 vertices vertices_count += num_of_segments * 12; // Add corner semicircles = 4 corners * N triangles * 3 vertices = N * 12 // Build 5 quads v2 vertices[vertices_count] = { i0, i1, i2, i0, i2, i3, // Center quad: i0, i1, i2, i3 o0, o1, i1, o0, i1, i0, // Top quad: o0, o1, i1, i0 i1, o2, o3, i1, o3, i2, // Left quad: i1, o2, o3, i2 i3, i2, o4, i3, o4, o5, // Bottom quad: i3, i2, o4, o5 o7, i0, i3, o7, i3, o6 // Right quad: o7, i0, i3, o6 }; u32 corner_offset = 30; // Corner semicircles v2 inner_vertices[4] = {i0, i1, i2, i3}; for(u32 quadrant = 0; quadrant < 4; quadrant++) { for(u32 i = quadrant*num_of_segments; i < (quadrant+1)*num_of_segments; i++) { f32 factor = TAU * .25 / num_of_segments; v2 inner = inner_vertices[quadrant]; v2 a = inner + radius * v2{cos( i * factor), -sin( i * factor)}; v2 b = inner + radius * v2{cos((i+1) * factor), -sin((i+1) * factor)}; vertices[corner_offset + 3*i + 0] = inner; vertices[corner_offset + 3*i + 1] = a; vertices[corner_offset + 3*i + 2] = b; } } r_2d_immediate_mesh(vertices_count, vertices, color, NULL, NULL); } void r_2d_immediate_mesh(u64 count, v2 *vertices, v4 color, v2 *uvs, r_texture *texture) { glDisable(GL_DEPTH_TEST); // Shader glUseProgram(r_render_state.shader_2d.id); // Texture set_texture_in_shader(&r_render_state.shader_2d, texture); // Vertex buffer data static GLuint gl_VAO, gl_vertices, gl_uvs; static bool init = false; if(!init) { init = true; glGenVertexArrays(1, &gl_VAO); glBindVertexArray(gl_VAO); glGenBuffers(1, &gl_vertices); glGenBuffers(1, &gl_uvs); } glBindVertexArray(gl_VAO); glBindBuffer(GL_ARRAY_BUFFER, gl_vertices); glBufferData(GL_ARRAY_BUFFER, count * sizeof(v2), vertices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); //glDisableVertexAttribArray(1); glVertexAttrib4f(1, color.r, color.g, color.b, color.a); glBindBuffer(GL_ARRAY_BUFFER, gl_uvs); glBufferData(GL_ARRAY_BUFFER, count * sizeof(v2), uvs , GL_STATIC_DRAW); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(v2), (void*)0); // Draw glDrawArrays(GL_TRIANGLES, 0, count); // Deinitvoid r_2d_draw_mesh(r_2d_mesh *mesh, r_texture *texture); // glDeleteBuffers(1, &gl_vertices); // glDeleteBuffers(1, &gl_uvs); // glDeleteVertexArrays(1, &gl_VAO); glEnable(GL_DEPTH_TEST); } void r_2d_immediate_rectangle_outline(Rect r, v4 color, f32 thickness) { v2 vertices[5]; vertices[0] = r.position + v2{ 0, 0}; vertices[1] = r.position + v2{ 0, r.h}; vertices[2] = r.position + v2{r.w, r.h}; vertices[3] = r.position + v2{r.w, 0}; vertices[4] = r.position + v2{ 0, 0}; r_2d_immediate_polygonal_chain(5, vertices, color, thickness); } void r_2d_immediate_rounded_rectangle_outline(Rect r, f32 radius, v4 color, f32 thickness) { radius = clamp(0, minimum(abs(r.w), abs(r.h)) / 2, radius); // Should compute PIXELS_PER_SEGMENT from the size of the radius u32 num_of_segments = floor(0.5 + radius * TAU * 0.25 / PIXELS_PER_SEGMENT); // Split into 9 sections: // - 5 quads (center, top, bottom, left, right) // - 4 semicircles (corners) // Inner vertices (CCW starting from 1st quadrant/upper-right) v2 i0, i1, i2, i3; i0.x = r.x + r.w - radius; i0.y = r.y + radius; i1.x = r.x + radius; i1.y = r.y + radius; i2.x = r.x + radius; i2.y = r.y + r.h - radius; i3.x = r.x + r.w - radius; i3.y = r.y + r.h - radius; // Reserve space for vertices u32 vertices_count = 1 + 4 + 4*num_of_segments; // Starting vertex (1) + one for each side (4) + one for each segment (4*num_of_segments) v2 inner_vertices[4] = {i0, i1, i2, i3}; v2 vertices[vertices_count] = { i3 + v2{radius, 0} // Starting vertex }; u32 v_index = 1; // Corner semicircles for(u32 quadrant = 0; quadrant < 4; quadrant++) { v2 inner = inner_vertices[quadrant]; v2 offset = {0, 0}; if(num_of_segments > 0) { f32 factor = TAU * .25 / num_of_segments; for(u32 i = quadrant*num_of_segments; i < (quadrant+1)*num_of_segments; i++) { v2 a = inner + radius * v2{cos( i * factor), -sin( i * factor)}; vertices[v_index] = a; v_index++; } offset = radius * v2{cos( (quadrant+1)*num_of_segments * factor), -sin( (quadrant+1)*num_of_segments * factor)}; } vertices[v_index] = inner + offset; v_index++; } r_2d_immediate_polygonal_chain(vertices_count, vertices, color, thickness); } void r_2d_draw_mesh(r_2d_mesh *mesh, r_texture *texture) { glDisable(GL_DEPTH_TEST); // Shader glUseProgram(r_render_state.shader_2d.id); // Texture set_texture_in_shader(&r_render_state.shader_2d, texture); // Draw glBindVertexArray(mesh->gl_VAO); glDrawArrays(GL_TRIANGLES, 0, mesh->count); glEnable(GL_DEPTH_TEST); } // Internal use functions static void set_texture_in_shader(r_shader *shader, r_texture *texture, v4 color, u32 texture_index) { // Remember to call glUseProgram before using this if(texture) { glUniform1i(shader->has_texture[texture_index], 1); glUniform1i(shader->texture[texture_index], 0); glUniform1i(shader->texture_channels[texture_index], r_texture_channels(texture)); glActiveTexture(GL_TEXTURE0 + texture_index); glBindTexture(GL_TEXTURE_2D, texture->gl_id); glUniform4f(shader->color[texture_index], color.r, color.g, color.b, color.a); } else { glUniform1i(shader->has_texture[texture_index], 0); } }