goat3dgfx

view src/texture.cc @ 22:92bfb0206969

- made all XFormNode functions virtual - added XFormNode::get_parent()
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 28 Dec 2013 06:48:23 +0200
parents 25b911c7c35c
children dc5918c62a64
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 using namespace goatgfx;
11 static int glifmt_from_ifmt(unsigned int ifmt);
12 static int glfmt_from_ifmt(unsigned int ifmt);
13 static int gltype_from_ifmt(unsigned int ifmt);
15 static int glifmt_from_imgfmt(Image::Format fmt);
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 };
22 namespace goatgfx {
24 void set_texture(Texture *tex, int tunit)
25 {
26 if(tex) {
27 tex->bind(tunit);
28 } else {
29 glActiveTexture(GL_TEXTURE0 + tunit);
30 glBindTexture(cur_target[tunit], 0);
31 glActiveTexture(GL_TEXTURE0);
32 }
33 }
35 Texture *load_texture(const char *fname)
36 {
37 TextureCube *texcube = new TextureCube;
38 if(texcube->load(fname)) {
39 return texcube;
40 }
41 delete texcube;
43 Texture2D *tex = new Texture2D;
44 if(tex->load(fname)) {
45 return tex;
46 }
47 delete tex;
48 return 0;
49 }
51 } // namespace goatgfx
53 Texture::Texture()
54 {
55 target = 0;
56 sz[0] = sz[1] = sz[2] = 0;
57 texfmt = 0;
59 glGenTextures(1, &id);
60 }
62 Texture::~Texture()
63 {
64 if(id) {
65 glDeleteTextures(1, &id);
66 }
67 }
69 void Texture::set_wrapping(unsigned int wrap)
70 {
71 if(!target) {
72 return;
73 }
75 glBindTexture(target, id);
76 glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap);
77 glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap);
78 }
80 void Texture::set_filtering(unsigned int filt)
81 {
82 unsigned int mag_filter;
84 if(!target) {
85 return;
86 }
88 switch(filt) {
89 case GL_LINEAR_MIPMAP_NEAREST:
90 case GL_LINEAR_MIPMAP_LINEAR:
91 mag_filter = GL_LINEAR;
92 break;
94 case GL_NEAREST_MIPMAP_NEAREST:
95 case GL_NEAREST_MIPMAP_LINEAR:
96 mag_filter = GL_NEAREST;
97 break;
99 default:
100 mag_filter = filt;
101 }
103 set_filtering(filt, mag_filter);
104 }
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 }
113 unsigned int Texture::get_format() const
114 {
115 return texfmt;
116 }
118 int Texture::get_size(int dim) const
119 {
120 if(dim < 0 || dim >= 3) {
121 return 0;
122 }
123 return sz[dim];
124 }
126 unsigned int Texture::get_id() const
127 {
128 return id;
129 }
131 void Texture::bind(int tex_unit) const
132 {
133 glActiveTexture(GL_TEXTURE0 + tex_unit);
134 glBindTexture(target, id);
135 glActiveTexture(GL_TEXTURE0);
137 cur_target[tex_unit] = target;
138 }
141 // ---- Texture2D ----
143 Texture2D::Texture2D()
144 {
145 target = GL_TEXTURE_2D;
146 }
148 void Texture2D::create(int xsz, int ysz, unsigned int ifmt)
149 {
150 int fmt = glfmt_from_ifmt(ifmt);
151 int type = gltype_from_ifmt(ifmt);
153 glBindTexture(GL_TEXTURE_2D, id);
154 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
155 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
156 glTexImage2D(GL_TEXTURE_2D, 0, glifmt_from_ifmt(ifmt), xsz, ysz, 0, fmt, type, 0);
157 CHECKGLERR;
158 sz[0] = xsz;
159 sz[1] = ysz;
160 texfmt = ifmt;
161 }
163 void Texture2D::set_image(const Image &img, int idx)
164 {
165 texfmt = glifmt_from_imgfmt(img.get_format());
166 unsigned int fmt = glfmt_from_ifmt(texfmt);
167 unsigned int type = gltype_from_ifmt(texfmt);
169 sz[0] = img.get_width();
170 sz[1] = img.get_height();
172 glBindTexture(GL_TEXTURE_2D, id);
173 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
174 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
175 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
176 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
178 #ifdef __GLEW_H__
179 if(GLEW_SGIS_generate_mipmap) {
180 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
181 #endif
182 glTexImage2D(GL_TEXTURE_2D, 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels());
183 #ifdef __GLEW_H__
184 } else {
185 gluBuild2DMipmaps(GL_TEXTURE_2D, texfmt, sz[0], sz[1], fmt, type, img.get_pixels());
186 }
187 #endif
189 #ifdef GL_ES_VERSION_2_0
190 glGenerateMipmap(GL_TEXTURE_2D);
191 #endif
192 }
194 bool Texture2D::load(const char *fname)
195 {
196 Image img;
197 if(!img.load(fname)) {
198 error_log("failed to load 2D texture: %s\n", fname);
199 return false;
200 }
201 set_image(img);
203 info_log("loaded 2D texture: %s\n", fname);
204 return true;
205 }
207 bool Texture2D::save(const char *fname) const
208 {
209 #ifndef GL_ES_VERSION_2_0
210 unsigned char *pixels = new unsigned char[sz[0] * sz[1] * 4];
212 glBindTexture(GL_TEXTURE_2D, id);
213 glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
215 if(img_save_pixels(fname, pixels, sz[0], sz[1]) == -1) {
216 error_log("failed to save 2D texture: %s\n", fname);
217 delete [] pixels;
218 return false;
219 }
221 info_log("saved 2D texture: %s\n", fname);
222 delete [] pixels;
223 return true;
224 #else
225 return false; // TODO
226 #endif
227 }
229 // ---- TextureCube ----
230 static unsigned int cube_faces[] = {
231 GL_TEXTURE_CUBE_MAP_POSITIVE_X,
232 GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
233 GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
234 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
235 GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
236 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
237 };
239 TextureCube::TextureCube()
240 {
241 target = GL_TEXTURE_CUBE_MAP;
242 }
244 void TextureCube::create(int xsz, int ysz, unsigned int ifmt)
245 {
246 if(xsz != ysz) {
247 error_log("trying to create cubemap with different width and height (%dx%d)\n", xsz, ysz);
248 return;
249 }
251 texfmt = ifmt;
253 glBindTexture(GL_TEXTURE_CUBE_MAP, id);
254 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
255 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
256 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
257 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
258 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
260 for(int i=0; i<6; i++) {
261 glTexImage2D(cube_faces[i], 0, ifmt, xsz, ysz, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
262 }
263 }
265 void TextureCube::set_image(const Image &img, int idx)
266 {
267 texfmt = glifmt_from_imgfmt(img.get_format());
268 unsigned int fmt = glfmt_from_ifmt(texfmt);
269 unsigned int type = gltype_from_ifmt(texfmt);
271 sz[0] = img.get_width();
272 sz[1] = img.get_height();
274 glBindTexture(GL_TEXTURE_CUBE_MAP, id);
275 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
276 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
277 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
278 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
279 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
281 glTexImage2D(cube_faces[idx], 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels());
282 }
284 bool TextureCube::load(const char *fname)
285 {
286 static const float one_third = 1.0 / 3.0;
287 static const float two_thirds = 2.0 / 3.0;
288 static const float hcross[2][6] = {
289 {0.5, 0.0, 0.25, 0.25, 0.25, 0.75}, {one_third, one_third, 0.0, two_thirds, one_third, one_third} };
290 static const float vcross[2][6] = {
291 {two_thirds, 0.0, one_third, one_third, one_third, one_third}, {0.25, 0.25, 0.0, 0.5, 0.25, 0.75} };
292 static const float hsix[2][6] = {
293 {0.0, 0.0, one_third, one_third, two_thirds, two_thirds}, {0.0, 0.5, 0.0, 0.5, 0.0, 0.5} };
295 Image img;
296 if(!img.load(fname)) {
297 return false;
298 }
300 int xsz = img.get_width();
301 int ysz = img.get_height();
303 if(xsz / 4 == ysz / 3) {
304 // horizontal cross, assume the vertical bit is center-left
305 return load_multi(img, hcross[0], hcross[1], xsz / 4);
306 }
307 if(xsz / 3 == ysz / 4) {
308 // vertical cross, assume the horizontal bit is center-top (180-rotated image 5)
309 return load_multi(img, vcross[0], vcross[1], ysz / 4, (1 << 5));
310 }
311 if(xsz / 3 == ysz / 2) {
312 // horizontal sixpack
313 return load_multi(img, hsix[0], hsix[1], ysz / 2);
314 }
316 error_log("failed to load %s: unknown cubemap configuration\n", fname);
317 return false;
318 }
320 bool TextureCube::save(const char *fname) const
321 {
322 return false; // TODO
323 }
325 bool TextureCube::load_multi(const Image &img, const float *xoffsets, const float *yoffsets, float sz,
326 unsigned int rotmask)
327 {
328 for(int i=0; i<6; i++) {
329 Image face;
331 int xoffs = xoffsets[i] * img.get_width();
332 int yoffs = yoffsets[i] * img.get_height();
334 if(!face.set_pixels(sz, sz, img.get_pixels(), xoffs, yoffs, img.get_width(), img.get_format())) {
335 return false;
336 }
338 if(rotmask & (1 << i)) {
339 face.rotate_180();
340 }
341 set_image(face, i);
342 }
343 return true;
344 }
346 static int glifmt_from_ifmt(unsigned int ifmt)
347 {
348 #ifdef GL_ES_VERSION_2_0
349 switch(ifmt) {
350 case GL_LUMINANCE16F:
351 case GL_LUMINANCE32F:
352 ifmt = GL_LUMINANCE;
353 break;
355 case GL_RGB16F:
356 case GL_RGB32F:
357 ifmt = GL_RGB;
358 break;
360 case GL_RGBA16F:
361 case GL_RGBA32F:
362 ifmt = GL_RGBA;
363 break;
365 default:
366 break;
367 }
368 #endif
369 return ifmt; // by default just pass it through...
370 }
372 static int glfmt_from_ifmt(unsigned int ifmt)
373 {
374 switch(ifmt) {
375 case GL_LUMINANCE16F:
376 case GL_LUMINANCE32F:
377 return GL_LUMINANCE;
379 case GL_RGB16F:
380 case GL_RGB32F:
381 return GL_RGB;
383 case GL_RGBA16F:
384 case GL_RGBA32F:
385 return GL_RGBA;
387 default:
388 break;
389 }
390 return ifmt;
391 }
393 static int gltype_from_ifmt(unsigned int ifmt)
394 {
395 switch(ifmt) {
396 case GL_RGB16F:
397 case GL_RGBA16F:
398 case GL_LUMINANCE16F:
399 #ifdef GL_ES_VERSION_2_0
400 return GL_HALF_FLOAT_OES;
401 #endif
402 case GL_RGB32F:
403 case GL_RGBA32F:
404 case GL_LUMINANCE32F:
405 return GL_FLOAT;
407 default:
408 break;
409 }
410 return GL_UNSIGNED_BYTE;
411 }
413 static int glifmt_from_imgfmt(Image::Format fmt)
414 {
415 switch(fmt) {
416 case Image::FMT_GREY:
417 return GL_LUMINANCE;
418 case Image::FMT_GREY_FLOAT:
419 return GL_LUMINANCE16F;
420 case Image::FMT_RGB:
421 return GL_RGB;
422 case Image::FMT_RGB_FLOAT:
423 return GL_RGB16F;
424 case Image::FMT_RGBA:
425 return GL_RGBA;
426 case Image::FMT_RGBA_FLOAT:
427 return GL_RGBA16F;
428 default:
429 break;
430 }
431 return 0;
432 }
434 // ---- TextureSet ----
435 static void destroy_texture(Texture *tex)
436 {
437 delete tex;
438 }
440 TextureSet::TextureSet()
441 : DataSet<Texture*>(load_texture, destroy_texture)
442 {
443 }