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