]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/client/texture.c
9f4f7ec74c9ce4e0fdb691de4c19c3c8d738d053
[dragonblocks_alpha.git] / src / client / texture.c
1 #define STB_IMAGE_IMPLEMENTATION
2 #include <stb/stb_image.h>
3 #include <stdbool.h>
4 #include <dragonstd/tree.h>
5 #include "client/client_config.h"
6 #include "client/texture.h"
7
8 static Tree textures;
9
10 typedef struct {
11         char *path;
12         Texture texture;
13 } TextureLookup;
14
15 static int cmp_texture(TextureLookup *texture, char *path)
16 {
17         return strcmp(texture->path, path);
18 }
19
20 static bool lookup_texture(char *path, Texture **texture)
21 {
22         TreeNode **node = tree_nfd(&textures, path, &cmp_texture);
23
24         if (*node) {
25                 *texture = &((TextureLookup *) &(*node)->dat)->texture;
26                 return true;
27         }
28
29         TextureLookup *lookup = malloc(sizeof *lookup);
30         lookup->path = strdup(path);
31         *texture = &lookup->texture;
32
33         tree_nmk(&textures, node, lookup);
34         return false;
35 }
36
37 static void delete_texture(TextureLookup *lookup)
38 {
39         free(lookup->path);
40         texture_destroy(&lookup->texture);
41         free(lookup);
42 }
43
44 __attribute__((constructor(101))) static void textures_init()
45 {
46         tree_ini(&textures);
47 }
48
49 __attribute__((destructor)) static void textures_deinit()
50 {
51         tree_clr(&textures, &delete_texture, NULL, NULL, 0);
52 }
53
54 Texture *texture_load(char *path, bool mipmap)
55 {
56         Texture *texture;
57         if (lookup_texture(path, &texture))
58                 return texture;
59
60         unsigned char *data = stbi_load(path,
61                 &texture->width, &texture->height, &texture->channels, 0);
62         if (!data) {
63                 fprintf(stderr, "[error] failed to load texture %s\n", path);
64                 exit(EXIT_FAILURE);
65         }
66
67         texture_upload(texture, data, GL_RGBA, mipmap);
68         stbi_image_free(data);
69
70         return texture;
71 }
72
73 Texture *texture_load_cubemap(char *path)
74 {
75         Texture *texture;
76         if (lookup_texture(path, &texture))
77                 return texture;
78
79         glGenTextures(1, &texture->txo);
80         glBindTexture(GL_TEXTURE_CUBE_MAP, texture->txo);
81
82         const char *directions[6] = {
83                 "right",
84                 "left",
85                 "top",
86                 "bottom",
87                 "front",
88                 "back",
89         };
90
91         for (int i = 0; i < 6; i++) {
92                 char filename[strlen(path) + 1 + strlen(directions[i]) + 1 + 3 + 1];
93                 sprintf(filename, "%s/%s.png", path, directions[i]);
94
95                 unsigned char *data = stbi_load(filename,
96                         &texture->width, &texture->height, &texture->channels, 0);
97                 if (!data) {
98                         fprintf(stderr, "[error] failed to load texture %s\n", filename);
99                         exit(EXIT_FAILURE);
100                 }
101
102                 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA,
103                         texture->width, texture->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
104                 stbi_image_free(data);
105         }
106
107         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
108         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
109         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
110         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
111         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
112
113         return texture;
114 }
115
116 void texture_upload(Texture *texture, unsigned char *data, GLenum format, bool mipmap)
117 {
118         glGenTextures(1, &texture->txo);
119         glBindTexture(GL_TEXTURE_2D, texture->txo);
120
121         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (mipmap && client_config.mipmap)
122                 ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST);
123         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
124         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
125         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
126
127         glTexImage2D(GL_TEXTURE_2D, 0, format,
128                 texture->width, texture->height, 0, format, GL_UNSIGNED_BYTE, data);
129         glGenerateMipmap(GL_TEXTURE_2D);
130 }
131
132 void texture_destroy(Texture *texture)
133 {
134         glDeleteTextures(1, &texture->txo);
135 }