added 3dengfx into the repo, probably not the correct version for this
[summerhack] / src / 3dengfx / src / 3dengfx / texman.cpp
1 /*
2 This file is part of 3dengfx, realtime visualization system.
3 Copyright (C) 2004, 2005, 2006 John Tsiombikas <nuclear@siggraph.org>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19 /* texture manager
20  *
21  * Author: John Tsiombikas 2004
22  */
23
24 #include "3dengfx_config.h"
25
26 #include <string>
27 #include <cstring>
28 #include "texman.hpp"
29 #include "common/hashtable.hpp"
30 #include "gfx/image.h"
31 #include "gfx/color.hpp"
32 #include "n3dmath2/n3dmath2.hpp"
33 #include "common/string_hash.hpp"
34 #include "common/err_msg.h"
35
36 using std::string;
37
38 static void create_normal_cube_map();
39
40 static HashTable<string, Texture*> *textures;
41 static Texture *normal_cubemap;
42
43 static void delete_texture(Texture *tex) {
44         glDeleteTextures(1, &tex->tex_id);
45         glGetError();
46         //delete tex;
47 }
48
49 static void init_tex_man() {
50         if(textures) return;
51         textures = new HashTable<string, Texture*>;
52         textures->set_hash_function(string_hash);
53         textures->set_data_destructor(delete_texture);
54 }
55
56 void add_texture(Texture *texture, const char *fname) {
57         
58         if(!textures) init_tex_man();
59         
60         if(!fname) {    // enter a randomly named texture
61                 textures->insert(tmpnam(0), texture);
62         } else {
63                 textures->insert(fname, texture);
64         }
65 }
66
67 // TODO: implement this one, after making HashTable remove by value
68 void remove_texture(Texture *texture) {
69 }
70
71 Texture *find_texture(const char *fname) {
72         
73         if(!textures) init_tex_man();
74         
75         Pair<string, Texture*> *res = textures->find(fname);
76         return res ? res->val : 0;
77 }
78
79
80 /* ----- get_texture() function -----
81  * first looks in the texture database in constant time (hash table)
82  * if the texture is already there it just returns the pointer. If the
83  * texture is not there it tries to load the image data, create the texture
84  * and return it, and if it fails it returns a NULL pointer
85  */
86 Texture *get_texture(const char *fname) {
87         if(!fname) return 0;
88         
89         Texture *tex;
90         if((tex = find_texture(fname))) return tex;
91
92         // first check to see if it's a custom file (cubemap).
93         if(is_cubemap(fname)) {
94                 tex = load_cubemap(fname);
95                 add_texture(tex, fname);
96                 return tex;
97         }
98         
99         PixelBuffer pbuf;
100         
101         void *img_buf = load_image(fname, &pbuf.width, &pbuf.height);
102         if(!img_buf) return 0;
103
104         pbuf.buffer = new Pixel[pbuf.width * pbuf.height];
105         memcpy(pbuf.buffer, img_buf, pbuf.width * pbuf.height * sizeof(Pixel));
106
107         free_image(img_buf);
108         
109         tex = new Texture;
110         tex->set_pixel_data(pbuf);
111         add_texture(tex, fname);
112         return tex;
113 }
114
115
116 void destroy_textures() {
117         static bool called_again = false;
118
119         if(called_again) {
120                 warning("Multiple destroy_textures() calls");
121                 return;
122         } else {
123                 called_again = true;
124         }
125
126         info("Shutting down texture manager, destroying all textures...");
127         delete textures;
128         textures = 0;
129 }
130
131
132 Texture *make_cube_map(Texture **tex_array) {
133         int size = tex_array[0]->width;
134
135         Texture *cube = new Texture(size, size, TEX_CUBE);
136
137         cube->lock(CUBE_MAP_PX);
138         tex_array[CUBE_MAP_INDEX_PX]->lock();
139         memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_PX]->buffer, size * size * sizeof(Pixel));
140         tex_array[CUBE_MAP_INDEX_PX]->unlock();
141         cube->unlock(CUBE_MAP_PX);
142
143         cube->lock(CUBE_MAP_NX);
144         tex_array[CUBE_MAP_INDEX_NX]->lock();
145         memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_NX]->buffer, size * size * sizeof(Pixel));
146         tex_array[CUBE_MAP_INDEX_NX]->unlock();
147         cube->unlock(CUBE_MAP_NX);
148         
149         cube->lock(CUBE_MAP_PY);
150         tex_array[CUBE_MAP_INDEX_PY]->lock();
151         memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_PY]->buffer, size * size * sizeof(Pixel));
152         tex_array[CUBE_MAP_INDEX_PY]->unlock();
153         cube->unlock(CUBE_MAP_PY);
154         
155         cube->lock(CUBE_MAP_NY);
156         tex_array[CUBE_MAP_INDEX_NY]->lock();
157         memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_NY]->buffer, size * size * sizeof(Pixel));
158         tex_array[CUBE_MAP_INDEX_NY]->unlock();
159         cube->unlock(CUBE_MAP_NY);
160
161         cube->lock(CUBE_MAP_PZ);
162         tex_array[CUBE_MAP_INDEX_PZ]->lock();
163         memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_PZ]->buffer, size * size * sizeof(Pixel));
164         tex_array[CUBE_MAP_INDEX_PZ]->unlock();
165         cube->unlock(CUBE_MAP_PZ);
166
167         cube->lock(CUBE_MAP_NZ);
168         tex_array[CUBE_MAP_INDEX_NZ]->lock();
169         memcpy(cube->buffer, tex_array[CUBE_MAP_INDEX_NZ]->buffer, size * size * sizeof(Pixel));
170         tex_array[CUBE_MAP_INDEX_NZ]->unlock();
171         cube->unlock(CUBE_MAP_NZ);
172
173         return cube;
174 }
175
176
177 Texture *get_normal_cube() {
178         if(!normal_cubemap) create_normal_cube_map();
179         return normal_cubemap;
180 }
181
182 static Color vec_to_color(const Vector3 &v) {
183         return Color(v.x * 0.5 + 0.5, v.y * 0.5 + 0.5, v.z * 0.5 + 0.5);
184 }
185
186 static void create_normal_cube_map() {
187         static const int size = 32;
188         static const scalar_t fsize = (scalar_t)size;
189         static const scalar_t half_size = fsize / 2.0;
190         Pixel *ptr;
191         
192         delete normal_cubemap;
193         normal_cubemap = new Texture(size, size, TEX_CUBE);
194
195         // +X
196         normal_cubemap->lock(CUBE_MAP_PX);
197         ptr = normal_cubemap->buffer;
198         for(int j=0; j<size; j++) {
199                 for(int i=0; i<size; i++) {
200                         Vector3 normal(half_size, -((scalar_t)j + 0.5 - half_size), -((scalar_t)i + 0.5 - half_size));
201                         *ptr++ = pack_color32(vec_to_color(normal.normalized()));
202                 }
203         }
204         normal_cubemap->unlock(CUBE_MAP_PX);
205         
206         // -X
207         normal_cubemap->lock(CUBE_MAP_NX);
208         ptr = normal_cubemap->buffer;
209         for(int j=0; j<size; j++) {
210                 for(int i=0; i<size; i++) {
211                         Vector3 normal(-half_size, -((scalar_t)j + 0.5 - half_size), (scalar_t)i + 0.5 - half_size);
212                         *ptr++ = pack_color32(vec_to_color(normal.normalized()));
213                 }
214         }
215         normal_cubemap->unlock(CUBE_MAP_NX);
216         
217         // +Y
218         normal_cubemap->lock(CUBE_MAP_PY);
219         ptr = normal_cubemap->buffer;
220         for(int j=0; j<size; j++) {
221                 for(int i=0; i<size; i++) {
222                         Vector3 normal((scalar_t)i + 0.5 - half_size, half_size, (scalar_t)j + 0.5 - half_size); 
223                         *ptr++ = pack_color32(vec_to_color(normal.normalized()));
224                 }
225         }
226         normal_cubemap->unlock(CUBE_MAP_PY);
227         
228         // -Y
229         normal_cubemap->lock(CUBE_MAP_NY);
230         ptr = normal_cubemap->buffer;
231         for(int j=0; j<size; j++) {
232                 for(int i=0; i<size; i++) {
233                         Vector3 normal((scalar_t)i + 0.5 - half_size, -half_size, -((scalar_t)j + 0.5 - half_size)); 
234                         *ptr++ = pack_color32(vec_to_color(normal.normalized()));
235                 }
236         }
237         normal_cubemap->unlock(CUBE_MAP_NY);
238
239         // +Z
240         normal_cubemap->lock(CUBE_MAP_PZ);
241         ptr = normal_cubemap->buffer;
242         for(int j=0; j<size; j++) {
243                 for(int i=0; i<size; i++) {
244                         Vector3 normal((scalar_t)i + 0.5 - half_size, -((scalar_t)j + 0.5 - half_size), half_size);
245                         *ptr++ = pack_color32(vec_to_color(normal.normalized()));
246                 }
247         }
248         normal_cubemap->unlock(CUBE_MAP_PZ);
249
250         // -Z
251         normal_cubemap->lock(CUBE_MAP_NZ);
252         ptr = normal_cubemap->buffer;
253         for(int j=0; j<size; j++) {
254                 for(int i=0; i<size; i++) {
255                         Vector3 normal(-((scalar_t)i + 0.5 - half_size), -((scalar_t)j + 0.5 - half_size), -half_size);
256                         *ptr++ = pack_color32(vec_to_color(normal.normalized()));
257                 }
258         }
259         normal_cubemap->unlock(CUBE_MAP_NZ);
260 }
261
262
263 bool is_cubemap(const char *fname) {
264         FILE *fp = fopen(fname, "r");
265         if(!fp) {
266                 return false;
267         }
268
269         char idstr[5];
270         fread(idstr, 1, 4, fp);
271         idstr[4] = 0;
272
273         fclose(fp);
274         return strcmp(idstr, "CUBE") == 0;
275 }
276
277 Texture *load_cubemap(const char *fname) {
278         FILE *fp = fopen(fname, "r");
279         if(!fp) {
280                 error("could not open %s", fname);
281                 return 0;
282         }
283
284         if(!is_cubemap(fname)) {
285                 error("%s is not a cubemap", fname);
286                 fclose(fp);
287                 return 0;
288         }
289
290         char line[512];
291         unsigned int cube_size = 0;
292         unsigned long xsz = 0, ysz = 0;
293         void *img[6] = {0};
294
295         fgets(line, 512, fp);   // skip file id & text description
296         
297         if(fgets(line, 512, fp) && isdigit(*line)) {
298                 cube_size = atoi(line);
299         }
300         
301         for(int i=0; i<6; i++) {
302                 if(!fgets(line, 512, fp)) {
303                         error("%s is not a complete cubemap file, EOF encountered", fname);
304                         break;
305                 }
306
307                 if(line[strlen(line)-1] == '\n') {
308                         line[strlen(line)-1] = 0;
309                 }
310                 
311                 unsigned long x, y;
312                 if(!(img[i] = load_image(line, &x, &y))) {
313                         error("cubemap %s requires %s, which cannot be opened", fname, line);
314                         break;
315                 }
316
317                 if(i > 0 && (x != xsz || y != ysz)) {
318                         error("inconsistent cubemap %s, image sizes differ", fname);
319                         break;
320                 }
321                 xsz = x;
322                 ysz = y;
323
324                 if(xsz != ysz) {
325                         error("cubemap %s contains non-square textures", fname);
326                         break;
327                 }
328         }
329
330         fclose(fp);
331         
332         if(!img[5]) {
333                 for(int i=0; i<6; i++) {
334                         if(img[i]) free_image(img[i]);
335                 }
336                 return 0;
337         }
338         
339         if(xsz != cube_size) {
340                 warning("cubemap %s loaded correctly, but wrong size in the header", fname);
341         }
342
343         Texture *cube = new Texture(cube_size, cube_size, TEX_CUBE);
344
345         CubeMapFace faces[] = {
346                 CUBE_MAP_PX, CUBE_MAP_NX,
347                 CUBE_MAP_PY, CUBE_MAP_NY,
348                 CUBE_MAP_PZ, CUBE_MAP_NZ
349         };
350
351         for(int i=0; i<6; i++) {
352                 cube->lock(faces[i]);
353                 memcpy(cube->buffer, img[i], cube_size * cube_size * sizeof(Pixel));
354                 cube->unlock(faces[i]);
355
356                 free_image(img[i]);
357         }
358
359         return cube;
360 }