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