vrshoot

view src/texture.cc @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children e7ca128b8713
line source
1 #include <math.h>
2 #include "texture.h"
3 #include "image.h"
4 #include "opengl.h"
5 #include "imago2.h"
6 #include "logger.h"
7 #include "datapath.h"
9 static int glifmt_from_ifmt(unsigned int ifmt);
10 static int glfmt_from_ifmt(unsigned int ifmt);
11 static int gltype_from_ifmt(unsigned int ifmt);
13 static int glifmt_from_imgfmt(Image::Format fmt);
15 static unsigned int cur_target[8] = {
16 GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D,
17 GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D
18 };
20 void set_texture(Texture *tex, int tunit)
21 {
22 if(tex) {
23 tex->bind(tunit);
24 } else {
25 glActiveTexture(GL_TEXTURE0 + tunit);
26 glBindTexture(cur_target[tunit], 0);
27 glActiveTexture(GL_TEXTURE0);
28 }
29 }
31 Texture *load_texture(const char *fname)
32 {
33 TextureCube *texcube = new TextureCube;
34 if(texcube->load(fname)) {
35 return texcube;
36 }
37 delete texcube;
39 Texture2D *tex = new Texture2D;
40 if(tex->load(fname)) {
41 return tex;
42 }
43 delete tex;
44 return 0;
45 }
47 Texture::Texture()
48 {
49 target = 0;
50 sz[0] = sz[1] = sz[2] = 0;
51 texfmt = 0;
53 glGenTextures(1, &id);
54 }
56 Texture::~Texture()
57 {
58 if(id) {
59 glDeleteTextures(1, &id);
60 }
61 }
63 void Texture::set_wrapping(unsigned int wrap)
64 {
65 if(!target) {
66 return;
67 }
69 glBindTexture(target, id);
70 glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap);
71 glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap);
72 }
74 void Texture::set_filtering(unsigned int filt)
75 {
76 unsigned int mag_filter;
78 if(!target) {
79 return;
80 }
82 switch(filt) {
83 case GL_LINEAR_MIPMAP_NEAREST:
84 case GL_LINEAR_MIPMAP_LINEAR:
85 mag_filter = GL_LINEAR;
86 break;
88 case GL_NEAREST_MIPMAP_NEAREST:
89 case GL_NEAREST_MIPMAP_LINEAR:
90 mag_filter = GL_NEAREST;
91 break;
93 default:
94 mag_filter = filt;
95 }
97 set_filtering(filt, mag_filter);
98 }
100 void Texture::set_filtering(unsigned int min_filt, unsigned int mag_filt)
101 {
102 glBindTexture(target, id);
103 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filt);
104 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, mag_filt);
105 }
107 unsigned int Texture::get_format() const
108 {
109 return texfmt;
110 }
112 int Texture::get_size(int dim) const
113 {
114 if(dim < 0 || dim >= 3) {
115 return 0;
116 }
117 return sz[dim];
118 }
120 unsigned int Texture::get_id() const
121 {
122 return id;
123 }
125 void Texture::bind(int tex_unit) const
126 {
127 glActiveTexture(GL_TEXTURE0 + tex_unit);
128 glBindTexture(target, id);
129 glActiveTexture(GL_TEXTURE0);
131 cur_target[tex_unit] = target;
132 }
135 // ---- Texture2D ----
137 Texture2D::Texture2D()
138 {
139 target = GL_TEXTURE_2D;
140 }
142 void Texture2D::create(int xsz, int ysz, unsigned int ifmt)
143 {
144 int fmt = glfmt_from_ifmt(ifmt);
145 int type = gltype_from_ifmt(ifmt);
147 glBindTexture(GL_TEXTURE_2D, id);
148 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
149 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
150 glTexImage2D(GL_TEXTURE_2D, 0, glifmt_from_ifmt(ifmt), xsz, ysz, 0, fmt, type, 0);
151 CHECKGLERR;
152 sz[0] = xsz;
153 sz[1] = ysz;
154 texfmt = ifmt;
155 }
157 void Texture2D::set_image(const Image &img, int idx)
158 {
159 texfmt = glifmt_from_imgfmt(img.get_format());
160 unsigned int fmt = glfmt_from_ifmt(texfmt);
161 unsigned int type = gltype_from_ifmt(texfmt);
163 sz[0] = img.get_width();
164 sz[1] = img.get_height();
166 glBindTexture(GL_TEXTURE_2D, id);
167 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
168 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
169 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
170 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
172 #ifdef __GLEW_H__
173 if(GLEW_SGIS_generate_mipmap) {
174 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
175 #endif
176 glTexImage2D(GL_TEXTURE_2D, 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels());
177 #ifdef __GLEW_H__
178 } else {
179 gluBuild2DMipmaps(GL_TEXTURE_2D, texfmt, sz[0], sz[1], fmt, type, img.get_pixels());
180 }
181 #endif
183 #ifdef GL_ES_VERSION_2_0
184 glGenerateMipmap(GL_TEXTURE_2D);
185 #endif
186 }
188 bool Texture2D::load(const char *fname)
189 {
190 Image img;
191 if(!img.load(fname)) {
192 error_log("failed to load 2D texture: %s\n", fname);
193 return false;
194 }
195 set_image(img);
197 info_log("loaded 2D texture: %s\n", fname);
198 return true;
199 }
201 bool Texture2D::save(const char *fname) const
202 {
203 #ifndef GL_ES_VERSION_2_0
204 unsigned char *pixels = new unsigned char[sz[0] * sz[1] * 4];
206 glBindTexture(GL_TEXTURE_2D, id);
207 glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
209 if(img_save_pixels(fname, pixels, sz[0], sz[1]) == -1) {
210 error_log("failed to save 2D texture: %s\n", fname);
211 delete [] pixels;
212 return false;
213 }
215 info_log("saved 2D texture: %s\n", fname);
216 delete [] pixels;
217 return true;
218 #else
219 return false; // TODO
220 #endif
221 }
223 // ---- TextureCube ----
224 static unsigned int cube_faces[] = {
225 GL_TEXTURE_CUBE_MAP_POSITIVE_X,
226 GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
227 GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
228 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
229 GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
230 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
231 };
233 TextureCube::TextureCube()
234 {
235 target = GL_TEXTURE_CUBE_MAP;
236 }
238 void TextureCube::create(int xsz, int ysz, unsigned int ifmt)
239 {
240 if(xsz != ysz) {
241 error_log("trying to create cubemap with different width and height (%dx%d)\n", xsz, ysz);
242 return;
243 }
245 texfmt = ifmt;
247 glBindTexture(GL_TEXTURE_CUBE_MAP, id);
248 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
249 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
250 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
251 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
252 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
254 for(int i=0; i<6; i++) {
255 glTexImage2D(cube_faces[i], 0, ifmt, xsz, ysz, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
256 }
257 }
259 void TextureCube::set_image(const Image &img, int idx)
260 {
261 texfmt = glifmt_from_imgfmt(img.get_format());
262 unsigned int fmt = glfmt_from_ifmt(texfmt);
263 unsigned int type = gltype_from_ifmt(texfmt);
265 sz[0] = img.get_width();
266 sz[1] = img.get_height();
268 glBindTexture(GL_TEXTURE_CUBE_MAP, id);
269 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
270 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
271 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
272 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
273 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
275 glTexImage2D(cube_faces[idx], 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels());
276 }
278 bool TextureCube::load(const char *fname)
279 {
280 static const float one_third = 1.0 / 3.0;
281 static const float two_thirds = 2.0 / 3.0;
282 static const float hcross[2][6] = {
283 {0.5, 0.0, 0.25, 0.25, 0.25, 0.75}, {one_third, one_third, 0.0, two_thirds, one_third, one_third} };
284 static const float vcross[2][6] = {
285 {two_thirds, 0.0, one_third, one_third, one_third, one_third}, {0.25, 0.25, 0.0, 0.5, 0.25, 0.75} };
286 static const float hsix[2][6] = {
287 {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 Image img;
290 if(!img.load(fname)) {
291 return false;
292 }
294 int xsz = img.get_width();
295 int ysz = img.get_height();
297 if(xsz / 4 == ysz / 3) {
298 // horizontal cross, assume the vertical bit is center-left
299 return load_multi(img, hcross[0], hcross[1], xsz / 4);
300 }
301 if(xsz / 3 == ysz / 4) {
302 // vertical cross, assume the horizontal bit is center-top (180-rotated image 5)
303 return load_multi(img, vcross[0], vcross[1], ysz / 4, (1 << 5));
304 }
305 if(xsz / 3 == ysz / 2) {
306 // horizontal sixpack
307 return load_multi(img, hsix[0], hsix[1], ysz / 2);
308 }
310 error_log("failed to load %s: unknown cubemap configuration\n", fname);
311 return false;
312 }
314 bool TextureCube::save(const char *fname) const
315 {
316 return false; // TODO
317 }
319 bool TextureCube::load_multi(const Image &img, const float *xoffsets, const float *yoffsets, float sz,
320 unsigned int rotmask)
321 {
322 for(int i=0; i<6; i++) {
323 Image face;
325 int xoffs = xoffsets[i] * img.get_width();
326 int yoffs = yoffsets[i] * img.get_height();
328 if(!face.set_pixels(sz, sz, img.get_pixels(), xoffs, yoffs, img.get_width(), img.get_format())) {
329 return false;
330 }
332 if(rotmask & (1 << i)) {
333 face.rotate_180();
334 }
335 set_image(face, i);
336 }
337 return true;
338 }
340 static int glifmt_from_ifmt(unsigned int ifmt)
341 {
342 #ifdef GL_ES_VERSION_2_0
343 switch(ifmt) {
344 case GL_LUMINANCE16F:
345 case GL_LUMINANCE32F:
346 ifmt = GL_LUMINANCE;
347 break;
349 case GL_RGB16F:
350 case GL_RGB32F:
351 ifmt = GL_RGB;
352 break;
354 case GL_RGBA16F:
355 case GL_RGBA32F:
356 ifmt = GL_RGBA;
357 break;
359 default:
360 break;
361 }
362 #endif
363 return ifmt; // by default just pass it through...
364 }
366 static int glfmt_from_ifmt(unsigned int ifmt)
367 {
368 switch(ifmt) {
369 case GL_LUMINANCE16F:
370 case GL_LUMINANCE32F:
371 return GL_LUMINANCE;
373 case GL_RGB16F:
374 case GL_RGB32F:
375 return GL_RGB;
377 case GL_RGBA16F:
378 case GL_RGBA32F:
379 return GL_RGBA;
381 default:
382 break;
383 }
384 return ifmt;
385 }
387 static int gltype_from_ifmt(unsigned int ifmt)
388 {
389 switch(ifmt) {
390 case GL_RGB16F:
391 case GL_RGBA16F:
392 case GL_LUMINANCE16F:
393 #ifdef GL_ES_VERSION_2_0
394 return GL_HALF_FLOAT_OES;
395 #endif
396 case GL_RGB32F:
397 case GL_RGBA32F:
398 case GL_LUMINANCE32F:
399 return GL_FLOAT;
401 default:
402 break;
403 }
404 return GL_UNSIGNED_BYTE;
405 }
407 static int glifmt_from_imgfmt(Image::Format fmt)
408 {
409 switch(fmt) {
410 case Image::FMT_GREY:
411 return GL_LUMINANCE;
412 case Image::FMT_GREY_FLOAT:
413 return GL_LUMINANCE16F;
414 case Image::FMT_RGB:
415 return GL_RGB;
416 case Image::FMT_RGB_FLOAT:
417 return GL_RGB16F;
418 case Image::FMT_RGBA:
419 return GL_RGBA;
420 case Image::FMT_RGBA_FLOAT:
421 return GL_RGBA16F;
422 default:
423 break;
424 }
425 return 0;
426 }
428 // ---- TextureSet ----
429 static void destroy_texture(Texture *tex)
430 {
431 delete tex;
432 }
434 TextureSet::TextureSet()
435 : DataSet<Texture*>(load_texture, destroy_texture)
436 {
437 }