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