--- a/src/2d.c Sat Jun 14 12:38:37 2025 +0200 +++ b/src/2d.c Sat Jun 14 14:02:16 2025 +0200 @@ -27,3 +27,139 @@ #include "ascension/2d.h" +#include "ascension/constants.h" +#include "ascension/context.h" +#include "ascension/error.h" +#include "ascension/shader.h" + +#include <GL/glew.h> + +typedef struct asc_rectangle_shader_s { + AscShaderProgram program; + GLint color; + GLint size; + GLint thickness; +} AscRectangleShader; + +static void *asc_rectangle_shader_create(bool fill) { + AscShaderCodes codes; + if (asc_shader_load_code_files((AscShaderCodeInfo){ + .files.vtx = "sprite_vtx.glsl", + .files.frag = "rectangle_frag.glsl", + .defines.frag = fill ? "#define FILL" : NULL, + }, &codes)) { + asc_error("Loading sprite shader failed."); + return NULL; + } + AscRectangleShader *shader = asc_shader_create(codes, sizeof(*shader)); + if (asc_has_error()) { + asc_shader_free_codes(codes); + return NULL; + } + shader->color = glGetUniformLocation(shader->program.gl_id, "color"); + shader->size = glGetUniformLocation(shader->program.gl_id, "size"); + if (fill) { + shader->thickness = -1; + } else { + shader->thickness = glGetUniformLocation(shader->program.gl_id, "thickness"); + } + asc_shader_free_codes(codes); + + asc_error_catch_all_gl(); + + return shader; +} + +static AscShaderProgram *asc_rectangle_shader_fill_create() { + return asc_rectangle_shader_create(true); +} + +static AscShaderProgram *asc_rectangle_shader_draw_create() { + return asc_rectangle_shader_create(false); +} + +static const AscRectangleShader *asc_rectangle_shader_draw(void) { + return asc_shader_lookup_or_create(ASC_SHADER_RECTANGLE_DRAW, asc_rectangle_shader_draw_create); +} + +static const AscRectangleShader *asc_rectangle_shader_fill(void) { + return asc_shader_lookup_or_create(ASC_SHADER_RECTANGLE_FILL, asc_rectangle_shader_fill_create); +} + +static void asc_rectangle_destroy(AscSceneNode *node) { + asc_ptr_cast(AscRectangle, rectangle, node); + asc_mesh_destroy(&rectangle->mesh); +} + +static void asc_rectangle_update(AscSceneNode *node) { + asc_ptr_cast(AscRectangle, rectangle, node); + asc_vec2f size = asc_vec2f_new(rectangle->width, rectangle->height); + asc_mesh_plane_2d(&rectangle->mesh, .size = size, .uv_scale = size); +} + +static void asc_rectangle_draw(const AscCamera *camera, const AscSceneNode *node) { + asc_ptr_cast(AscRectangle, rectangle, node); + bool filled = asc_test_flag(rectangle->flags, ASC_RECTANGLE_FILLED); + + // Activate shader + // TODO: scene should know which shader we are going to activate s.t. it can pre-sort nodes + const AscRectangleShader *shader = filled + ? asc_rectangle_shader_fill() + : asc_rectangle_shader_draw(); + asc_shader_use(&shader->program, camera); + + // Upload uniforms + // TODO: uploading model matrix could be a helper function + glUniformMatrix4fv(shader->program.model, 1, + GL_FALSE, node->world_transform); + + glUniform4f(shader->color, + rectangle->color.red, + rectangle->color.green, + rectangle->color.blue, + rectangle->color.alpha + ); + glUniform2f(shader->size, (float) rectangle->width, (float) rectangle->height); + + if (!filled) { + // TODO: implement thickness + glUniform1f(shader->thickness, 1); + } + + // Draw mesh + asc_mesh_draw_triangle_strip(&rectangle->mesh); +} + +AscSceneNode *asc_rectangle_create(struct asc_rectangle_create_args args) { + AscRectangle *rectangle = cxZallocDefault(sizeof(AscRectangle)); + + if (args.bounds.size.width + args.bounds.size.height > 0) { + rectangle->node.position.x = (float) args.bounds.pos.x; + rectangle->node.position.y = (float) args.bounds.pos.y; + rectangle->width = args.bounds.size.width; + rectangle->height = args.bounds.size.height; + } else { + rectangle->node.position.x = (float) args.x; + rectangle->node.position.y = (float) args.y; + rectangle->width = args.width; + rectangle->height = args.height; + } + + rectangle->color = asc_col_itof(asc_context.ink); + + if (args.filled) { + asc_set_flag(rectangle->flags, ASC_RECTANGLE_FILLED); + } + + AscSceneNode *node = &rectangle->node; + node->position.z = ASC_SCENE_2D_DEPTH_OFFSET; + node->scale = asc_vec3f_one; + node->render_group = asc_context.ink.alpha < 255 + ? ASC_RENDER_GROUP_2D_BLEND + : ASC_RENDER_GROUP_2D_OPAQUE; + node->update_func = asc_rectangle_update; + node->destroy_func = asc_rectangle_destroy; + node->draw_func = asc_rectangle_draw; + asc_node_update(node); + return node; +}