diff --git a/src/rendering/twn_gl_15_rendering.c b/src/rendering/twn_gl_15_rendering.c index 46f74c2..038779d 100644 --- a/src/rendering/twn_gl_15_rendering.c +++ b/src/rendering/twn_gl_15_rendering.c @@ -340,7 +340,7 @@ void finish_index_builder(IndexBufferBuilder *builder) { static void load_cubemap_side(const char *path, GLenum target) { - SDL_Surface *surface = textures_load_surface(path); + SDL_Surface *surface = textures_load_surface(path, false); /* TODO: sanity check whether all of them have same dimensions? */ glTexImage2D(target, 0, @@ -350,6 +350,7 @@ static void load_cubemap_side(const char *path, GLenum target) { surface->format->BytesPerPixel == 4 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, surface->pixels); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); diff --git a/src/rendering/twn_gl_any_rendering.c b/src/rendering/twn_gl_any_rendering.c index c9914ea..8dcbb28 100644 --- a/src/rendering/twn_gl_any_rendering.c +++ b/src/rendering/twn_gl_any_rendering.c @@ -147,22 +147,27 @@ GPUTexture create_gpu_texture(TextureFilter filter, bool generate_mipmaps, int c SDL_assert(width > 0 && height > 0); SDL_assert(channels > 0 && channels <= 4); + /* TODO: test whether emscripten emulates this */ #ifndef __EMSCRIPTEN__ - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, (GLboolean)generate_mipmaps); + if (generate_mipmaps) { + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, (GLboolean)generate_mipmaps); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 3); + } #else (void)generate_mipmaps; #endif if (filter == TEXTURE_FILTER_NEAREAST) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, generate_mipmaps ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } else if (filter == TEXTURE_FILTER_LINEAR) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, generate_mipmaps ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + /* it's assumed to be default gl state, so, don't bother */ + // glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + // glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); diff --git a/src/twn_stb.c b/src/twn_stb.c index 82f5121..1a27b32 100644 --- a/src/twn_stb.c +++ b/src/twn_stb.c @@ -25,4 +25,6 @@ #include #define STB_IMAGE_IMPLEMENTATION +#define STBI_NO_STDIO +#define STBI_NO_HDR #include diff --git a/src/twn_textures.c b/src/twn_textures.c index b47e1ac..9be2b72 100644 --- a/src/twn_textures.c +++ b/src/twn_textures.c @@ -95,7 +95,7 @@ static SDL_Surface *gen_missing_texture_surface(void) { } -SDL_Surface *textures_load_surface(const char *path) { +SDL_Surface *textures_load_surface(const char *path, bool apply_border) { if (SDL_strncmp(path, "!", 1) == 0) goto GET_MISSING_TEXTURE; @@ -141,11 +141,59 @@ SDL_Surface *textures_load_surface(const char *path) { if (surface == NULL) goto ERR_CANNOT_CREATE_SURFACE; + /* TODO: investigate possibility of growing 1px border on stbi side, reducing the overhead (right now texture is held 2 times in memory) */ + /* use in atlases introduces seams on filtering, add 1px padding, growing the resulting */ + if (apply_border && (width < 2048 && height < 2048)) { + SDL_Surface* border = SDL_CreateRGBSurface(0, + width + TEXTURE_BORDER_REPEAT_SIZE * 2, + height + TEXTURE_BORDER_REPEAT_SIZE * 2, + channels * 8, + rmask, gmask, bmask, amask); + if (surface == NULL) + goto ERR_CANNOT_CREATE_BORDER; + + /* main portion */ + SDL_SoftStretch(surface, + &(SDL_Rect){ .x = 0, .y = 0, .w = width, .h = height }, + border, + &(SDL_Rect){ .x = TEXTURE_BORDER_REPEAT_SIZE, .y = TEXTURE_BORDER_REPEAT_SIZE, .w = width, .h = height }); + + /* left border */ + SDL_SoftStretch(surface, + &(SDL_Rect){ .w = 1, .h = height }, + border, + &(SDL_Rect){ .y = TEXTURE_BORDER_REPEAT_SIZE, .w = TEXTURE_BORDER_REPEAT_SIZE, .h = height }); + + /* right border */ + SDL_SoftStretch(surface, + &(SDL_Rect){ .x = width - 1, .w = 1, .h = height }, + border, + &(SDL_Rect){ .y = TEXTURE_BORDER_REPEAT_SIZE, .x = width + TEXTURE_BORDER_REPEAT_SIZE, .w = TEXTURE_BORDER_REPEAT_SIZE, .h = height }); + + /* up border */ + SDL_SoftStretch(surface, + &(SDL_Rect){ .w = width, .h = 1 }, + border, + &(SDL_Rect){ .x = TEXTURE_BORDER_REPEAT_SIZE, .w = width, .h = TEXTURE_BORDER_REPEAT_SIZE }); + + /* bottom border */ + SDL_SoftStretch(surface, + &(SDL_Rect){ .y = height - 1, .w = width, .h = 1 }, + border, + &(SDL_Rect){ .x = TEXTURE_BORDER_REPEAT_SIZE, .y = height + TEXTURE_BORDER_REPEAT_SIZE, .w = width, .h = TEXTURE_BORDER_REPEAT_SIZE }); + + stbi_image_free(image_mem); + SDL_FreeSurface(surface); + surface = border; + } + SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE); SDL_SetSurfaceRLE(surface, true); return surface; +ERR_CANNOT_CREATE_BORDER: + SDL_FreeSurface(surface); ERR_CANNOT_CREATE_SURFACE: stbi_image_free(image_mem); @@ -361,7 +409,10 @@ void textures_cache_deinit(TextureCache *cache) { /* free cache hashes */ for (size_t i = 0; i < shlenu(cache->hash); ++i) { - if (missing_texture_surface == NULL || cache->hash[i].value.data->pixels != missing_texture_surface->pixels) + /* TODO: better to have field that stores the source of memory directly, ugh */ + if (cache->hash[i].value.srcrect.w < 2048 && cache->hash[i].value.srcrect.h < 2048) + (void)0; /* do nothing, memory owned by surface */ + else if (missing_texture_surface == NULL || cache->hash[i].value.data->pixels != missing_texture_surface->pixels) stbi_image_free(cache->hash[i].value.data->pixels); else SDL_free(cache->hash[i].value.data->pixels); @@ -427,7 +478,7 @@ bool textures_load_workers_thread(void) { SDL_assert(texture_id != -1 && queue_index != -1); - SDL_Surface *const surface = textures_load_surface(path); + SDL_Surface *const surface = textures_load_surface(path, true); SDL_free(path); Texture const response = { @@ -594,7 +645,15 @@ int32_t textures_get_atlas_id(const TextureCache *cache, TextureKey key) { Rect textures_get_srcrect(const TextureCache *cache, TextureKey key) { if (m_texture_key_is_valid(key)) { - return cache->hash[key.id].value.srcrect; + Rect const srcrect = cache->hash[key.id].value.srcrect; + if (srcrect.w >= 2048 || srcrect.h >= 2048) + return srcrect; + else + /* offset to not include border*/ + return (Rect){ .x = srcrect.x + TEXTURE_BORDER_REPEAT_SIZE, + .y = srcrect.y + TEXTURE_BORDER_REPEAT_SIZE, + .w = srcrect.w - TEXTURE_BORDER_REPEAT_SIZE * 2, + .h = srcrect.h - TEXTURE_BORDER_REPEAT_SIZE * 2 }; } else { CRY("Texture lookup failed.", "Tried to get texture that isn't loaded."); diff --git a/src/twn_textures_c.h b/src/twn_textures_c.h index 37b58df..b0ab49e 100644 --- a/src/twn_textures_c.h +++ b/src/twn_textures_c.h @@ -13,7 +13,7 @@ #define TEXTURE_ATLAS_SIZE_DEFAULT 2048 #define TEXTURE_ATLAS_BIT_DEPTH 32 #define TEXTURE_ATLAS_FORMAT SDL_PIXELFORMAT_RGBA32 - +#define TEXTURE_BORDER_REPEAT_SIZE 8 /* alpha channel information */ typedef enum TextureMode { @@ -98,7 +98,7 @@ void textures_reset_state(void); /* uncached low-level loading */ /* warn: surface->pixels must be freed along side the surface itself */ -SDL_Surface *textures_load_surface(const char *path); +SDL_Surface *textures_load_surface(const char *path, bool apply_border); /* note: will only take an effect after `textures_update_atlas` */ bool textures_load_workers_thread(void);