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