From 3edd692771f6cd6d743e0aa6c7ab977e7967e87a Mon Sep 17 00:00:00 2001 From: veclavtalica Date: Sun, 28 Jul 2024 23:59:23 +0300 Subject: [PATCH] automatic detenction of texture mode and batching based on it --- src/game/scenes/ingame.c | 23 +++++++------------- src/private/rendering.h | 1 - src/private/textures.h | 4 +++- src/rendering.c | 4 ++-- src/rendering.h | 1 - src/rendering/sprites.h | 26 ++++++++++++++++------- src/textures.c | 45 ++++++++++++++++++++++++++++++++++++++-- src/textures.h | 4 ++++ src/textures/modes.h | 11 ++++++++++ 9 files changed, 89 insertions(+), 30 deletions(-) create mode 100644 src/textures/modes.h diff --git a/src/game/scenes/ingame.c b/src/game/scenes/ingame.c index 666b027..8a26802 100644 --- a/src/game/scenes/ingame.c +++ b/src/game/scenes/ingame.c @@ -11,38 +11,31 @@ static void ingame_tick(struct state *state) { world_drawdef(scn->world); player_calc(scn->player); - for (size_t i = 100000; --i;) - push_sprite_ex((t_frect){ .x = 32, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){ - .path = "/assets/light.png", - .color = (t_color){255, 0, 0, 255}, - .blend = false }); + push_sprite_ex((t_frect){ .x = 32, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){ + .path = "/assets/light.png", + .color = (t_color){255, 0, 0, 255}, }); push_sprite_ex((t_frect){ .x = 48, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){ .path = "/assets/light.png", - .color = (t_color){0, 255, 0, 255}, - .blend = true }); + .color = (t_color){0, 255, 0, 255}, }); push_sprite_ex((t_frect){ .x = 64, .y = 64, .w = 64, .h = 64 }, (t_push_sprite_args){ .path = "/assets/light.png", - .color = (t_color){0, 0, 255, 255}, - .blend = true }); + .color = (t_color){0, 0, 255, 255}, }); push_sprite_ex((t_frect){ .x = 32, .y = 32, .w = 64, .h = 64 }, (t_push_sprite_args){ .path = "/assets/player/baron-walk.png", .color = (t_color){255, 255, 255, 255}, - .rotation = (float)M_PI * 2 * (float)(ctx.tick_count % 64) / 64, - .blend = true }); + .rotation = (float)M_PI * 2 * (float)(ctx.tick_count % 64) / 64, }); push_sprite_ex((t_frect){ .x = 64, .y = 32, .w = 64, .h = 64 }, (t_push_sprite_args){ .path = "/assets/player/baron-walk.png", .color = (t_color){255, 255, 255, 255}, - .rotation = (float)M_PI / 64, - .blend = true }); + .rotation = (float)M_PI / 64, }); push_sprite_ex((t_frect){ .x = 96, .y = 32, .w = 64, .h = 64 }, (t_push_sprite_args){ .path = "/assets/player/baron-walk.png", - .color = (t_color){255, 255, 255, 255}, - .blend = true }); + .color = (t_color){255, 255, 255, 255}, }); unfurl_triangle("/assets/big-violet.png", (t_fvec3){ -1, -1, 0 }, diff --git a/src/private/rendering.h b/src/private/rendering.h index d15467c..e7fe7ee 100644 --- a/src/private/rendering.h +++ b/src/private/rendering.h @@ -17,7 +17,6 @@ struct sprite_primitive { t_texture_key texture_key; bool flip_x; bool flip_y; - bool blend; /* must be explicitly stated, textures having alpha channel isn't enough */ }; struct rect_primitive { diff --git a/src/private/textures.h b/src/private/textures.h index 3ea2ca3..dace5e7 100644 --- a/src/private/textures.h +++ b/src/private/textures.h @@ -2,6 +2,7 @@ #define PRIVATE_TEXTURES_H #include "../util.h" +#include "../textures/modes.h" #include #include @@ -13,7 +14,8 @@ struct texture { t_rect srcrect; /* position in atlas */ SDL_Surface *data; /* original image data */ int atlas_index; - GLuint loner_texture; /* stored directly for loners, == 0 means atlas_index should be used*/ + GLuint loner_texture; /* stored directly for loners, == 0 means atlas_index should be used */ + enum texture_mode mode; }; diff --git a/src/rendering.c b/src/rendering.c index e3e51ee..9f8b59e 100644 --- a/src/rendering.c +++ b/src/rendering.c @@ -71,7 +71,6 @@ static void render_2d(void) { glEnable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0); glDisable(GL_CULL_FACE); - glDisable(GL_ALPHA_TEST); glEnable(GL_DEPTH_TEST); const size_t render_queue_len = arrlenu(ctx.render_queue_2d); @@ -107,11 +106,12 @@ static void render_2d(void) { static void render_space(void) { glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); - glDisable(GL_ALPHA_TEST); + glDepthMask(GL_TRUE); glDepthFunc(GL_LESS); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); + glDisable(GL_ALPHA_TEST); glActiveTexture(GL_TEXTURE0); /* solid white, no modulation */ diff --git a/src/rendering.h b/src/rendering.h index 7e48cd4..5d31cb4 100644 --- a/src/rendering.h +++ b/src/rendering.h @@ -13,7 +13,6 @@ typedef struct push_sprite_args { float rotation; bool flip_x; bool flip_y; - bool blend; } t_push_sprite_args; /* clears all render queues */ diff --git a/src/rendering/sprites.h b/src/rendering/sprites.h index 2e8d894..54e43e5 100644 --- a/src/rendering/sprites.h +++ b/src/rendering/sprites.h @@ -68,7 +68,6 @@ void push_sprite(char *path, t_frect rect) { .texture_key = textures_get_key(&ctx.texture_cache, path), .flip_x = false, .flip_y = false, - .blend = true, }; struct primitive_2d primitive = { @@ -88,7 +87,6 @@ void push_sprite_ex(t_frect rect, t_push_sprite_args args) { .texture_key = textures_get_key(&ctx.texture_cache, args.path), .flip_x = args.flip_x, .flip_y = args.flip_y, - .blend = args.blend, }; struct primitive_2d primitive = { @@ -101,16 +99,16 @@ void push_sprite_ex(t_frect rect, t_push_sprite_args args) { static struct sprite_batch { - int atlas_id; size_t size; /* how many primitives are in current batch */ - bool blend; /* whether it's blended or not */ + int atlas_id; + enum texture_mode mode; bool constant_colored; /* whether colored batch is uniformly colored */ } collect_sprite_batch(const struct primitive_2d *primitives, size_t len) { /* assumes that first primitive is already a sprite */ struct sprite_batch batch = { .atlas_id = textures_get_atlas_id(&ctx.texture_cache, primitives[0].sprite.texture_key), - .blend = primitives[0].sprite.blend, + .mode = textures_get_mode(&ctx.texture_cache, primitives[0].sprite.texture_key), .constant_colored = true, }; @@ -128,7 +126,8 @@ static struct sprite_batch { break; /* only collect the same blend modes */ - if (current->sprite.blend != batch.blend) + const enum texture_mode mode = textures_get_mode(&ctx.texture_cache, current->sprite.texture_key); + if (mode != batch.mode) break; /* only collect the same texture atlases */ @@ -156,13 +155,23 @@ static void render_sprites(const struct primitive_2d primitives[], if (vertex_array == 0) glGenBuffers(1, &vertex_array); - if (batch.blend) { + if (batch.mode == TEXTURE_MODE_GHOSTLY) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDepthFunc(GL_ALWAYS); + glDepthMask(GL_TRUE); + glDisable(GL_ALPHA_TEST); + } else if (batch.mode == TEXTURE_MODE_SEETHROUGH) { + glDisable(GL_BLEND); + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_TRUE); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_EQUAL, 1.0f); } else { glDisable(GL_BLEND); glDepthFunc(GL_LESS); + glDepthMask(GL_TRUE); + glDisable(GL_ALPHA_TEST); } size_t payload_size; @@ -186,7 +195,8 @@ static void render_sprites(const struct primitive_2d primitives[], void *const payload = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); for (size_t i = 0; i < batch.size; ++i) { - const size_t cur = !batch.blend ? batch.size - i - 1: i; /* render opaques front to back */ + /* render opaques front to back */ + const size_t cur = batch.mode == TEXTURE_MODE_GHOSTLY ? i : batch.size - i - 1; const struct sprite_primitive sprite = primitives[cur].sprite; const t_rect srcrect = diff --git a/src/textures.c b/src/textures.c index 5ba34cf..7bd9a4e 100644 --- a/src/textures.c +++ b/src/textures.c @@ -293,6 +293,34 @@ void textures_dump_atlases(struct texture_cache *cache) { } +static enum texture_mode infer_texture_mode(SDL_Surface *surface) { + const uint32_t amask = surface->format->Amask; + if (amask == 0) + return TEXTURE_MODE_OPAQUE; + + enum texture_mode result = TEXTURE_MODE_OPAQUE; + + SDL_LockSurface(surface); + + for (int i = 0; i < surface->w * surface->h; ++i) { + /* TODO: don't assume 32 bit depth ? */ + t_color color; + SDL_GetRGBA(((uint32_t *)surface->pixels)[i], surface->format, &color.r, &color.g, &color.b, &color.a); + + if (color.a == 0) + result = TEXTURE_MODE_SEETHROUGH; + else if (color.a != 255) { + result = TEXTURE_MODE_GHOSTLY; + break; + } + } + + SDL_UnlockSurface(surface); + + return result; +} + + static t_texture_key textures_load(struct texture_cache *cache, const char *path) { /* no need to do anything if it was loaded already */ const ptrdiff_t i = shgeti(cache->hash, path); @@ -300,8 +328,10 @@ static t_texture_key textures_load(struct texture_cache *cache, const char *path return (t_texture_key){ (uint16_t)i }; SDL_Surface *surface = image_to_surface(path); - struct texture new_texture = {0}; - new_texture.data = surface; + struct texture new_texture = { + .data = surface, + .mode = infer_texture_mode(surface), + }; /* it's a "loner texture," it doesn't fit in an atlas so it's not in one */ if (surface->w >= TEXTURE_ATLAS_SIZE || surface->h >= TEXTURE_ATLAS_SIZE) { @@ -472,6 +502,17 @@ void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum } +enum texture_mode textures_get_mode(const struct texture_cache *cache, t_texture_key key) { + if (m_texture_key_is_valid(key)) { + return cache->hash[key.id].value.mode; + } else { + CRY("Texture binding failed.", + "Tried to get texture that isn't loaded."); + return TEXTURE_MODE_GHOSTLY; + } +} + + size_t textures_get_num_atlases(const struct texture_cache *cache) { return cache->atlas_index + 1; } diff --git a/src/textures.h b/src/textures.h index bf9bf8d..ac275a6 100644 --- a/src/textures.h +++ b/src/textures.h @@ -2,6 +2,7 @@ #define TEXTURES_H #include "private/textures.h" +#include "textures/modes.h" #include "util.h" #include @@ -45,6 +46,9 @@ int32_t textures_get_atlas_id(const struct texture_cache *cache, t_texture_key k /* binds atlas texture in opengl state */ void textures_bind(const struct texture_cache *cache, t_texture_key key, GLenum target); +/* returns helpful information about contents of alpha channel in given texture */ +enum texture_mode textures_get_mode(const struct texture_cache *cache, t_texture_key key); + /* returns the number of atlases in the cache */ size_t textures_get_num_atlases(const struct texture_cache *cache); diff --git a/src/textures/modes.h b/src/textures/modes.h new file mode 100644 index 0000000..0de11f9 --- /dev/null +++ b/src/textures/modes.h @@ -0,0 +1,11 @@ +#ifndef TEXTURES_MODES_H +#define TEXTURES_MODES_H + +/* alpha channel information */ +enum texture_mode { + TEXTURE_MODE_OPAQUE, /* all pixels are solid */ + TEXTURE_MODE_SEETHROUGH, /* some pixels are alpha zero */ + TEXTURE_MODE_GHOSTLY, /* arbitrary alpha values */ +}; + +#endif