fixed bugs in the resource manager and removed the hardcoded textures
[laserbrain_demo] / src / texture.cc
1 #include <math.h>
2 #include <assert.h>
3 #include "texture.h"
4 #include "image.h"
5 #include "opengl.h"
6 #include "imago2.h"
7
8 static int glifmt_from_ifmt(unsigned int ifmt);
9 static int glfmt_from_ifmt(unsigned int ifmt);
10 static int gltype_from_ifmt(unsigned int ifmt);
11
12 static int glifmt_from_imgfmt(Image::Format fmt);
13
14 static unsigned int type_to_target(TextureType type);
15 static TextureType target_to_type(unsigned int targ);
16
17 static unsigned int cur_target[8] = {
18         GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D,
19         GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D
20 };
21
22 static unsigned int cube_faces[] = {
23         GL_TEXTURE_CUBE_MAP_POSITIVE_X,
24         GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
25         GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
26         GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
27         GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
28         GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
29 };
30
31
32 void bind_texture(Texture *tex, int tunit)
33 {
34         if(tex) {
35                 tex->bind(tunit);
36         } else {
37                 glActiveTexture(GL_TEXTURE0 + tunit);
38                 glBindTexture(cur_target[tunit], 0);
39                 assert(glGetError() == GL_NO_ERROR);
40                 glActiveTexture(GL_TEXTURE0);
41         }
42 }
43
44
45 Image *Texture::default_img;
46
47 Texture::Texture()
48 {
49         target = 0;
50         sz[0] = sz[1] = sz[2] = 0;
51         texfmt = 0;
52
53         img = 0;
54         glGenTextures(1, &id);
55 }
56
57 Texture::~Texture()
58 {
59         if(id) {
60                 glDeleteTextures(1, &id);
61         }
62         if(img) {
63                 delete img;
64         }
65 }
66
67 void Texture::set_wrapping(unsigned int wrap)
68 {
69         if(!target) {
70                 return;
71         }
72
73         glBindTexture(target, id);
74         assert(glGetError() == GL_NO_ERROR);
75         glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap);
76         glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap);
77         glTexParameteri(target, GL_TEXTURE_WRAP_R, wrap);
78 }
79
80 void Texture::set_filtering(unsigned int filt)
81 {
82         unsigned int mag_filter;
83
84         if(!target) {
85                 return;
86         }
87
88         switch(filt) {
89         case GL_LINEAR_MIPMAP_NEAREST:
90         case GL_LINEAR_MIPMAP_LINEAR:
91                 mag_filter = GL_LINEAR;
92                 break;
93
94         case GL_NEAREST_MIPMAP_NEAREST:
95         case GL_NEAREST_MIPMAP_LINEAR:
96                 mag_filter = GL_NEAREST;
97                 break;
98
99         default:
100                 mag_filter = filt;
101         }
102
103         set_filtering(filt, mag_filter);
104 }
105
106 void Texture::set_filtering(unsigned int min_filt, unsigned int mag_filt)
107 {
108         glBindTexture(target, id);
109         assert(glGetError() == GL_NO_ERROR);
110         glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filt);
111         glTexParameteri(target, GL_TEXTURE_MAG_FILTER, mag_filt);
112 }
113
114 unsigned int Texture::get_format() const
115 {
116         return texfmt;
117 }
118
119 int Texture::get_size(int dim) const
120 {
121         if(dim < 0 || dim >= 3) {
122                 return 0;
123         }
124         return sz[dim];
125 }
126
127 unsigned int Texture::get_id() const
128 {
129         return id;
130 }
131
132 TextureType Texture::get_type() const
133 {
134         return target_to_type(target);
135 }
136
137 void Texture::bind(int tex_unit) const
138 {
139         glActiveTexture(GL_TEXTURE0 + tex_unit);
140         glBindTexture(target, id);
141         assert(glGetError() == GL_NO_ERROR);
142         glActiveTexture(GL_TEXTURE0);
143
144         cur_target[tex_unit] = target;
145 }
146
147
148 void Texture::create(int xsz, int ysz, TextureType textype, unsigned int ifmt)
149 {
150         if(textype == TEX_CUBE && xsz != ysz) {
151                 fprintf(stderr, "trying to create cubemap with different width and height (%dx%d)\n", xsz, ysz);
152                 return;
153         }
154
155         int fmt = glfmt_from_ifmt(ifmt);
156         int type = gltype_from_ifmt(ifmt);
157
158         target = type_to_target(textype);
159
160         glBindTexture(target, id);
161         assert(glGetError() == GL_NO_ERROR);
162         glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
163         glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
164
165         switch(type) {
166         case TEX_2D:
167                 glTexImage2D(GL_TEXTURE_2D, 0, glifmt_from_ifmt(ifmt), xsz, ysz, 0, fmt, type, 0);
168                 break;
169
170         case TEX_CUBE:
171                 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
172                 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
173                 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
174                 for(int i=0; i<6; i++) {
175                         glTexImage2D(cube_faces[i], 0, ifmt, xsz, ysz, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
176                 }
177                 break;
178         }
179
180         sz[0] = xsz;
181         sz[1] = ysz;
182         texfmt = ifmt;
183 }
184
185 #define DEF_IMAGE_SIZE  64
186 void Texture::create_default(TextureType type)
187 {
188         if(!default_img) {
189                 default_img = new Image;
190                 default_img->create(DEF_IMAGE_SIZE, DEF_IMAGE_SIZE, Image::FMT_RGBA);
191
192                 unsigned char *pixels = (unsigned char*)default_img->get_pixels();
193                 for(int i=0; i<DEF_IMAGE_SIZE; i++) {
194                         for(int j=0; j<DEF_IMAGE_SIZE; j++) {
195                                 bool chess = ((i >> 3) & 1) == ((j >> 3) & 1);
196                                 pixels[0] = chess ? 255 : 32;
197                                 pixels[1] = 64;
198                                 pixels[2] = chess ? 32 : 255;
199                                 pixels[3] = 255;
200                                 pixels += 4;
201                         }
202                 }
203                 default_img->save("/tmp/foo.png");
204         }
205
206         switch(type) {
207         case TEX_2D:
208                 set_image(*default_img);
209                 break;
210
211         case TEX_CUBE:
212                 for(int i=0; i<6; i++) {
213                         set_image(*default_img, i);
214                 }
215                 break;
216         }
217 }
218
219 void Texture::set_image(const Image &img, int idx)
220 {
221         if(idx >= 0 && idx < 6) {
222                 set_image_cube(img, idx);
223         } else {
224                 if(!set_image_cube(img)) {
225                         set_image_2d(img);
226                 }
227         }
228 }
229
230 void Texture::set_image_2d(const Image &img)
231 {
232         texfmt = glifmt_from_imgfmt(img.get_format());
233         unsigned int fmt = glfmt_from_ifmt(texfmt);
234         unsigned int type = gltype_from_ifmt(texfmt);
235
236         sz[0] = img.get_width();
237         sz[1] = img.get_height();
238
239         target = GL_TEXTURE_2D;
240         glBindTexture(target, id);
241         assert(glGetError() == GL_NO_ERROR);
242         glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
243         glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
244         glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT);
245         glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT);
246
247 #ifdef __GLEW_H__
248         if(GLEW_SGIS_generate_mipmap) {
249                 glTexParameteri(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
250 #endif
251                 glTexImage2D(target, 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels());
252 #ifdef __GLEW_H__
253         } else {
254                 gluBuild2DMipmaps(target, texfmt, sz[0], sz[1], fmt, type, img.get_pixels());
255         }
256 #endif
257
258 #ifdef GL_ES_VERSION_2_0
259         glGenerateMipmap(target);
260 #endif
261 }
262
263 bool Texture::set_image_cube(const Image &img, int idx)
264 {
265         if(idx < 0 || idx >= 6) {
266                 return false;
267         }
268
269         texfmt = glifmt_from_imgfmt(img.get_format());
270         unsigned int fmt = glfmt_from_ifmt(texfmt);
271         unsigned int type = gltype_from_ifmt(texfmt);
272
273         sz[0] = img.get_width();
274         sz[1] = img.get_height();
275
276         target = GL_TEXTURE_CUBE_MAP;
277         glBindTexture(target, id);
278         assert(glGetError() == GL_NO_ERROR);
279         glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
280         glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
281         glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
282         glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
283         glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
284
285         glTexImage2D(cube_faces[idx], 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels());
286         return true;
287 }
288
289 bool Texture::set_image_cube(const Image &img)
290 {
291         static const float one_third = 1.0 / 3.0;
292         static const float two_thirds = 2.0 / 3.0;
293         static const float hcross[2][6] = {
294                 {0.5, 0.0, 0.25, 0.25, 0.25, 0.75}, {one_third, one_third, 0.0, two_thirds, one_third, one_third} };
295         static const float vcross[2][6] = {
296                 {two_thirds, 0.0, one_third, one_third, one_third, one_third}, {0.25, 0.25, 0.0, 0.5, 0.25, 0.75} };
297         static const float hsix[2][6] = {
298                 {0.0, 0.0, one_third, one_third, two_thirds, two_thirds}, {0.0, 0.5, 0.0, 0.5, 0.0, 0.5} };
299
300         int xsz = img.get_width();
301         int ysz = img.get_height();
302
303         if(xsz / 4 == ysz / 3) {
304                 // horizontal cross, assume the vertical bit is center-left
305                 return set_cube_multi(img, hcross[0], hcross[1], xsz / 4);
306         }
307         if(xsz / 3 == ysz / 4) {
308                 // vertical cross, assume the horizontal bit is center-top (180-rotated image 5)
309                 return set_cube_multi(img, vcross[0], vcross[1], ysz / 4, (1 << 5));
310         }
311         if(xsz / 3 == ysz / 2) {
312                 // horizontal sixpack
313                 return set_cube_multi(img, hsix[0], hsix[1], ysz / 2);
314         }
315
316         return false;
317 }
318
319
320 bool Texture::load(const char *fname)
321 {
322         Image img;
323         if(!img.load(fname)) {
324                 fprintf(stderr, "failed to load 2D texture: %s\n", fname);
325                 return false;
326         }
327         set_image(img);
328
329         printf("loaded 2D texture: %s\n", fname);
330         return true;
331 }
332
333 bool Texture::load_cube(const char *fname)
334 {
335         Image img;
336         if(!img.load(fname)) {
337                 return false;
338         }
339         return set_image_cube(img);
340 }
341
342 bool Texture::set_cube_multi(const Image &img, const float *xoffsets, const float *yoffsets, float sz,
343                 unsigned int rotmask)
344 {
345         for(int i=0; i<6; i++) {
346                 Image face;
347
348                 int xoffs = xoffsets[i] * img.get_width();
349                 int yoffs = yoffsets[i] * img.get_height();
350
351                 if(!face.set_pixels(sz, sz, img.get_pixels(), xoffs, yoffs, img.get_width(), img.get_format())) {
352                         return false;
353                 }
354
355                 if(rotmask & (1 << i)) {
356                         face.rotate_180();
357                 }
358                 set_image_cube(face, i);
359         }
360         return true;
361 }
362
363 static int glifmt_from_ifmt(unsigned int ifmt)
364 {
365 #ifdef GL_ES_VERSION_2_0
366         switch(ifmt) {
367         case GL_LUMINANCE16F_ARB:
368         case GL_LUMINANCE32F_ARB:
369                 ifmt = GL_LUMINANCE;
370                 break;
371
372         case GL_RGB16F:
373         case GL_RGB32F:
374                 ifmt = GL_RGB;
375                 break;
376
377         case GL_RGBA16F:
378         case GL_RGBA32F:
379                 ifmt = GL_RGBA;
380                 break;
381
382         default:
383                 break;
384         }
385 #endif
386         return ifmt;    // by default just pass it through...
387 }
388
389 static int glfmt_from_ifmt(unsigned int ifmt)
390 {
391         switch(ifmt) {
392         case GL_LUMINANCE16F_ARB:
393         case GL_LUMINANCE32F_ARB:
394         case GL_SLUMINANCE:
395                 return GL_LUMINANCE;
396
397         case GL_RGB16F:
398         case GL_RGB32F:
399         case GL_SRGB:
400                 return GL_RGB;
401
402         case GL_RGBA16F:
403         case GL_RGBA32F:
404         case GL_SRGB_ALPHA:
405                 return GL_RGBA;
406
407         default:
408                 break;
409         }
410         return ifmt;
411 }
412
413 static int gltype_from_ifmt(unsigned int ifmt)
414 {
415         switch(ifmt) {
416         case GL_RGB16F:
417         case GL_RGBA16F:
418         case GL_LUMINANCE16F_ARB:
419 #ifdef GL_ES_VERSION_2_0
420                 return GL_HALF_FLOAT_OES;
421 #endif
422         case GL_RGB32F:
423         case GL_RGBA32F:
424         case GL_LUMINANCE32F_ARB:
425                 return GL_FLOAT;
426
427         default:
428                 break;
429         }
430         return GL_UNSIGNED_BYTE;
431 }
432
433 static int glifmt_from_imgfmt(Image::Format fmt)
434 {
435         switch(fmt) {
436         case Image::FMT_GREY:
437                 return GL_SLUMINANCE;
438         case Image::FMT_GREY_FLOAT:
439                 return GL_LUMINANCE16F_ARB;
440         case Image::FMT_RGB:
441                 return GL_SRGB;
442         case Image::FMT_RGB_FLOAT:
443                 return GL_RGB16F;
444         case Image::FMT_RGBA:
445                 return GL_SRGB_ALPHA;
446         case Image::FMT_RGBA_FLOAT:
447                 return GL_RGBA16F;
448         default:
449                 break;
450         }
451         return 0;
452 }
453
454 static unsigned int type_to_target(TextureType type)
455 {
456         return type == TEX_CUBE ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
457 }
458
459 static TextureType target_to_type(unsigned int targ)
460 {
461         return targ == GL_TEXTURE_CUBE_MAP ? TEX_CUBE : TEX_2D;
462 }
463
464 // ---- TextureSet ----
465 TextureSet::TextureSet()
466         : DataSet<Texture*>(create_tex, load_tex, done_tex, free_tex)
467 {
468 }
469
470 Texture *TextureSet::get_texture(const char *name, TextureType type) const
471 {
472         std::map<std::string, Texture*>::const_iterator iter = data.find(name);
473         if(iter != data.end()) {
474                 return iter->second;
475         }
476
477         Texture *res = create();
478         data[name] = res;
479         res->create_default(type);
480         resman_lookup(rman, name, res);
481         return res;
482 }
483
484 // static callbacks
485
486 Texture *TextureSet::create_tex()
487 {
488         return new Texture;
489 }
490
491 bool TextureSet::load_tex(Texture *tex, const char *fname)
492 {
493         Image *img = new Image;
494         if(!img->load(fname)) {
495                 delete img;
496                 return false;
497         }
498
499         delete tex->img;
500         tex->img = img;
501
502         return true;
503 }
504
505 bool TextureSet::done_tex(Texture *tex)
506 {
507         if(!tex->img) {
508                 return false;
509         }
510
511         tex->set_image(*tex->img);
512         return true;
513 }
514
515 void TextureSet::free_tex(Texture *tex)
516 {
517         delete tex;
518 }