383 lines
11 KiB
C++
383 lines
11 KiB
C++
|
|
#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
|
||
|
|
GLuint gl_VAO, gl_VBO;
|
||
|
|
glGenVertexArrays(1, &gl_VAO);
|
||
|
|
glBindVertexArray(gl_VAO);
|
||
|
|
glGenBuffers(1, &gl_VBO);
|
||
|
|
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
|
||
|
|
GLuint gl_VAO, gl_VBO;
|
||
|
|
glGenVertexArrays(1, &gl_VAO);
|
||
|
|
glBindVertexArray(gl_VAO);
|
||
|
|
glGenBuffers(1, &gl_VBO);
|
||
|
|
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
|
||
|
|
GLuint gl_VAO, gl_VBO;
|
||
|
|
glGenVertexArrays(1, &gl_VAO);
|
||
|
|
glBindVertexArray(gl_VAO);
|
||
|
|
glGenBuffers(1, &gl_VBO);
|
||
|
|
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
|
||
|
|
GLuint gl_VAO, gl_VBO;
|
||
|
|
glGenVertexArrays(1, &gl_VAO);
|
||
|
|
glBindVertexArray(gl_VAO);
|
||
|
|
glGenBuffers(1, &gl_VBO);
|
||
|
|
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
|
||
|
|
f32 factor = TAU * .25 / num_of_segments;
|
||
|
|
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++)
|
||
|
|
{
|
||
|
|
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
|
||
|
|
GLuint gl_VAO, gl_vertices, gl_uvs;
|
||
|
|
glGenVertexArrays(1, &gl_VAO);
|
||
|
|
glBindVertexArray(gl_VAO);
|
||
|
|
glGenBuffers(1, &gl_vertices);
|
||
|
|
glGenBuffers(1, &gl_uvs);
|
||
|
|
|
||
|
|
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
|
||
|
|
f32 factor = TAU * .25 / num_of_segments;
|
||
|
|
for(u32 quadrant = 0; quadrant < 4; quadrant++)
|
||
|
|
{
|
||
|
|
v2 inner = inner_vertices[quadrant];
|
||
|
|
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++;
|
||
|
|
}
|
||
|
|
vertices[v_index] = inner + radius * v2{cos( (quadrant+1)*num_of_segments * factor), -sin( (quadrant+1)*num_of_segments * factor)};
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
}
|