-no-srgb option
[vrfileman] / src / texture.cc
1 #include <stdlib.h>
2 #include "texture.h"
3 #include "image.h"
4 #include "opengl.h"
5 #include "imago2.h"
6 #include "opt.h"
7
8 #if defined(GL_ES_VERSION_2_0) || defined(GL_VERSION_3_0)
9 #define USE_GL_GENERATE_MIPMAP
10 #elif defined(__GLEW_H__)
11 #define USE_SGIS_GENERATE_MIPMAP
12 #endif
13
14 static int glifmt_from_ifmt(unsigned int ifmt);
15 static int glfmt_from_ifmt(unsigned int ifmt);
16 static int gltype_from_ifmt(unsigned int ifmt);
17
18 static int glifmt_from_imgfmt(Image::Format fmt);
19 static Image::Format imgfmt_from_glifmt(unsigned int ifmt);
20
21 static unsigned int cur_target[8] = {
22         GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D,
23         GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D
24 };
25
26 void bind_texture(Texture *tex, int tunit)
27 {
28         if(tex) {
29                 tex->bind(tunit);
30         } else {
31                 glActiveTexture(GL_TEXTURE0 + tunit);
32                 glBindTexture(cur_target[tunit], 0);
33                 glActiveTexture(GL_TEXTURE0);
34         }
35 }
36
37 Texture *load_texture(const char *fname)
38 {
39         TextureCube *texcube = new TextureCube;
40         if(texcube->load(fname)) {
41                 return texcube;
42         }
43         delete texcube;
44
45         Texture2D *tex = new Texture2D;
46         if(tex->load(fname)) {
47                 return tex;
48         }
49         delete tex;
50         return 0;
51 }
52
53
54 Texture::Texture()
55 {
56         target = 0;
57         sz[0] = sz[1] = sz[2] = 0;
58         texfmt = 0;
59
60         glGenTextures(1, &id);
61 }
62
63 Texture::~Texture()
64 {
65         if(id) {
66                 glDeleteTextures(1, &id);
67         }
68 }
69
70 void Texture::set_wrapping(unsigned int wrap)
71 {
72         if(!target) {
73                 return;
74         }
75
76         glBindTexture(target, id);
77         glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap);
78         glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap);
79 }
80
81 void Texture::set_filtering(unsigned int filt)
82 {
83         unsigned int mag_filter;
84
85         if(!target) {
86                 return;
87         }
88
89         switch(filt) {
90         case GL_LINEAR_MIPMAP_NEAREST:
91         case GL_LINEAR_MIPMAP_LINEAR:
92                 mag_filter = GL_LINEAR;
93                 break;
94
95         case GL_NEAREST_MIPMAP_NEAREST:
96         case GL_NEAREST_MIPMAP_LINEAR:
97                 mag_filter = GL_NEAREST;
98                 break;
99
100         default:
101                 mag_filter = filt;
102         }
103
104         set_filtering(filt, mag_filter);
105 }
106
107 void Texture::set_filtering(unsigned int min_filt, unsigned int mag_filt)
108 {
109         glBindTexture(target, id);
110         glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filt);
111         glTexParameteri(target, GL_TEXTURE_MAG_FILTER, mag_filt);
112 }
113
114 void Texture::set_anisotropy(int aniso)
115 {
116         if(GLEW_EXT_texture_filter_anisotropic) {
117                 glBindTexture(target, id);
118                 glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);
119         }
120 }
121
122 unsigned int Texture::get_format() const
123 {
124         return texfmt;
125 }
126
127 int Texture::get_size(int dim) const
128 {
129         if(dim < 0 || dim >= 3) {
130                 return 0;
131         }
132         return sz[dim];
133 }
134
135 unsigned int Texture::get_id() const
136 {
137         return id;
138 }
139
140 void Texture::bind(int tex_unit) const
141 {
142         glActiveTexture(GL_TEXTURE0 + tex_unit);
143         glBindTexture(target, id);
144         glActiveTexture(GL_TEXTURE0);
145
146         cur_target[tex_unit] = target;
147 }
148
149
150 // ---- Texture2D ----
151
152 Texture2D::Texture2D()
153 {
154         target = GL_TEXTURE_2D;
155 }
156
157 TextureType Texture2D::get_type() const
158 {
159         return TEXTURE_2D;
160 }
161
162 void Texture2D::create(int xsz, int ysz, unsigned int ifmt)
163 {
164         int fmt = glfmt_from_ifmt(ifmt);
165         int type = gltype_from_ifmt(ifmt);
166
167         glBindTexture(GL_TEXTURE_2D, id);
168         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
169         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
170         glTexImage2D(GL_TEXTURE_2D, 0, glifmt_from_ifmt(ifmt), xsz, ysz, 0, fmt, type, 0);
171         sz[0] = xsz;
172         sz[1] = ysz;
173         texfmt = ifmt;
174 }
175
176 void Texture2D::set_image(const Image &img, int idx)
177 {
178         texfmt = glifmt_from_imgfmt(img.get_format());
179         unsigned int fmt = glfmt_from_ifmt(texfmt);
180         unsigned int type = gltype_from_ifmt(texfmt);
181
182         sz[0] = img.get_width();
183         sz[1] = img.get_height();
184
185         glBindTexture(GL_TEXTURE_2D, id);
186         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
187         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
188         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
189         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
190
191 #ifdef USE_SGIS_GENERATE_MIPMAP
192         if(GLEW_SGIS_generate_mipmap) {
193                 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
194 #endif
195                 glTexImage2D(GL_TEXTURE_2D, 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels());
196 #ifdef USE_SGIS_GENERATE_MIPMAP
197         } else {
198                 gluBuild2DMipmaps(GL_TEXTURE_2D, texfmt, sz[0], sz[1], fmt, type, img.get_pixels());
199         }
200 #endif
201
202 #ifdef USE_GL_GENERATE_MIPMAP
203         glGenerateMipmap(GL_TEXTURE_2D);
204 #endif
205 }
206
207 void Texture2D::get_image(Image *img, int idx) const
208 {
209 #ifndef GL_ES_VERSION_2_0
210         glBindTexture(GL_TEXTURE_2D, id);
211
212         Image::Format img_fmt = imgfmt_from_glifmt(texfmt);
213         img->create(sz[0], sz[1], img_fmt);
214
215         unsigned int fmt = glfmt_from_ifmt(texfmt);
216         unsigned int type = gltype_from_ifmt(texfmt);
217         glGetTexImage(GL_TEXTURE_2D, 0, fmt, type, img->get_pixels());
218 #endif  // GL_ES_VERSION_2_0
219 }
220
221 void Texture2D::set_subimage(const Image &img, int xoffs, int yoffs)
222 {
223         unsigned int ifmt = glifmt_from_imgfmt(img.get_format());
224
225         glBindTexture(GL_TEXTURE_2D, id);
226         glTexSubImage2D(GL_TEXTURE_2D, 0, xoffs, yoffs, img.get_width(), img.get_height(),
227                         glfmt_from_ifmt(ifmt), gltype_from_ifmt(ifmt), img.get_pixels());
228 }
229
230 bool Texture2D::load(const char *fname)
231 {
232         Image img;
233         if(!img.load(fname)) {
234                 fprintf(stderr, "failed to load 2D texture: %s\n", fname);
235                 return false;
236         }
237         set_image(img);
238
239         printf("loaded 2D texture: %s\n", fname);
240         return true;
241 }
242
243 bool Texture2D::save(const char *fname) const
244 {
245 #ifndef GL_ES_VERSION_2_0
246         unsigned char *pixels = new unsigned char[sz[0] * sz[1] * 4];
247
248         glBindTexture(GL_TEXTURE_2D, id);
249         glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
250
251         if(img_save_pixels(fname, pixels, sz[0], sz[1]) == -1) {
252                 fprintf(stderr, "failed to save 2D texture: %s\n", fname);
253                 delete [] pixels;
254                 return false;
255         }
256
257         printf("saved 2D texture: %s\n", fname);
258         delete [] pixels;
259         return true;
260 #else
261         return false;   // TODO
262 #endif
263 }
264
265 // ---- TextureCube ----
266 static unsigned int cube_faces[] = {
267         GL_TEXTURE_CUBE_MAP_POSITIVE_X,
268         GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
269         GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
270         GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
271         GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
272         GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
273 };
274
275 TextureCube::TextureCube()
276 {
277         target = GL_TEXTURE_CUBE_MAP;
278 }
279
280 TextureType TextureCube::get_type() const
281 {
282         return TEXTURE_CUBE;
283 }
284
285
286 void TextureCube::create(int xsz, int ysz, unsigned int ifmt)
287 {
288         if(xsz != ysz) {
289                 fprintf(stderr, "trying to create cubemap with different width and height (%dx%d)\n", xsz, ysz);
290                 return;
291         }
292
293         texfmt = ifmt;
294
295         glBindTexture(GL_TEXTURE_CUBE_MAP, id);
296         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
297         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
298         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
299         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
300
301         for(int i=0; i<6; i++) {
302                 glTexImage2D(cube_faces[i], 0, ifmt, xsz, ysz, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
303         }
304 }
305
306 void TextureCube::set_image(const Image &img, int idx)
307 {
308         // TODO
309 }
310
311 void TextureCube::get_image(Image *img, int idx) const
312 {
313         // TODO
314 }
315
316 bool TextureCube::load(const char *fname)
317 {
318         return false;   // TODO
319 }
320
321 bool TextureCube::save(const char *fname) const
322 {
323         return false;   // TODO
324 }
325
326 static int glifmt_from_ifmt(unsigned int ifmt)
327 {
328 #ifdef GL_ES_VERSION_2_0
329         switch(ifmt) {
330         case GL_LUMINANCE16F:
331         case GL_LUMINANCE32F:
332                 ifmt = GL_LUMINANCE;
333                 break;
334
335         case GL_RGB16F:
336         case GL_RGB32F:
337                 ifmt = GL_RGB;
338                 break;
339
340         case GL_RGBA16F:
341         case GL_RGBA32F:
342                 ifmt = GL_RGBA;
343                 break;
344
345         default:
346                 break;
347         }
348 #endif
349         return ifmt;    // by default just pass it through...
350 }
351
352 static int glfmt_from_ifmt(unsigned int ifmt)
353 {
354         switch(ifmt) {
355         case GL_LUMINANCE16F:
356         case GL_LUMINANCE32F:
357         case GL_SLUMINANCE:
358                 return GL_LUMINANCE;
359
360         case GL_RGB16F:
361         case GL_RGB32F:
362         case GL_SRGB:
363                 return GL_RGB;
364
365         case GL_RGBA16F:
366         case GL_RGBA32F:
367         case GL_SRGB_ALPHA:
368                 return GL_RGBA;
369
370         default:
371                 break;
372         }
373         return ifmt;
374 }
375
376 static int gltype_from_ifmt(unsigned int ifmt)
377 {
378         switch(ifmt) {
379         case GL_RGB16F:
380         case GL_RGBA16F:
381         case GL_LUMINANCE16F:
382 #ifdef GL_ES_VERSION_2_0
383                 return GL_HALF_FLOAT_OES;
384 #endif
385         case GL_RGB32F:
386         case GL_RGBA32F:
387         case GL_LUMINANCE32F:
388                 return GL_FLOAT;
389
390         default:
391                 break;
392         }
393         return GL_UNSIGNED_BYTE;
394 }
395
396 static int glifmt_from_imgfmt(Image::Format fmt)
397 {
398         bool use_srgb = GLEW_EXT_texture_sRGB && opt.srgb;
399
400         switch(fmt) {
401         case Image::FMT_GREY:
402                 return use_srgb ? GL_SLUMINANCE : GL_LUMINANCE;
403
404         case Image::FMT_GREY_FLOAT:
405                 return GL_LUMINANCE16F;
406
407         case Image::FMT_RGB:
408                 return use_srgb ? GL_SRGB : GL_RGB;
409
410         case Image::FMT_RGB_FLOAT:
411                 return GL_RGB16F;
412
413         case Image::FMT_RGBA:
414                 return use_srgb ? GL_SRGB_ALPHA : GL_RGBA;
415
416         case Image::FMT_RGBA_FLOAT:
417                 return GL_RGBA16F;
418
419         default:
420                 break;
421         }
422         return 0;
423 }
424
425 static Image::Format imgfmt_from_glifmt(unsigned int ifmt)
426 {
427         switch(ifmt) {
428         case GL_LUMINANCE:
429         case GL_SLUMINANCE:
430                 return Image::FMT_GREY;
431         case GL_LUMINANCE16F:
432                 return Image::FMT_GREY_FLOAT;
433         case GL_RGB:
434         case GL_SRGB:
435                 return Image::FMT_RGB;
436         case GL_RGB16F:
437                 return Image::FMT_RGB_FLOAT;
438         case GL_RGBA:
439         case GL_SRGB_ALPHA:
440                 return Image::FMT_RGBA;
441         case GL_RGBA16F:
442                 return Image::FMT_RGBA_FLOAT;
443         default:
444                 break;
445         }
446
447         fprintf(stderr, "imgfmt_from_glifmt: unknown internal format: %x\n", ifmt);
448 #ifndef NDEBUG
449         abort();
450 #endif
451         return Image::FMT_RGBA; // ... whatever
452 }