Sun, 01 Jun 2025 16:35:23 +0200
remove pre-defined dummy textures by introducing conditional compilation for shaders
and by the way resolves #645
shader/sprite_frag.glsl | file | annotate | diff | comparison | revisions | |
shader/sprite_vtx.glsl | file | annotate | diff | comparison | revisions | |
src/Makefile | file | annotate | diff | comparison | revisions | |
src/ascension/2d/sprite.h | file | annotate | diff | comparison | revisions | |
src/ascension/glcontext.h | file | annotate | diff | comparison | revisions | |
src/ascension/scene.h | file | annotate | diff | comparison | revisions | |
src/ascension/scene_node.h | file | annotate | diff | comparison | revisions | |
src/ascension/shader.h | file | annotate | diff | comparison | revisions | |
src/glcontext.c | file | annotate | diff | comparison | revisions | |
src/scene.c | file | annotate | diff | comparison | revisions | |
src/shader.c | file | annotate | diff | comparison | revisions | |
src/sprite.c | file | annotate | diff | comparison | revisions | |
src/text.c | file | annotate | diff | comparison | revisions | |
test/snake/Makefile | file | annotate | diff | comparison | revisions |
--- a/shader/sprite_frag.glsl Sun Jun 01 14:59:40 2025 +0200 +++ b/shader/sprite_frag.glsl Sun Jun 01 16:35:23 2025 +0200 @@ -1,11 +1,12 @@ -#version 400 core - layout(location = 0) out vec4 diffuse; in vec2 uvcoord; -uniform sampler2D uv_tex; -uniform sampler2DRect rect_tex; +#ifdef USE_RECT +uniform sampler2DRect tex; +#else +uniform sampler2D tex; +#endif void main(void) { - diffuse = texture(rect_tex, uvcoord) * texture(uv_tex, uvcoord); + diffuse = texture(tex, uvcoord); }
--- a/shader/sprite_vtx.glsl Sun Jun 01 14:59:40 2025 +0200 +++ b/shader/sprite_vtx.glsl Sun Jun 01 16:35:23 2025 +0200 @@ -1,5 +1,3 @@ -#version 400 core - layout(location = 0) in vec2 in_pos; layout(location = 1) in vec2 in_uv; out vec2 uvcoord;
--- a/src/Makefile Sun Jun 01 14:59:40 2025 +0200 +++ b/src/Makefile Sun Jun 01 16:35:23 2025 +0200 @@ -52,24 +52,33 @@ $(BUILD_DIR)/camera.o: camera.c ascension/error.h ascension/context.h \ ascension/datatypes.h ascension/window.h ascension/glcontext.h \ - ascension/shader.h ascension/texture.h ascension/scene.h \ - ascension/scene_node.h ascension/transform.h ascension/camera.h \ + ascension/2d/sprite.h ascension/2d/../scene_node.h \ + ascension/2d/../datatypes.h ascension/2d/../transform.h \ + ascension/2d/../mesh.h ascension/2d/../texture.h \ + ascension/2d/../shader.h ascension/2d/../camera.h ascension/texture.h \ + ascension/scene.h ascension/scene_node.h ascension/camera.h \ ascension/input.h ascension/ui/font.h ascension/camera.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/context.o: context.c ascension/context.h \ ascension/datatypes.h ascension/window.h ascension/glcontext.h \ - ascension/shader.h ascension/texture.h ascension/scene.h \ - ascension/scene_node.h ascension/transform.h ascension/camera.h \ + ascension/2d/sprite.h ascension/2d/../scene_node.h \ + ascension/2d/../datatypes.h ascension/2d/../transform.h \ + ascension/2d/../mesh.h ascension/2d/../texture.h \ + ascension/2d/../shader.h ascension/2d/../camera.h ascension/texture.h \ + ascension/scene.h ascension/scene_node.h ascension/camera.h \ ascension/input.h ascension/ui/font.h ascension/error.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/error.o: error.c ascension/context.h ascension/datatypes.h \ - ascension/window.h ascension/glcontext.h ascension/shader.h \ - ascension/texture.h ascension/scene.h ascension/scene_node.h \ - ascension/transform.h ascension/camera.h ascension/input.h \ + ascension/window.h ascension/glcontext.h ascension/2d/sprite.h \ + ascension/2d/../scene_node.h ascension/2d/../datatypes.h \ + ascension/2d/../transform.h ascension/2d/../mesh.h \ + ascension/2d/../texture.h ascension/2d/../shader.h \ + ascension/2d/../camera.h ascension/texture.h ascension/scene.h \ + ascension/scene_node.h ascension/camera.h ascension/input.h \ ascension/ui/font.h ascension/error.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -80,16 +89,22 @@ $(BUILD_DIR)/font.o: font.c ascension/error.h ascension/context.h \ ascension/datatypes.h ascension/window.h ascension/glcontext.h \ - ascension/shader.h ascension/texture.h ascension/scene.h \ - ascension/scene_node.h ascension/transform.h ascension/camera.h \ + ascension/2d/sprite.h ascension/2d/../scene_node.h \ + ascension/2d/../datatypes.h ascension/2d/../transform.h \ + ascension/2d/../mesh.h ascension/2d/../texture.h \ + ascension/2d/../shader.h ascension/2d/../camera.h ascension/texture.h \ + ascension/scene.h ascension/scene_node.h ascension/camera.h \ ascension/input.h ascension/ui/font.h ascension/filesystem.h \ ascension/ui/font.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/glcontext.o: glcontext.c ascension/glcontext.h \ - ascension/shader.h ascension/texture.h ascension/datatypes.h \ - ascension/error.h + ascension/2d/sprite.h ascension/2d/../scene_node.h \ + ascension/2d/../datatypes.h ascension/2d/../transform.h \ + ascension/2d/../mesh.h ascension/2d/../texture.h \ + ascension/2d/../shader.h ascension/2d/../camera.h ascension/texture.h \ + ascension/error.h ascension/2d/sprite.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< @@ -100,29 +115,34 @@ $(BUILD_DIR)/scene.o: scene.c ascension/error.h ascension/context.h \ ascension/datatypes.h ascension/window.h ascension/glcontext.h \ - ascension/shader.h ascension/texture.h ascension/scene.h \ - ascension/scene_node.h ascension/transform.h ascension/camera.h \ + ascension/2d/sprite.h ascension/2d/../scene_node.h \ + ascension/2d/../datatypes.h ascension/2d/../transform.h \ + ascension/2d/../mesh.h ascension/2d/../texture.h \ + ascension/2d/../shader.h ascension/2d/../camera.h ascension/texture.h \ + ascension/scene.h ascension/scene_node.h ascension/camera.h \ ascension/input.h ascension/ui/font.h ascension/scene.h \ - ascension/behavior.h ascension/shader.h ascension/2d.h \ - ascension/2d/sprite.h ascension/2d/../scene_node.h \ - ascension/2d/../mesh.h ascension/2d/../datatypes.h \ - ascension/2d/../texture.h + ascension/behavior.h ascension/shader.h ascension/2d.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/scene_node.o: scene_node.c ascension/scene_node.h \ ascension/datatypes.h ascension/transform.h ascension/context.h \ - ascension/window.h ascension/glcontext.h ascension/shader.h \ - ascension/texture.h ascension/scene.h ascension/scene_node.h \ - ascension/camera.h ascension/input.h ascension/ui/font.h \ - ascension/error.h + ascension/window.h ascension/glcontext.h ascension/2d/sprite.h \ + ascension/2d/../scene_node.h ascension/2d/../mesh.h \ + ascension/2d/../datatypes.h ascension/2d/../texture.h \ + ascension/2d/../shader.h ascension/2d/../camera.h ascension/texture.h \ + ascension/scene.h ascension/scene_node.h ascension/camera.h \ + ascension/input.h ascension/ui/font.h ascension/error.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/shader.o: shader.c ascension/context.h ascension/datatypes.h \ - ascension/window.h ascension/glcontext.h ascension/shader.h \ - ascension/texture.h ascension/scene.h ascension/scene_node.h \ - ascension/transform.h ascension/camera.h ascension/input.h \ + ascension/window.h ascension/glcontext.h ascension/2d/sprite.h \ + ascension/2d/../scene_node.h ascension/2d/../datatypes.h \ + ascension/2d/../transform.h ascension/2d/../mesh.h \ + ascension/2d/../texture.h ascension/2d/../shader.h \ + ascension/2d/../camera.h ascension/texture.h ascension/scene.h \ + ascension/scene_node.h ascension/camera.h ascension/input.h \ ascension/ui/font.h ascension/error.h ascension/shader.h \ ascension/filesystem.h @echo "Compiling $<" @@ -131,38 +151,46 @@ $(BUILD_DIR)/sprite.o: sprite.c ascension/2d/sprite.h \ ascension/2d/../scene_node.h ascension/2d/../datatypes.h \ ascension/2d/../transform.h ascension/2d/../mesh.h \ - ascension/2d/../texture.h ascension/context.h ascension/datatypes.h \ - ascension/window.h ascension/glcontext.h ascension/shader.h \ + ascension/2d/../texture.h ascension/2d/../shader.h \ + ascension/2d/../camera.h ascension/context.h ascension/datatypes.h \ + ascension/window.h ascension/glcontext.h ascension/2d/sprite.h \ ascension/texture.h ascension/scene.h ascension/scene_node.h \ ascension/camera.h ascension/input.h ascension/ui/font.h \ - ascension/glcontext.h ascension/mesh.h + ascension/glcontext.h ascension/error.h ascension/mesh.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/text.o: text.c ascension/error.h ascension/context.h \ ascension/datatypes.h ascension/window.h ascension/glcontext.h \ - ascension/shader.h ascension/texture.h ascension/scene.h \ - ascension/scene_node.h ascension/transform.h ascension/camera.h \ + ascension/2d/sprite.h ascension/2d/../scene_node.h \ + ascension/2d/../datatypes.h ascension/2d/../transform.h \ + ascension/2d/../mesh.h ascension/2d/../texture.h \ + ascension/2d/../shader.h ascension/2d/../camera.h ascension/texture.h \ + ascension/scene.h ascension/scene_node.h ascension/camera.h \ ascension/input.h ascension/ui/font.h ascension/ui/text.h \ - ascension/ui/font.h ascension/ui/../2d/sprite.h \ - ascension/ui/../2d/../scene_node.h ascension/ui/../2d/../mesh.h \ - ascension/ui/../2d/../datatypes.h ascension/ui/../2d/../texture.h + ascension/ui/font.h ascension/ui/../2d/sprite.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/texture.o: texture.c ascension/error.h ascension/context.h \ ascension/datatypes.h ascension/window.h ascension/glcontext.h \ - ascension/shader.h ascension/texture.h ascension/scene.h \ - ascension/scene_node.h ascension/transform.h ascension/camera.h \ + ascension/2d/sprite.h ascension/2d/../scene_node.h \ + ascension/2d/../datatypes.h ascension/2d/../transform.h \ + ascension/2d/../mesh.h ascension/2d/../texture.h \ + ascension/2d/../shader.h ascension/2d/../camera.h ascension/texture.h \ + ascension/scene.h ascension/scene_node.h ascension/camera.h \ ascension/input.h ascension/ui/font.h ascension/texture.h \ ascension/filesystem.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $< $(BUILD_DIR)/window.o: window.c ascension/error.h ascension/window.h \ - ascension/datatypes.h ascension/glcontext.h ascension/shader.h \ - ascension/texture.h ascension/scene.h ascension/scene_node.h \ - ascension/transform.h ascension/camera.h ascension/context.h \ + ascension/datatypes.h ascension/glcontext.h ascension/2d/sprite.h \ + ascension/2d/../scene_node.h ascension/2d/../datatypes.h \ + ascension/2d/../transform.h ascension/2d/../mesh.h \ + ascension/2d/../texture.h ascension/2d/../shader.h \ + ascension/2d/../camera.h ascension/texture.h ascension/scene.h \ + ascension/scene_node.h ascension/camera.h ascension/context.h \ ascension/window.h ascension/input.h ascension/ui/font.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $<
--- a/src/ascension/2d/sprite.h Sun Jun 01 14:59:40 2025 +0200 +++ b/src/ascension/2d/sprite.h Sun Jun 01 16:35:23 2025 +0200 @@ -31,6 +31,7 @@ #include "../scene_node.h" #include "../mesh.h" #include "../texture.h" +#include "../shader.h" typedef struct AscSprite { AscSceneNode data; @@ -68,8 +69,25 @@ #define asc_sprite(...) \ asc_sprite_create((struct asc_sprite_create_args) { __VA_ARGS__ }) -void asc_sprite_draw(AscSprite const *sprite); - void asc_sprite_set_size(AscSceneNode *node, unsigned width, unsigned height); + + + +typedef struct AscShaderSprite { + AscShaderProgram program; + int tex; +} AscShaderSprite; + +/** + * Loads and initializes the sprite shader. + * + * @param sprite the structure to initialize + * @param rect true if the version for rectangular textures shall be compiled + * @return zero on success, non-zero on failure + */ +int asc_shader_sprite_init(AscShaderSprite *sprite, bool rect); + +void asc_sprite_draw(const AscShaderSprite *shader, const AscSprite *node); + #endif //ASCENSION_SPRITE_H
--- a/src/ascension/glcontext.h Sun Jun 01 14:59:40 2025 +0200 +++ b/src/ascension/glcontext.h Sun Jun 01 16:35:23 2025 +0200 @@ -32,7 +32,7 @@ #include <cx/list.h> -#include "shader.h" +#include "2d/sprite.h" // TODO: this shouldn't be included here #include "texture.h" typedef struct AscGLContextSettings { @@ -56,18 +56,17 @@ SDL_Window *window; SDL_GLContext glctx; CxList *cleanup_funcs; - // TODO: find a better way to store the dummy textures - AscTexture textures_2d[ASC_TEXTURE_2D_COUNT]; - AscTexture textures_rect[ASC_TEXTURE_RECT_COUNT]; + // TODO: replace with something similar to the font cache struct { - AscShaderSprite sprite; + AscShaderSprite sprite_rect; + AscShaderSprite sprite_uv; } shader; } AscGLContext; #define asc_active_glctx (&asc_active_window->glctx) -#define ASC_TEXTURE_2D_EMPTY_1X1 (&asc_active_glctx->textures_2d[ASC_TEXTURE_2D_EMPTY_1X1_IDX]) -#define ASC_TEXTURE_RECT_EMPTY_1X1 (&asc_active_glctx->textures_rect[ASC_TEXTURE_RECT_EMPTY_1X1_IDX]) -#define ASC_SHADER_SPRITE (&asc_active_glctx->shader.sprite) + +#define ASC_SHADER_SPRITE_RECT (&asc_active_glctx->shader.sprite_rect) +#define ASC_SHADER_SPRITE_UV (&asc_active_glctx->shader.sprite_uv) bool asc_gl_context_initialize( AscGLContext *ctx,
--- a/src/ascension/scene.h Sun Jun 01 14:59:40 2025 +0200 +++ b/src/ascension/scene.h Sun Jun 01 16:35:23 2025 +0200 @@ -31,9 +31,14 @@ #include "scene_node.h" #include "camera.h" +#include <cx/list.h> + typedef struct { AscCamera camera; AscSceneNode *root; + struct { + CxList *render_groups[ASC_RENDER_GROUP_COUNT]; + } internal; } AscScene; /**
--- a/src/ascension/scene_node.h Sun Jun 01 14:59:40 2025 +0200 +++ b/src/ascension/scene_node.h Sun Jun 01 16:35:23 2025 +0200 @@ -41,8 +41,10 @@ enum AscRenderGroup { ASC_RENDER_GROUP_NONE = -1, - ASC_RENDER_GROUP_SPRITE_OPAQUE, - ASC_RENDER_GROUP_SPRITE_BLEND, + ASC_RENDER_GROUP_SPRITE_OPAQUE_UV, + ASC_RENDER_GROUP_SPRITE_OPAQUE_RECT, + ASC_RENDER_GROUP_SPRITE_BLEND_UV, + ASC_RENDER_GROUP_SPRITE_BLEND_RECT, ASC_RENDER_GROUP_COUNT };
--- a/src/ascension/shader.h Sun Jun 01 14:59:40 2025 +0200 +++ b/src/ascension/shader.h Sun Jun 01 16:35:23 2025 +0200 @@ -28,14 +28,46 @@ #ifndef ASCENSION_SHADER_H #define ASCENSION_SHADER_H -typedef struct AscShaderCodeFiles { +#include "camera.h" + +struct asc_shader_code_files_s { + /** + * File name of the vertex shader. + */ const char *vtx; + /** + * File name of the fragment shader. + */ const char *frag; -} AscShaderCodeFiles; +}; + +struct asc_shader_code_defines_s { + /** + * Preprocessor code that shall be prepended to the vertex shader. + */ + const char *vtx; + /** + * Preprocessor code that shall be prepended to the fragment shader. + */ + const char *frag; +}; + +typedef struct asc_shader_code_info_s { + struct asc_shader_code_files_s files; + struct asc_shader_code_defines_s defines; +} AscShaderCodeInfo; typedef struct AscShaderCodes { char *vtx; char *frag; + /** + * Optional preprocessor code for the vertex shader. + */ + const char *vtx_pp; + /** + * Optional preprocessor code for the fragment shader. + */ + const char *frag_pp; } AscShaderCodes; typedef struct AscShaderProgram { @@ -45,21 +77,14 @@ int projection; } AscShaderProgram; -typedef struct AscShaderSprite { - AscShaderProgram program; - int depth; - int rect_tex; - int uv_tex; -} AscShaderSprite; - /** * Loads shader codes from files. * - * @param files the structure containing the file names + * @param info the structure containing the file names and preprocessing info * @param codes the structure where to store the loaded codes * @return zero on success, non-zero on failure */ -int asc_shader_load_code_files(AscShaderCodeFiles files, AscShaderCodes *codes); +int asc_shader_load_code_files(AscShaderCodeInfo info, AscShaderCodes *codes); /** * Deallocates the memory for the source codes. @@ -83,12 +108,6 @@ */ void asc_shader_program_destroy(AscShaderProgram *program); -/** - * Loads and initializes the sprite shader. - * - * @param sprite the structure to initialize - * @return zero on success, non-zero on failure - */ -int asc_shader_sprite_init(AscShaderSprite *sprite); +void asc_shader_program_use(const AscShaderProgram *shader, const AscCamera *camera); #endif //ASCENSION_SHADER_H
--- a/src/glcontext.c Sun Jun 01 14:59:40 2025 +0200 +++ b/src/glcontext.c Sun Jun 01 16:35:23 2025 +0200 @@ -28,6 +28,8 @@ #include "ascension/glcontext.h" #include "ascension/error.h" +#include "ascension/2d/sprite.h" + #include <GL/glew.h> #include <cx/array_list.h> @@ -46,40 +48,17 @@ } static int asc_shader_initialize_predefined(AscGLContext *ctx) { + // TODO: check if we can replace that by lazy loading shaders just like fonts int ret = 0; - ret |= asc_shader_sprite_init(&ctx->shader.sprite); + ret |= asc_shader_sprite_init(&ctx->shader.sprite_uv, false); + ret |= asc_shader_sprite_init(&ctx->shader.sprite_rect, true); ret |= asc_error_catch_all_gl(); return ret; } static void asc_shader_destroy_predefined(AscGLContext *ctx) { - asc_shader_program_destroy(&ctx->shader.sprite.program); -} - -static int asc_texture_initialize_predefined(AscGLContext *ctx) { - asc_texture_init_rectangle(ctx->textures_rect, ASC_TEXTURE_RECT_COUNT); - asc_texture_init_2d(ctx->textures_2d, ASC_TEXTURE_2D_COUNT); - - // Create a 1x1 surface with 32-bit RGBA format - SDL_Surface* white1x1 = SDL_CreateRGBSurface( - 0, 1, 1, 32, - 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); - if (white1x1 == NULL) { - asc_error("Failed to create surface: %s", SDL_GetError()); - return 1; - } - SDL_FillRect(white1x1, NULL, 0xFFFFFFFF); - - // Initialize the empty textures with the white surface - asc_texture_from_surface(&ctx->textures_rect[ASC_TEXTURE_RECT_EMPTY_1X1_IDX], white1x1); - asc_texture_from_surface(&ctx->textures_2d[ASC_TEXTURE_2D_EMPTY_1X1_IDX], white1x1); - - return asc_error_catch_all_gl(); -} - -static void asc_texture_destroy_predefined(AscGLContext *ctx) { - asc_texture_destroy(ctx->textures_rect, ASC_TEXTURE_RECT_COUNT); - asc_texture_destroy(ctx->textures_2d, ASC_TEXTURE_2D_COUNT); + asc_shader_program_destroy(&ctx->shader.sprite_uv.program); + asc_shader_program_destroy(&ctx->shader.sprite_rect.program); } struct asc_gl_context_cleanup_data { @@ -136,11 +115,6 @@ return false; } - if (asc_texture_initialize_predefined(ctx)) { - asc_error("Initializing predefined textures failed"); - return false; - } - ctx->cleanup_funcs = cxArrayListCreateSimple(sizeof(struct asc_gl_context_cleanup_data), 8); cxDefineDestructor(ctx->cleanup_funcs, asc_gl_context_cleanup); @@ -157,7 +131,6 @@ SDL_GL_MakeCurrent(ctx->window, ctx->glctx); cxListFree(ctx->cleanup_funcs); - asc_texture_destroy_predefined(ctx); asc_shader_destroy_predefined(ctx); // destroy the GL context and the window
--- a/src/scene.c Sun Jun 01 14:59:40 2025 +0200 +++ b/src/scene.c Sun Jun 01 16:35:23 2025 +0200 @@ -33,7 +33,6 @@ #include "ascension/2d.h" #include <cx/tree.h> -#include <cx/linked_list.h> #include <cx/array_list.h> #include <GL/glew.h> @@ -47,13 +46,19 @@ } asc_camera_init_(&scene->camera, args); scene->root = asc_scene_node_empty(); + for (unsigned i = 0 ; i < ASC_RENDER_GROUP_COUNT ; i++) { + scene->internal.render_groups[i] = cxArrayListCreateSimple(CX_STORE_POINTERS, 32); + } asc_dprintf("Initialized scene %"PRIxPTR, (uintptr_t) scene); } void asc_scene_destroy(AscScene *scene) { if (scene->root == NULL) return; - + for (unsigned i = 0 ; i < ASC_RENDER_GROUP_COUNT ; i++) { + cxListFree(scene->internal.render_groups[i]); + scene->internal.render_groups[i] = NULL; + } asc_dprintf("Destroyed scene %"PRIxPTR, (uintptr_t) scene); asc_scene_node_free(scene->root); } @@ -73,6 +78,58 @@ } } +void asc_scene_draw_sprites( + const AscScene *scene, + const CxList *opaque_rect, + const CxList *opaque_uv, + const CxList *blend_rect, + const CxList *blend_uv +) { + // render opaque sprites from front to back + CxIterator iter_opaque_rect = cxListBackwardsIterator(opaque_rect); + CxIterator iter_opaque_uv = cxListBackwardsIterator(opaque_uv); + + // render sprites with alpha value from back to front + CxIterator iter_blend_rect = cxListIterator(blend_rect); + CxIterator iter_blend_uv = cxListIterator(blend_uv); + + // TODO: implement interleaving by depth + if (cxIteratorValid(iter_opaque_rect)) { + glDisable(GL_BLEND); + AscShaderSprite *shader = ASC_SHADER_SPRITE_RECT; + asc_shader_program_use(&shader->program, &scene->camera); + cx_foreach(const AscSprite*, node, iter_opaque_rect) { + asc_sprite_draw(shader, node); + } + } + if (cxIteratorValid(iter_opaque_uv)) { + glDisable(GL_BLEND); + AscShaderSprite *shader = ASC_SHADER_SPRITE_UV; + asc_shader_program_use(&shader->program, &scene->camera); + cx_foreach(const AscSprite*, node, iter_opaque_uv) { + asc_sprite_draw(shader, node); + } + } + if (cxIteratorValid(iter_blend_rect)) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + AscShaderSprite *shader = ASC_SHADER_SPRITE_RECT; + asc_shader_program_use(&shader->program, &scene->camera); + cx_foreach(const AscSprite*, node, iter_blend_rect) { + asc_sprite_draw(shader, node); + } + } + if (cxIteratorValid(iter_blend_uv)) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + AscShaderSprite *shader = ASC_SHADER_SPRITE_UV; + asc_shader_program_use(&shader->program, &scene->camera); + cx_foreach(const AscSprite*, node, iter_blend_uv) { + asc_sprite_draw(shader, node); + } + } +} + void asc_scene_draw(AscScene *scene) { if (scene->root == NULL) return; @@ -90,10 +147,10 @@ } } - // create render groups - CxList *render_group[ASC_RENDER_GROUP_COUNT]; + // reset render groups + CxList **render_group = scene->internal.render_groups; for (unsigned i = 0 ; i < ASC_RENDER_GROUP_COUNT ; i++) { - render_group[i] = cxArrayListCreateSimple(CX_STORE_POINTERS, 32); + cxListClear(render_group[i]); } // update the scene graph and add nodes to their render groups @@ -170,7 +227,6 @@ // ------------------------- // process the render groups // ------------------------- - CxIterator render_iter; // 2D Elements // =========== @@ -179,36 +235,12 @@ // Sprites // ------- - size_t sprite_count = cxListSize(render_group[ASC_RENDER_GROUP_SPRITE_OPAQUE]) - + cxListSize(render_group[ASC_RENDER_GROUP_SPRITE_BLEND]); - if (sprite_count > 0) { - AscShaderProgram *shader = &ASC_SHADER_SPRITE->program; - glUseProgram(shader->id); - glUniformMatrix4fv(shader->projection, 1, - GL_FALSE, scene->camera.projection); - glUniformMatrix4fv(shader->view, 1, - GL_FALSE, scene->camera.view); - - // render opaque sprites from front to back - glDisable(GL_BLEND); - render_iter = cxListBackwardsIterator(render_group[ASC_RENDER_GROUP_SPRITE_OPAQUE]); - cx_foreach(AscSprite const *, node, render_iter) { - asc_sprite_draw(node); - } - - // render sprites with alpha value from back to front - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - render_iter = cxListIterator(render_group[ASC_RENDER_GROUP_SPRITE_BLEND]); - cx_foreach(AscSprite const *, node, render_iter) { - asc_sprite_draw(node); - } - } - - // deallocate render groups - for (unsigned i = 0 ; i < ASC_RENDER_GROUP_COUNT ; i++) { - cxListFree(render_group[i]); - } + asc_scene_draw_sprites(scene, + render_group[ASC_RENDER_GROUP_SPRITE_OPAQUE_RECT], + render_group[ASC_RENDER_GROUP_SPRITE_OPAQUE_UV], + render_group[ASC_RENDER_GROUP_SPRITE_BLEND_RECT], + render_group[ASC_RENDER_GROUP_SPRITE_BLEND_UV] + ); } void asc_scene_add_node(AscScene *scene, AscSceneNode *node) {
--- a/src/shader.c Sun Jun 01 14:59:40 2025 +0200 +++ b/src/shader.c Sun Jun 01 16:35:23 2025 +0200 @@ -43,9 +43,10 @@ * * @param type the shader type (use the GL enum) * @param code the source code + * @param code_pp the optional preprocessor code * @return the compiled shader */ -static unsigned asc_shader_compile(unsigned int type, char const *code) { +static unsigned asc_shader_compile(unsigned int type, const char *code, const char *code_pp) { GLuint id = glCreateShader(type); if (id == 0) { asc_error("glCreateShader failed: %s", glGetError()); @@ -53,8 +54,17 @@ } GLint success; - int length = (int) strlen(code); // must be int because of OpenGL API - glShaderSource(id, 1, &code, &length); + const char *code_array[4]; + GLint length_array[4]; +#define store_str(i, s) do {cxstring cxs = cx_str(s); code_array[i] = cxs.ptr; length_array[i] = (GLint) cxs.length;} while(0) + store_str(0, "#version 400 core\n"); + store_str(1, code_pp); + store_str(2, "\n#line 1\n"); + store_str(3, code); +#undef store_str + + // compile + glShaderSource(id, cx_nmemb(length_array), code_array, length_array); glCompileShader(id); glGetShaderiv(id, GL_COMPILE_STATUS, &success); if (success) { @@ -121,35 +131,14 @@ program->id = 0; } -int asc_shader_sprite_init(AscShaderSprite *sprite) { - AscShaderCodes codes; - if (asc_shader_load_code_files((AscShaderCodeFiles){ - .vtx = "sprite_vtx.glsl", - .frag = "sprite_frag.glsl" - }, &codes)) { - asc_error("Loading sprite shader failed."); - return 1; - } - sprite->program = asc_shader_program_create(codes); - if (asc_has_error()) { - asc_shader_free_codes(codes); - return 1; - } - sprite->depth = glGetUniformLocation(sprite->program.id, "depth"); - sprite->rect_tex = glGetUniformLocation(sprite->program.id, "rect_tex"); - sprite->uv_tex = glGetUniformLocation(sprite->program.id, "uv_tex"); - asc_shader_free_codes(codes); - return 0; -} - AscShaderProgram asc_shader_program_create(AscShaderCodes codes) { unsigned shader[4]; unsigned n = 0; if (codes.vtx) { - shader[n++] = asc_shader_compile(GL_VERTEX_SHADER, codes.vtx); + shader[n++] = asc_shader_compile(GL_VERTEX_SHADER, codes.vtx, codes.vtx_pp); } if (codes.frag) { - shader[n++] = asc_shader_compile(GL_FRAGMENT_SHADER, codes.frag); + shader[n++] = asc_shader_compile(GL_FRAGMENT_SHADER, codes.frag, codes.frag_pp); } const AscShaderProgram prog = asc_shader_link(shader, n); for (unsigned i = 0; i < n; i++) { @@ -158,6 +147,12 @@ return prog; } +void asc_shader_program_use(const AscShaderProgram *shader, const AscCamera *camera) { + glUseProgram(shader->id); + glUniformMatrix4fv(shader->projection, 1, GL_FALSE, camera->projection); + glUniformMatrix4fv(shader->view, 1, GL_FALSE, camera->view); +} + static int asc_shader_load_code_file(const char *filename, char **code) { if (filename == NULL) { *code = NULL; @@ -177,10 +172,12 @@ return *code == NULL ? -1 : 0; } -int asc_shader_load_code_files(AscShaderCodeFiles files, AscShaderCodes *codes) { +int asc_shader_load_code_files(AscShaderCodeInfo info, AscShaderCodes *codes) { int ret = 0; - ret |= asc_shader_load_code_file(files.vtx, &codes->vtx); - ret |= asc_shader_load_code_file(files.frag, &codes->frag); + ret |= asc_shader_load_code_file(info.files.vtx, &codes->vtx); + codes->vtx_pp = info.defines.vtx; + ret |= asc_shader_load_code_file(info.files.frag, &codes->frag); + codes->frag_pp = info.defines.frag; return ret; }
--- a/src/sprite.c Sun Jun 01 14:59:40 2025 +0200 +++ b/src/sprite.c Sun Jun 01 16:35:23 2025 +0200 @@ -32,6 +32,7 @@ #include <GL/glew.h> +#include "ascension/error.h" #include "ascension/mesh.h" static void asc_sprite_destroy(AscSceneNode *node) { @@ -75,8 +76,8 @@ AscSceneNode *node = (AscSceneNode *) sprite; asc_scene_node_name(node, args.name); node->render_group = args.opaque - ? ASC_RENDER_GROUP_SPRITE_OPAQUE - : ASC_RENDER_GROUP_SPRITE_BLEND; + ? ASC_RENDER_GROUP_SPRITE_OPAQUE_UV + : ASC_RENDER_GROUP_SPRITE_BLEND_UV; node->update_func = asc_sprite_update; node->destroy_func = asc_sprite_destroy; @@ -87,22 +88,13 @@ return node; } -void asc_sprite_draw(AscSprite const *node) { - // Obtain shader - AscShaderSprite *shader = ASC_SHADER_SPRITE; - +void asc_sprite_draw(const AscShaderSprite *shader, const AscSprite *node) { // Upload model matrix glUniformMatrix4fv(shader->program.model, 1, GL_FALSE, node->data.world_transform); // Bind texture - if (node->texture->target == GL_TEXTURE_RECTANGLE) { - asc_texture_bind(node->texture, shader->rect_tex, 0); - asc_texture_bind(ASC_TEXTURE_2D_EMPTY_1X1, shader->uv_tex, 1); - } else { - asc_texture_bind(ASC_TEXTURE_RECT_EMPTY_1X1, shader->rect_tex, 0); - asc_texture_bind(node->texture, shader->uv_tex, 1); - } + asc_texture_bind(node->texture, shader->tex, 0); // Draw mesh asc_mesh_draw_triangle_strip(&node->mesh); @@ -114,3 +106,24 @@ sprite->height = height; asc_node_update(node); } + +int asc_shader_sprite_init(AscShaderSprite *sprite, bool rect) { + AscShaderCodes codes; + if (asc_shader_load_code_files((AscShaderCodeInfo){ + .files.vtx = "sprite_vtx.glsl", + .files.frag = "sprite_frag.glsl", + .defines.frag = rect ? "#define USE_RECT" : NULL, + }, &codes)) { + asc_error("Loading sprite shader failed."); + return 1; + } + sprite->program = asc_shader_program_create(codes); + if (asc_has_error()) { + asc_shader_free_codes(codes); + return 1; + } + sprite->tex = glGetUniformLocation(sprite->program.id, "tex"); + asc_shader_free_codes(codes); + + return asc_error_catch_all_gl(); +}
--- a/src/text.c Sun Jun 01 14:59:40 2025 +0200 +++ b/src/text.c Sun Jun 01 16:35:23 2025 +0200 @@ -97,7 +97,7 @@ // node properties asc_scene_node_name(node, args.name); - node->render_group = ASC_RENDER_GROUP_SPRITE_BLEND; + node->render_group = ASC_RENDER_GROUP_SPRITE_BLEND_RECT; node->destroy_func = asc_text_destroy; node->update_func = asc_text_update; node->position = asc_vec3f_new(args.x, args.y, ASC_SCENE_2D_DEPTH_OFFSET);
--- a/test/snake/Makefile Sun Jun 01 14:59:40 2025 +0200 +++ b/test/snake/Makefile Sun Jun 01 16:35:23 2025 +0200 @@ -47,17 +47,17 @@ $(BUILD_DIR)/snake.o: snake.c ../../src/ascension/core.h \ ../../src/ascension/error.h ../../src/ascension/context.h \ ../../src/ascension/datatypes.h ../../src/ascension/window.h \ - ../../src/ascension/glcontext.h ../../src/ascension/shader.h \ - ../../src/ascension/texture.h ../../src/ascension/scene.h \ - ../../src/ascension/scene_node.h ../../src/ascension/transform.h \ + ../../src/ascension/glcontext.h ../../src/ascension/2d/sprite.h \ + ../../src/ascension/2d/../scene_node.h \ + ../../src/ascension/2d/../datatypes.h \ + ../../src/ascension/2d/../transform.h ../../src/ascension/2d/../mesh.h \ + ../../src/ascension/2d/../texture.h ../../src/ascension/2d/../shader.h \ + ../../src/ascension/2d/../camera.h ../../src/ascension/texture.h \ + ../../src/ascension/scene.h ../../src/ascension/scene_node.h \ ../../src/ascension/camera.h ../../src/ascension/input.h \ ../../src/ascension/ui/font.h ../../src/ascension/behavior.h \ ../../src/ascension/ui.h ../../src/ascension/ui/text.h \ - ../../src/ascension/ui/font.h ../../src/ascension/ui/../2d/sprite.h \ - ../../src/ascension/ui/../2d/../scene_node.h \ - ../../src/ascension/ui/../2d/../mesh.h \ - ../../src/ascension/ui/../2d/../datatypes.h \ - ../../src/ascension/ui/../2d/../texture.h + ../../src/ascension/ui/font.h ../../src/ascension/ui/../2d/sprite.h @echo "Compiling $<" $(CC) -o $@ $(CFLAGS) -c $<