Return to project, refactoring

This commit is contained in:
Krzosa Karol
2024-11-30 11:08:08 +01:00
parent 35cc5ae48d
commit f1e674ca37
15 changed files with 144 additions and 188 deletions

391
src/render/opengl.cpp Normal file
View File

@@ -0,0 +1,391 @@
struct Shader {
uint32_t pipeline;
uint32_t fshader;
uint32_t vshader;
};
Shader CreateShader(char *glsl_vshader, char *glsl_fshader);
struct Vertex2D {
Vec2 pos;
Vec2 tex;
Color color;
};
struct VertexNode2D {
VertexNode2D *next;
int count;
Vertex2D vertices[1024 * 16];
Rect2 scissor;
};
struct VertexList2D {
VertexNode2D *first;
VertexNode2D *last;
};
VertexList2D Vertices;
int64_t TotalVertexCount;
unsigned VBO, VAO;
Shader Shader2D;
Arena RenderArena;
Rect2 CurrentScissor;
Font MainFont;
Int FontLineSpacing;
Int FontCharSpacing;
void BeginFrameRender(float wx, float wy) {
Clear(&RenderArena);
TotalVertexCount = 0;
Vertices.first = NULL;
Vertices.last = NULL;
CurrentScissor = Rect0Size(wx, wy);
}
void EndFrameRender(float wx, float wy, Color color) {
glEnable(GL_BLEND);
glEnable(GL_SCISSOR_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glViewport(0, 0, (GLsizei)wx, (GLsizei)wy);
glScissor(0, 0, (GLsizei)wx, (GLsizei)wy);
glClearColor(color.r, color.g, color.b, color.a);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// Default draw using the font texture
glBindProgramPipeline(Shader2D.pipeline);
float xinverse = 1.f / (wx / 2.f);
float yinverse = 1.f / (wy / 2.f);
glProgramUniform2f(Shader2D.vshader, 0, xinverse, yinverse);
for (VertexNode2D *it = Vertices.first; it; it = it->next) {
Rect2 rect = it->scissor;
GLint x = (GLint)rect.min.x;
GLint y = (GLint)rect.min.y;
GLsizei w = (GLsizei)(rect.max.x - x);
GLsizei h = (GLsizei)(rect.max.y - y);
glScissor(x, (GLint)wy - (GLint)rect.max.y, w, h);
glNamedBufferSubData(VBO, 0, it->count * sizeof(Vertex2D), it->vertices);
glBindVertexArray(VAO);
GLint s_texture = 0; // texture unit that sampler2D will use in GLSL code
glBindTextureUnit(s_texture, MainFont.texture_id);
glDrawArrays(GL_TRIANGLES, 0, it->count);
}
}
void GLDebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *user) {
printf("%s", message);
if (severity == GL_DEBUG_SEVERITY_HIGH || severity == GL_DEBUG_SEVERITY_MEDIUM) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "OpenGL error", message, NULL);
}
}
void InitRender() {
InitArena(&RenderArena);
glDebugMessageCallback(&GLDebugCallback, NULL);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glCreateBuffers(1, &VBO);
glNamedBufferStorage(VBO, Lengthof(VertexNode2D::vertices) * sizeof(Vertex2D), 0, GL_DYNAMIC_STORAGE_BIT);
glCreateVertexArrays(1, &VAO);
GLint vbuf_index = 0;
glVertexArrayVertexBuffer(VAO, vbuf_index, VBO, 0, sizeof(struct Vertex2D));
GLint a_pos = 0;
glVertexArrayAttribFormat(VAO, a_pos, 2, GL_FLOAT, GL_FALSE, offsetof(struct Vertex2D, pos));
glVertexArrayAttribBinding(VAO, a_pos, vbuf_index);
glEnableVertexArrayAttrib(VAO, a_pos);
GLint a_tex = 1;
glVertexArrayAttribFormat(VAO, a_tex, 2, GL_FLOAT, GL_FALSE, offsetof(struct Vertex2D, tex));
glVertexArrayAttribBinding(VAO, a_tex, vbuf_index);
glEnableVertexArrayAttrib(VAO, a_tex);
GLint a_color = 2;
glVertexArrayAttribFormat(VAO, a_color, 4, GL_UNSIGNED_BYTE, GL_FALSE, offsetof(struct Vertex2D, color));
glVertexArrayAttribBinding(VAO, a_color, vbuf_index);
glEnableVertexArrayAttrib(VAO, a_color);
char *glsl_vshader = R"==(
#version 450 core
layout(location=0) uniform vec2 U_InvHalfScreenSize;
layout(location=0) in vec2 VPos;
layout(location=1) in vec2 VTex;
layout(location=2) in vec4 VColor;
out gl_PerVertex { vec4 gl_Position; }; // required because of ARB_separate_shader_objects
out vec2 FUV;
out vec4 FColor;
void main() {
vec2 pos = VPos * U_InvHalfScreenSize;
pos.y = 2 - pos.y; // invert y axis
pos -= vec2(1, 1); // convert to 0,1 range
gl_Position = vec4(pos, 0, 1);
FUV = VTex;
FColor = VColor / 255.0;
}
)==";
char *glsl_fshader = R"==(
#version 450 core
in vec2 FUV;
in vec4 FColor;
layout (binding=0) uniform sampler2D S_Texture;
layout (location=0) out vec4 OutColor;
void main() {
vec4 c = FColor;
c.a *= texture(S_Texture, FUV).r;
OutColor = c;
}
)==";
Shader2D = CreateShader(glsl_vshader, glsl_fshader);
}
Shader CreateShader(char *glsl_vshader, char *glsl_fshader) {
Shader result = {};
result.vshader = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &glsl_vshader);
result.fshader = glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &glsl_fshader);
GLint linked;
glGetProgramiv(result.vshader, GL_LINK_STATUS, &linked);
if (!linked) {
char message[1024];
glGetProgramInfoLog(result.vshader, sizeof(message), NULL, message);
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Failed to create vertex shader!", message, NULL);
}
glGetProgramiv(result.fshader, GL_LINK_STATUS, &linked);
if (!linked) {
char message[1024];
glGetProgramInfoLog(result.fshader, sizeof(message), NULL, message);
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Failed to create fragment shader!", message, NULL);
}
glGenProgramPipelines(1, &result.pipeline);
glUseProgramStages(result.pipeline, GL_VERTEX_SHADER_BIT, result.vshader);
glUseProgramStages(result.pipeline, GL_FRAGMENT_SHADER_BIT, result.fshader);
return result;
}
#define SLL_QUEUE_ADD_MOD(f, l, n, next) \
do { \
(n)->next = 0; \
if ((f) == 0) { \
(f) = (l) = (n); \
} else { \
(l) = (l)->next = (n); \
} \
} while (0)
#define SLL_QUEUE_ADD(f, l, n) SLL_QUEUE_ADD_MOD(f, l, n, next)
VertexNode2D *AllocVertexNode2D(Allocator allocator, VertexList2D *list) {
VertexNode2D *node = AllocType(allocator, VertexNode2D);
SLL_QUEUE_ADD(list->first, list->last, node);
return node;
}
void SetScissor(Rect2 rect) {
CurrentScissor = rect;
VertexNode2D *node = Vertices.last;
if (!node) {
node = AllocVertexNode2D(RenderArena, &Vertices);
node->scissor = rect;
return;
}
if (node->scissor != rect && node->count == 0) {
node->scissor = rect;
return;
}
if (node->scissor != rect) {
node = AllocVertexNode2D(RenderArena, &Vertices);
node->scissor = rect;
return;
}
}
void SetScissor(Rect2I rect) { SetScissor(ToRect2(rect)); }
Vertex2D *AllocVertex2D(Allocator allocator, VertexList2D *list, int count) {
VertexNode2D *node = list->last;
if (node == 0 || node->count + count > Lengthof(node->vertices)) {
node = AllocVertexNode2D(allocator, list);
node->scissor = CurrentScissor;
}
TotalVertexCount += count;
Vertex2D *result = node->vertices + node->count;
node->count += count;
return result;
}
void PushVertex2D(Allocator allocator, VertexList2D *list, Vertex2D *vertices, int count) {
Vertex2D *result = AllocVertex2D(allocator, list, count);
for (int i = 0; i < count; i += 1) result[i] = vertices[i];
}
void PushQuad2D(Allocator arena, VertexList2D *list, Rect2 rect, Rect2 tex, Color color, float rotation = 0.f, Vec2 rotation_point = {}) {
Vertex2D *v = AllocVertex2D(arena, list, 6);
v[0] = {
{rect.min.x, rect.max.y},
{ tex.min.x, tex.max.y},
color
};
v[1] = {
{rect.max.x, rect.max.y},
{ tex.max.x, tex.max.y},
color
};
v[2] = {
{rect.min.x, rect.min.y},
{ tex.min.x, tex.min.y},
color
};
v[3] = {
{rect.min.x, rect.min.y},
{ tex.min.x, tex.min.y},
color
};
v[4] = {
{rect.max.x, rect.max.y},
{ tex.max.x, tex.max.y},
color
};
v[5] = {
{rect.max.x, rect.min.y},
{ tex.max.x, tex.min.y},
color
};
if (rotation != 0.f) {
float s = sinf(rotation);
float c = cosf(rotation);
for (int i = 0; i < 6; i += 1) {
v[i].pos -= rotation_point;
v[i].pos = {v[i].pos.x * c + v[i].pos.y * (-s), v[i].pos.x * s + v[i].pos.y * c};
v[i].pos += rotation_point;
}
}
}
void DrawRect(Rect2 rect, Color color) {
PushQuad2D(RenderArena, &Vertices, rect, MainFont.white_texture_bounding_box, color);
}
void DrawRect(Rect2I rect, Color color) {
PushQuad2D(RenderArena, &Vertices, ToRect2(rect), MainFont.white_texture_bounding_box, color);
}
void DrawRectOutline(Rect2 rect, Color color, float thickness = 2) {
Rect2 a = CutLeft(&rect, thickness);
Rect2 b = CutRight(&rect, thickness);
Rect2 c = CutTop(&rect, thickness);
Rect2 d = CutBottom(&rect, thickness);
PushQuad2D(RenderArena, &Vertices, a, MainFont.white_texture_bounding_box, color);
PushQuad2D(RenderArena, &Vertices, b, MainFont.white_texture_bounding_box, color);
PushQuad2D(RenderArena, &Vertices, c, MainFont.white_texture_bounding_box, color);
PushQuad2D(RenderArena, &Vertices, d, MainFont.white_texture_bounding_box, color);
}
void DrawRectOutline(Rect2I rect, Color color, Int thickness = 2) {
DrawRectOutline(ToRect2(rect), color, (float)thickness);
}
Int GetCharSpacing(Font *font, int codepoint = '_') {
Glyph *g = GetGlyph(font, codepoint);
if (g->xadvance) return (Int)g->xadvance;
return (Int)g->size.x;
}
Int GetLineSpacing(Font *font) {
Int result = (Int)(font->ascent - font->descent + font->line_gap);
return result;
}
Vec2 DrawString(Font *font, String16 string, Vec2 pos, Color color, bool draw = true) {
pos.y += GetLineSpacing(font) + font->descent;
Vec2 original_pos = pos;
For(string) {
Glyph *g = GetGlyph(font, it);
Rect2 rect = Rect2FromSize(pos + g->offset, g->size);
if (draw && it != '\n' && it != ' ' && it != '\t') {
PushQuad2D(RenderArena, &Vertices, rect, g->atlas_bounding_box, color);
}
pos.x += g->xadvance;
}
Vec2 result = {pos.x - original_pos.x, font->size};
return result;
}
#define PI32 3.14159265359f
void DrawCircle(Vec2 pos, float radius, Color color) {
const int segment_count = 16;
const int vertex_count = segment_count * 3;
Vec2 points[segment_count + 1];
for (int i = 0; i < Lengthof(points); i += 1) {
float radians = 2.0f * PI32 * float(i) / float(segment_count);
float x = radius * cosf(radians);
float y = radius * sinf(radians);
points[i] = {x, y};
}
points[segment_count] = points[0]; // wrap around
Vertex2D *vertices = AllocVertex2D(RenderArena, &Vertices, vertex_count);
int point_i = 0;
int segment_i = 0;
for (; segment_i < vertex_count; segment_i += 3, point_i += 1) {
Rect2 tex = MainFont.white_texture_bounding_box;
Vertex2D *it = vertices + segment_i;
it[0].color = color;
it[1].color = color;
it[2].color = color;
it[0].tex = {tex.min.x, tex.max.y};
it[1].tex = {tex.max.x, tex.max.y};
it[2].tex = {tex.min.x, tex.min.y};
it[0].pos = {pos + points[point_i]};
it[1].pos = {pos + points[point_i + 1]};
it[2].pos = {pos};
}
}
void ReloadFont() {
Int size = StyleFontSize;
size = ClampBottom((Int)2, size);
if (MainFont.texture_id) {
glDeleteTextures(1, &MainFont.texture_id);
Dealloc(&MainFont.glyphs);
MainFont = {};
}
Scratch scratch;
Atlas atlas = CreateAtlas(scratch, {2048, 2048});
MainFont = CreateFont(&atlas, (uint32_t)size, StyleFont);
{
GLint filter = GL_NEAREST;
if (StyleFontFilter == 1) filter = GL_LINEAR;
glCreateTextures(GL_TEXTURE_2D, 1, &atlas.texture_id);
glTextureParameteri(atlas.texture_id, GL_TEXTURE_MIN_FILTER, filter);
glTextureParameteri(atlas.texture_id, GL_TEXTURE_MAG_FILTER, filter);
glTextureParameteri(atlas.texture_id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTextureParameteri(atlas.texture_id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTextureStorage2D(atlas.texture_id, 1, GL_R8, (GLsizei)atlas.size.x, (GLsizei)atlas.size.y);
glTextureSubImage2D(atlas.texture_id, 0, 0, 0, (GLsizei)atlas.size.x, (GLsizei)atlas.size.y, GL_RED, GL_UNSIGNED_BYTE, atlas.bitmap);
}
MainFont.texture_id = atlas.texture_id;
FontCharSpacing = GetCharSpacing(&MainFont);
FontLineSpacing = GetLineSpacing(&MainFont);
}