X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fclient%2Ftexture.c;h=a80738faeb6c87c49fee353d50cd3456c377e00d;hb=6d60079b8f06b9701c4e823d33ac11a843814183;hp=1036a91218f133e457f56d63116118fd6551a43a;hpb=7edcc13168d2d0738225d8adb5e431a069919ccf;p=dragonblocks_alpha.git diff --git a/src/client/texture.c b/src/client/texture.c index 1036a91..a80738f 100644 --- a/src/client/texture.c +++ b/src/client/texture.c @@ -1,57 +1,103 @@ #define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_RESIZE_IMPLEMENTATION #include +#include #include -#include +#include #include "client/client_config.h" +#include "client/gl_debug.h" #include "client/texture.h" -#include "util.h" -static List textures; +static Tree textures; -__attribute((constructor(101))) static void textures_init() +typedef struct { + char *path; + Texture texture; +} TextureLookup; + +static int cmp_texture(TextureLookup *texture, char *path) { - textures = list_create(&list_compare_string); + return strcmp(texture->path, path); } -static void list_delete_texture(unused void *key, void *value, unused void *arg) +static bool lookup_texture(char *path, Texture **texture) { - texture_delete(value); + TreeNode **node = tree_nfd(&textures, path, &cmp_texture); + + if (*node) { + *texture = &((TextureLookup *) (*node)->dat)->texture; + return true; + } + + TextureLookup *lookup = malloc(sizeof *lookup); + lookup->path = strdup(path); + *texture = &lookup->texture; + + tree_nmk(&textures, node, lookup); + return false; } -__attribute((destructor)) static void textures_deinit() +static void delete_texture(TextureLookup *lookup) { - list_clear_func(&textures, &list_delete_texture, NULL); + free(lookup->path); + texture_destroy(&lookup->texture); + free(lookup); } -Texture *texture_create(unsigned char *data, int width, int height, GLenum format, bool mipmap) +__attribute__((constructor(101))) static void textures_init() { - Texture *texture = malloc(sizeof(Texture)); - texture->width = width; - texture->height = height; + tree_ini(&textures); +} - glGenTextures(1, &texture->id); +__attribute__((destructor)) static void textures_deinit() +{ + tree_clr(&textures, &delete_texture, NULL, NULL, 0); +} - glBindTexture(GL_TEXTURE_2D, texture->id); +Texture *texture_load(char *path, bool mipmap) +{ + Texture *texture; + if (lookup_texture(path, &texture)) + return texture; + + unsigned char *data = stbi_load(path, + &texture->width, &texture->height, &texture->channels, 0); + if (!data) { + fprintf(stderr, "[error] failed to load texture %s\n", path); + exit(EXIT_FAILURE); + } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (mipmap && client_config.mipmap) ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + texture_upload(texture, data, GL_RGBA, mipmap); + stbi_image_free(data); - glTexImage2D(GL_TEXTURE_2D, 0, format, texture->width, texture->height, 0, format, GL_UNSIGNED_BYTE, data); - glGenerateMipmap(GL_TEXTURE_2D); + return texture; +} - glBindTexture(GL_TEXTURE_2D, 0); +static inline int least_common_multiple(int a, int b) +{ + int high, low; + if (a > b) { + high = a; + low = b; + } else { + high = b; + low = a; + } - return texture; + int lcm = high; + while (lcm % low) + lcm += high; + return lcm; } -GLuint texture_create_cubemap(char *path) +Texture *texture_load_cubemap(char *path) { - GLuint id; - glGenTextures(1, &id); + Texture *texture; + if (lookup_texture(path, &texture)) + return texture; - glBindTexture(GL_TEXTURE_CUBE_MAP, id); + glGenTextures(1, &texture->txo); GL_DEBUG + glBindTexture(GL_TEXTURE_CUBE_MAP, texture->txo); GL_DEBUG const char *directions[6] = { "right", @@ -62,51 +108,77 @@ GLuint texture_create_cubemap(char *path) "back", }; + typedef struct { + unsigned char *data; + int width, height, channels; + } CubemapFace; + + CubemapFace faces[6]; + int size = 1; + for (int i = 0; i < 6; i++) { char filename[strlen(path) + 1 + strlen(directions[i]) + 1 + 3 + 1]; sprintf(filename, "%s/%s.png", path, directions[i]); - int width, height, channels; - unsigned char *data = stbi_load(filename, &width, &height, &channels, 0); - if (! data) { - fprintf(stderr, "Failed to load texture %s\n", filename); - return 0; + if (!(faces[i].data = stbi_load(filename, + &faces[i].width, &faces[i].height, &faces[i].channels, 0))) { + fprintf(stderr, "[error] failed to load texture %s\n", filename); + exit(EXIT_FAILURE); } - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - stbi_image_free(data); + size = least_common_multiple(size, faces[i].width); + size = least_common_multiple(size, faces[i].height); } - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - - return id; -} - -void texture_delete(Texture *texture) -{ - glDeleteTextures(1, &texture->id); - free(texture); -} + for (int i = 0; i < 6; i++) { + unsigned char *data = faces[i].data; + + bool resize = faces[i].width != size || faces[i].height != size; + if (resize) { + data = malloc(size * size * faces[i].channels); + + stbir_resize_uint8_generic( + faces[i].data, faces[i].width, faces[i].height, 0, + data, size, size, 0, + faces[i].channels, STBIR_ALPHA_CHANNEL_NONE, 0, + STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR, + NULL); + } -Texture *texture_load(char *path, bool mipmap) -{ - int width, height, channels; + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA, + size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); GL_DEBUG - unsigned char *data = stbi_load(path, &width, &height, &channels, 0); - if (! data) { - fprintf(stderr, "Failed to load texture %s\n", path); - return NULL; + stbi_image_free(faces[i].data); + if (resize) + stbi_image_free(data); } - Texture *texture = texture_create(data, width, height, GL_RGBA, mipmap); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); GL_DEBUG + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); GL_DEBUG + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); GL_DEBUG + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); GL_DEBUG + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); GL_DEBUG - stbi_image_free(data); + return texture; +} - list_put(&textures, texture, NULL); +void texture_upload(Texture *texture, unsigned char *data, GLenum format, bool mipmap) +{ + glGenTextures(1, &texture->txo); GL_DEBUG + glBindTexture(GL_TEXTURE_2D, texture->txo); GL_DEBUG + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (mipmap && client_config.mipmap) + ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST); GL_DEBUG + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); GL_DEBUG + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); GL_DEBUG + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); GL_DEBUG + + glTexImage2D(GL_TEXTURE_2D, 0, format, + texture->width, texture->height, 0, format, GL_UNSIGNED_BYTE, data); GL_DEBUG + glGenerateMipmap(GL_TEXTURE_2D); GL_DEBUG +} - return texture; +void texture_destroy(Texture *texture) +{ + glDeleteTextures(1, &texture->txo); GL_DEBUG }