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