]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/client/texture.c
Rework structure
[dragonblocks_alpha.git] / src / client / texture.c
1 #define STB_IMAGE_IMPLEMENTATION
2 #define STB_IMAGE_RESIZE_IMPLEMENTATION
3 #include <stb_image.h>
4 #include <stb_image_resize.h>
5 #include <stdbool.h>
6 #include <dragonstd/tree.h>
7 #include "client/client_config.h"
8 #include "client/gl_debug.h"
9 #include "client/texture.h"
10
11 static Tree textures;
12
13 typedef struct {
14         char *path;
15         Texture texture;
16 } TextureLookup;
17
18 static int cmp_texture(TextureLookup *texture, char *path)
19 {
20         return strcmp(texture->path, path);
21 }
22
23 static bool lookup_texture(char *path, Texture **texture)
24 {
25         TreeNode **node = tree_nfd(&textures, path, &cmp_texture);
26
27         if (*node) {
28                 *texture = &((TextureLookup *) (*node)->dat)->texture;
29                 return true;
30         }
31
32         TextureLookup *lookup = malloc(sizeof *lookup);
33         lookup->path = strdup(path);
34         *texture = &lookup->texture;
35
36         tree_nmk(&textures, node, lookup);
37         return false;
38 }
39
40 static void delete_texture(TextureLookup *lookup)
41 {
42         free(lookup->path);
43         texture_destroy(&lookup->texture);
44         free(lookup);
45 }
46
47 __attribute__((constructor(101))) static void textures_init()
48 {
49         tree_ini(&textures);
50 }
51
52 __attribute__((destructor)) static void textures_deinit()
53 {
54         tree_clr(&textures, &delete_texture, NULL, NULL, 0);
55 }
56
57 Texture *texture_load(char *path, bool mipmap)
58 {
59         Texture *texture;
60         if (lookup_texture(path, &texture))
61                 return texture;
62
63         unsigned char *data = stbi_load(path,
64                 &texture->width, &texture->height, &texture->channels, 0);
65         if (!data) {
66                 fprintf(stderr, "[error] failed to load texture %s\n", path);
67                 abort();
68         }
69
70         texture_upload(texture, data, GL_RGBA, mipmap);
71         stbi_image_free(data);
72
73         return texture;
74 }
75
76 static inline int least_common_multiple(int a, int b)
77 {
78         int high, low;
79         if (a > b) {
80                 high = a;
81                 low = b;
82         } else {
83                 high = b;
84                 low = a;
85         }
86
87         int lcm = high;
88         while (lcm % low)
89                 lcm += high;
90         return lcm;
91 }
92
93 Texture *texture_load_cubemap(char *path, bool bilinear_filter)
94 {
95         Texture *texture;
96         if (lookup_texture(path, &texture))
97                 return texture;
98
99         glGenTextures(1, &texture->txo); GL_DEBUG
100         glBindTexture(GL_TEXTURE_CUBE_MAP, texture->txo); GL_DEBUG
101
102         const char *directions[6] = {
103                 "right",
104                 "left",
105                 "top",
106                 "bottom",
107                 "front",
108                 "back",
109         };
110
111         typedef struct {
112                 unsigned char *data;
113                 int width, height, channels;
114         } CubemapFace;
115
116         CubemapFace faces[6];
117         int size = 1;
118
119         for (int i = 0; i < 6; i++) {
120                 char filename[strlen(path) + 1 + strlen(directions[i]) + 1 + 3 + 1];
121                 sprintf(filename, "%s/%s.png", path, directions[i]);
122
123                 if (!(faces[i].data = stbi_load(filename,
124                                 &faces[i].width, &faces[i].height, &faces[i].channels, 0))) {
125                         fprintf(stderr, "[error] failed to load texture %s\n", filename);
126                         abort();
127                 }
128
129                 size = least_common_multiple(size, faces[i].width);
130                 size = least_common_multiple(size, faces[i].height);
131         }
132
133         for (int i = 0; i < 6; i++) {
134                 unsigned char *data = faces[i].data;
135
136                 bool resize = faces[i].width != size || faces[i].height != size;
137                 if (resize) {
138                         data = malloc(size * size * faces[i].channels);
139
140                         stbir_resize_uint8_generic(
141                                 faces[i].data, faces[i].width, faces[i].height, 0,
142                                 data, size, size, 0,
143                                 faces[i].channels, STBIR_ALPHA_CHANNEL_NONE, 0,
144                                 STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_COLORSPACE_LINEAR,
145                                 NULL);
146                 }
147
148                 glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA,
149                         size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); GL_DEBUG
150
151                 stbi_image_free(faces[i].data);
152                 if (resize)
153                         stbi_image_free(data);
154         }
155
156         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, bilinear_filter ? GL_LINEAR : GL_NEAREST); GL_DEBUG
157         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, bilinear_filter ? GL_LINEAR : GL_NEAREST); GL_DEBUG
158         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); GL_DEBUG
159         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); GL_DEBUG
160         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); GL_DEBUG
161
162         return texture;
163 }
164
165 void texture_upload(Texture *texture, unsigned char *data, GLenum format, bool mipmap)
166 {
167         glGenTextures(1, &texture->txo); GL_DEBUG
168         glBindTexture(GL_TEXTURE_2D, texture->txo); GL_DEBUG
169
170         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (mipmap && client_config.mipmap)
171                 ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST); GL_DEBUG
172         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); GL_DEBUG
173         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); GL_DEBUG
174         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); GL_DEBUG
175
176         glTexImage2D(GL_TEXTURE_2D, 0, format,
177                 texture->width, texture->height, 0, format, GL_UNSIGNED_BYTE, data); GL_DEBUG
178         glGenerateMipmap(GL_TEXTURE_2D); GL_DEBUG
179 }
180
181 void texture_destroy(Texture *texture)
182 {
183         glDeleteTextures(1, &texture->txo); GL_DEBUG
184 }