Sat, 14 Jun 2025 14:02:16 +0200
adds first basic rectangle shader
partially solves issue #384
shader/rectangle_frag.glsl | file | annotate | diff | comparison | revisions | |
src/2d.c | file | annotate | diff | comparison | revisions | |
src/Makefile | file | annotate | diff | comparison | revisions | |
src/ascension/2d.h | file | annotate | diff | comparison | revisions | |
src/ascension/constants.h | file | annotate | diff | comparison | revisions | |
src/ascension/core.h | file | annotate | diff | comparison | revisions | |
src/sprite.c | file | annotate | diff | comparison | revisions | |
test/snake/Makefile | file | annotate | diff | comparison | revisions | |
test/snake/snake.c | file | annotate | diff | comparison | revisions |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/shader/rectangle_frag.glsl Sat Jun 14 14:02:16 2025 +0200 @@ -0,0 +1,20 @@ +layout(location = 0) out vec4 diffuse; +in vec2 uvcoord; + +uniform vec4 color; +uniform vec2 size; +#ifndef FILL +uniform float thickness; +#endif + +void main(void) { +#ifdef FILL + diffuse = color; +#else + if (uvcoord.x < thickness || uvcoord.x >= size.x - thickness || uvcoord.y < thickness || uvcoord.y >= size.y-thickness) { + diffuse = color; + } else { + discard; + } +#endif +}
--- 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; +}
--- a/src/Makefile Sat Jun 14 12:38:37 2025 +0200 +++ b/src/Makefile Sat Jun 14 14:02:16 2025 +0200 @@ -44,13 +44,20 @@ FORCE: -$(BUILD_DIR)/2d.o: 2d.c ascension/2d.h +$(BUILD_DIR)/2d.o: 2d.c ascension/2d.h ascension/scene_node.h \ + ascension/datatypes.h ascension/transform.h ascension/mesh.h \ + ascension/constants.h ascension/context.h ascension/window.h \ + ascension/glcontext.h ascension/scene.h ascension/camera.h \ + ascension/input.h ascension/ui/font.h ascension/error.h \ + ascension/shader.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/behavior.o: behavior.c ascension/behavior.h \ ascension/scene_node.h ascension/datatypes.h ascension/transform.h \ - ascension/error.h ascension/scene.h ascension/camera.h + ascension/context.h ascension/window.h ascension/glcontext.h \ + ascension/scene.h ascension/camera.h ascension/input.h \ + ascension/ui/font.h ascension/error.h ascension/scene.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -104,8 +111,7 @@ ascension/datatypes.h ascension/window.h ascension/glcontext.h \ ascension/scene.h ascension/scene_node.h ascension/transform.h \ ascension/camera.h ascension/input.h ascension/ui/font.h \ - ascension/scene.h ascension/behavior.h ascension/shader.h \ - ascension/sprite.h ascension/mesh.h ascension/texture.h + ascension/scene.h ascension/behavior.h ascension/shader.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $<
--- a/src/ascension/2d.h Sat Jun 14 12:38:37 2025 +0200 +++ b/src/ascension/2d.h Sat Jun 14 14:02:16 2025 +0200 @@ -28,6 +28,32 @@ #ifndef ASCENSION_2D_H #define ASCENSION_2D_H +#include "scene_node.h" +#include "mesh.h" + +#define ASC_RECTANGLE_FILLED 1 + +typedef struct asc_rectangle_s { + AscSceneNode node; + AscMesh mesh; + asc_col4f color; + unsigned int width; + unsigned int height; + int flags; +} AscRectangle; + +struct asc_rectangle_create_args { + asc_recti bounds; + int x; + int y; + unsigned int width; + unsigned int height; + bool filled; +}; + +AscSceneNode *asc_rectangle_create(struct asc_rectangle_create_args args); + +#define asc_rectangle(...) asc_rectangle_create((struct asc_rectangle_create_args) { __VA_ARGS__ }) #endif /* ASCENSION_2D_H */
--- a/src/ascension/constants.h Sat Jun 14 12:38:37 2025 +0200 +++ b/src/ascension/constants.h Sat Jun 14 14:02:16 2025 +0200 @@ -32,11 +32,12 @@ // Internally used shader IDs. // -------------------------------------- -#define ASC_SHADER_INTERNAL_ID(id) (1000000000u+id) +#define ASC_SHADER_INTERNAL_ID(id) (1000000000u+id) -#define ASC_SHADER_SPRITE_RECT ASC_SHADER_INTERNAL_ID(1) -#define ASC_SHADER_SPRITE_UV ASC_SHADER_INTERNAL_ID(2) - +#define ASC_SHADER_SPRITE_RECT ASC_SHADER_INTERNAL_ID(1) +#define ASC_SHADER_SPRITE_UV ASC_SHADER_INTERNAL_ID(2) +#define ASC_SHADER_RECTANGLE_DRAW ASC_SHADER_INTERNAL_ID(3) +#define ASC_SHADER_RECTANGLE_FILL ASC_SHADER_INTERNAL_ID(4) #endif // ASC_CONSTANTS_H
--- a/src/ascension/core.h Sat Jun 14 12:38:37 2025 +0200 +++ b/src/ascension/core.h Sat Jun 14 14:02:16 2025 +0200 @@ -33,5 +33,8 @@ #include "scene.h" #include "behavior.h" +// forward declare some functions s.t. the whole header does not need to be included +void asc_shader_clear_registry(void); + #endif /* ASCENSION_CORE_H */
--- a/src/sprite.c Sat Jun 14 12:38:37 2025 +0200 +++ b/src/sprite.c Sat Jun 14 14:02:16 2025 +0200 @@ -39,7 +39,7 @@ typedef struct asc_sprite_shader_s { AscShaderProgram program; - int tex; + GLint tex; } AscSpriteShader; static void *asc_sprite_shader_create(bool rect) {
--- a/test/snake/Makefile Sat Jun 14 12:38:37 2025 +0200 +++ b/test/snake/Makefile Sat Jun 14 14:02:16 2025 +0200 @@ -56,7 +56,8 @@ ../../src/ascension/ui/../scene_node.h ../../src/ascension/ui/../mesh.h \ ../../src/ascension/ui/../datatypes.h \ ../../src/ascension/ui/../texture.h ../../src/ascension/ui/../camera.h \ - ../../src/ascension/sprite.h ../../src/ascension/shader.h + ../../src/ascension/sprite.h ../../src/ascension/2d.h \ + ../../src/ascension/mesh.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $<
--- a/test/snake/snake.c Sat Jun 14 12:38:37 2025 +0200 +++ b/test/snake/snake.c Sat Jun 14 14:02:16 2025 +0200 @@ -28,7 +28,7 @@ #include <ascension/core.h> #include <ascension/ui.h> #include <ascension/sprite.h> -#include <ascension/shader.h> +#include <ascension/2d.h> #include <cx/printf.h> @@ -195,6 +195,12 @@ // create spaceship create_spaceship(); + // TODO: play around with the test rectangle + asc_ink_rgb(255, 0, 0); + asc_scene_add_node(MAIN_SCENE, + asc_rectangle(.x = 200, .y = 250, .width = 100, .height = 75) + ); + // Main Loop do { // quit application on any error