goat3dgfx
view src/texture.cc @ 33:253542d715f4
default texture
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sun, 02 Mar 2014 06:32:37 +0200 |
parents | dc5918c62a64 |
children |
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 type_to_target(TextureType type);
18 static TextureType target_to_type(unsigned int targ);
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 };
25 static unsigned int cube_faces[] = {
26 GL_TEXTURE_CUBE_MAP_POSITIVE_X,
27 GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
28 GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
29 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
30 GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
31 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
32 };
35 void goatgfx::bind_texture(Texture *tex, int tunit)
36 {
37 if(tex) {
38 tex->bind(tunit);
39 } else {
40 glActiveTexture(GL_TEXTURE0 + tunit);
41 glBindTexture(cur_target[tunit], 0);
42 glActiveTexture(GL_TEXTURE0);
43 }
44 }
47 Image *Texture::default_img;
49 Texture::Texture()
50 {
51 target = 0;
52 sz[0] = sz[1] = sz[2] = 0;
53 texfmt = 0;
55 img = 0;
56 glGenTextures(1, &id);
57 }
59 Texture::~Texture()
60 {
61 if(id) {
62 glDeleteTextures(1, &id);
63 }
64 if(img) {
65 delete img;
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 glTexParameteri(target, GL_TEXTURE_WRAP_R, wrap);
79 }
81 void Texture::set_filtering(unsigned int filt)
82 {
83 unsigned int mag_filter;
85 if(!target) {
86 return;
87 }
89 switch(filt) {
90 case GL_LINEAR_MIPMAP_NEAREST:
91 case GL_LINEAR_MIPMAP_LINEAR:
92 mag_filter = GL_LINEAR;
93 break;
95 case GL_NEAREST_MIPMAP_NEAREST:
96 case GL_NEAREST_MIPMAP_LINEAR:
97 mag_filter = GL_NEAREST;
98 break;
100 default:
101 mag_filter = filt;
102 }
104 set_filtering(filt, mag_filter);
105 }
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 }
114 unsigned int Texture::get_format() const
115 {
116 return texfmt;
117 }
119 int Texture::get_size(int dim) const
120 {
121 if(dim < 0 || dim >= 3) {
122 return 0;
123 }
124 return sz[dim];
125 }
127 unsigned int Texture::get_id() const
128 {
129 return id;
130 }
132 TextureType Texture::get_type() const
133 {
134 return target_to_type(target);
135 }
137 void Texture::bind(int tex_unit) const
138 {
139 glActiveTexture(GL_TEXTURE0 + tex_unit);
140 glBindTexture(target, id);
141 glActiveTexture(GL_TEXTURE0);
143 cur_target[tex_unit] = target;
144 }
147 void Texture::create(int xsz, int ysz, TextureType textype, unsigned int ifmt)
148 {
149 if(textype == TEX_CUBE && xsz != ysz) {
150 error_log("trying to create cubemap with different width and height (%dx%d)\n", xsz, ysz);
151 return;
152 }
154 int fmt = glfmt_from_ifmt(ifmt);
155 int type = gltype_from_ifmt(ifmt);
157 target = type_to_target(textype);
159 glBindTexture(target, id);
160 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
161 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
163 switch(type) {
164 case TEX_2D:
165 glTexImage2D(GL_TEXTURE_2D, 0, glifmt_from_ifmt(ifmt), xsz, ysz, 0, fmt, type, 0);
166 break;
168 case TEX_CUBE:
169 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
170 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
171 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
172 for(int i=0; i<6; i++) {
173 glTexImage2D(cube_faces[i], 0, ifmt, xsz, ysz, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
174 }
175 break;
176 }
178 CHECKGLERR;
179 sz[0] = xsz;
180 sz[1] = ysz;
181 texfmt = ifmt;
182 }
184 #define DEF_IMAGE_SIZE 64
185 void Texture::create_default(TextureType type)
186 {
187 if(!default_img) {
188 default_img = new Image;
189 default_img->create(DEF_IMAGE_SIZE, DEF_IMAGE_SIZE, Image::FMT_RGBA);
191 unsigned char *pixels = (unsigned char*)default_img->get_pixels();
192 for(int i=0; i<DEF_IMAGE_SIZE; i++) {
193 for(int j=0; j<DEF_IMAGE_SIZE; j++) {
194 bool chess = ((i >> 3) & 1) == ((j >> 3) & 1);
195 pixels[0] = chess ? 255 : 32;
196 pixels[1] = 64;
197 pixels[2] = chess ? 32 : 255;
198 pixels[3] = 255;
199 }
200 }
201 }
203 switch(type) {
204 case TEX_2D:
205 set_image(*default_img);
206 break;
208 case TEX_CUBE:
209 for(int i=0; i<6; i++) {
210 set_image(*default_img, i);
211 }
212 break;
213 }
214 }
216 void Texture::set_image(const Image &img, int idx)
217 {
218 if(idx >= 0 && idx < 6) {
219 set_image_cube(img, idx);
220 } else {
221 if(!set_image_cube(img)) {
222 set_image_2d(img);
223 }
224 }
225 }
227 void Texture::set_image_2d(const Image &img)
228 {
229 texfmt = glifmt_from_imgfmt(img.get_format());
230 unsigned int fmt = glfmt_from_ifmt(texfmt);
231 unsigned int type = gltype_from_ifmt(texfmt);
233 sz[0] = img.get_width();
234 sz[1] = img.get_height();
236 glBindTexture(GL_TEXTURE_2D, id);
237 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
238 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
239 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
240 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
242 #ifdef __GLEW_H__
243 if(GLEW_SGIS_generate_mipmap) {
244 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
245 #endif
246 glTexImage2D(GL_TEXTURE_2D, 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels());
247 #ifdef __GLEW_H__
248 } else {
249 gluBuild2DMipmaps(GL_TEXTURE_2D, texfmt, sz[0], sz[1], fmt, type, img.get_pixels());
250 }
251 #endif
253 #ifdef GL_ES_VERSION_2_0
254 glGenerateMipmap(GL_TEXTURE_2D);
255 #endif
256 }
258 bool Texture::set_image_cube(const Image &img, int idx)
259 {
260 if(idx < 0 || idx >= 6) {
261 return false;
262 }
264 texfmt = glifmt_from_imgfmt(img.get_format());
265 unsigned int fmt = glfmt_from_ifmt(texfmt);
266 unsigned int type = gltype_from_ifmt(texfmt);
268 sz[0] = img.get_width();
269 sz[1] = img.get_height();
271 glBindTexture(GL_TEXTURE_CUBE_MAP, id);
272 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
273 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
274 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
275 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
276 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
278 glTexImage2D(cube_faces[idx], 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels());
279 return true;
280 }
282 bool Texture::set_image_cube(const Image &img)
283 {
284 static const float one_third = 1.0 / 3.0;
285 static const float two_thirds = 2.0 / 3.0;
286 static const float hcross[2][6] = {
287 {0.5, 0.0, 0.25, 0.25, 0.25, 0.75}, {one_third, one_third, 0.0, two_thirds, one_third, one_third} };
288 static const float vcross[2][6] = {
289 {two_thirds, 0.0, one_third, one_third, one_third, one_third}, {0.25, 0.25, 0.0, 0.5, 0.25, 0.75} };
290 static const float hsix[2][6] = {
291 {0.0, 0.0, one_third, one_third, two_thirds, two_thirds}, {0.0, 0.5, 0.0, 0.5, 0.0, 0.5} };
293 int xsz = img.get_width();
294 int ysz = img.get_height();
296 if(xsz / 4 == ysz / 3) {
297 // horizontal cross, assume the vertical bit is center-left
298 return set_cube_multi(img, hcross[0], hcross[1], xsz / 4);
299 }
300 if(xsz / 3 == ysz / 4) {
301 // vertical cross, assume the horizontal bit is center-top (180-rotated image 5)
302 return set_cube_multi(img, vcross[0], vcross[1], ysz / 4, (1 << 5));
303 }
304 if(xsz / 3 == ysz / 2) {
305 // horizontal sixpack
306 return set_cube_multi(img, hsix[0], hsix[1], ysz / 2);
307 }
309 return false;
310 }
313 bool Texture::load(const char *fname)
314 {
315 Image img;
316 if(!img.load(fname)) {
317 error_log("failed to load 2D texture: %s\n", fname);
318 return false;
319 }
320 set_image(img);
322 info_log("loaded 2D texture: %s\n", fname);
323 return true;
324 }
326 bool Texture::load_cube(const char *fname)
327 {
328 Image img;
329 if(!img.load(fname)) {
330 return false;
331 }
332 return set_image_cube(img);
333 }
335 bool Texture::set_cube_multi(const Image &img, const float *xoffsets, const float *yoffsets, float sz,
336 unsigned int rotmask)
337 {
338 for(int i=0; i<6; i++) {
339 Image face;
341 int xoffs = xoffsets[i] * img.get_width();
342 int yoffs = yoffsets[i] * img.get_height();
344 if(!face.set_pixels(sz, sz, img.get_pixels(), xoffs, yoffs, img.get_width(), img.get_format())) {
345 return false;
346 }
348 if(rotmask & (1 << i)) {
349 face.rotate_180();
350 }
351 set_image_cube(face, i);
352 }
353 return true;
354 }
356 static int glifmt_from_ifmt(unsigned int ifmt)
357 {
358 #ifdef GL_ES_VERSION_2_0
359 switch(ifmt) {
360 case GL_LUMINANCE16F:
361 case GL_LUMINANCE32F:
362 ifmt = GL_LUMINANCE;
363 break;
365 case GL_RGB16F:
366 case GL_RGB32F:
367 ifmt = GL_RGB;
368 break;
370 case GL_RGBA16F:
371 case GL_RGBA32F:
372 ifmt = GL_RGBA;
373 break;
375 default:
376 break;
377 }
378 #endif
379 return ifmt; // by default just pass it through...
380 }
382 static int glfmt_from_ifmt(unsigned int ifmt)
383 {
384 switch(ifmt) {
385 case GL_LUMINANCE16F:
386 case GL_LUMINANCE32F:
387 return GL_LUMINANCE;
389 case GL_RGB16F:
390 case GL_RGB32F:
391 return GL_RGB;
393 case GL_RGBA16F:
394 case GL_RGBA32F:
395 return GL_RGBA;
397 default:
398 break;
399 }
400 return ifmt;
401 }
403 static int gltype_from_ifmt(unsigned int ifmt)
404 {
405 switch(ifmt) {
406 case GL_RGB16F:
407 case GL_RGBA16F:
408 case GL_LUMINANCE16F:
409 #ifdef GL_ES_VERSION_2_0
410 return GL_HALF_FLOAT_OES;
411 #endif
412 case GL_RGB32F:
413 case GL_RGBA32F:
414 case GL_LUMINANCE32F:
415 return GL_FLOAT;
417 default:
418 break;
419 }
420 return GL_UNSIGNED_BYTE;
421 }
423 static int glifmt_from_imgfmt(Image::Format fmt)
424 {
425 switch(fmt) {
426 case Image::FMT_GREY:
427 return GL_LUMINANCE;
428 case Image::FMT_GREY_FLOAT:
429 return GL_LUMINANCE16F;
430 case Image::FMT_RGB:
431 return GL_RGB;
432 case Image::FMT_RGB_FLOAT:
433 return GL_RGB16F;
434 case Image::FMT_RGBA:
435 return GL_RGBA;
436 case Image::FMT_RGBA_FLOAT:
437 return GL_RGBA16F;
438 default:
439 break;
440 }
441 return 0;
442 }
444 static unsigned int type_to_target(TextureType type)
445 {
446 return type == TEX_CUBE ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
447 }
449 static TextureType target_to_type(unsigned int targ)
450 {
451 return targ == GL_TEXTURE_CUBE_MAP ? TEX_CUBE : TEX_2D;
452 }
454 // ---- TextureSet ----
455 TextureSet::TextureSet()
456 : DataSet<Texture*>(create_tex, load_tex, done_tex, free_tex)
457 {
458 }
460 Texture *TextureSet::get_texture(const char *name, TextureType type) const
461 {
462 typename std::map<std::string, Texture*>::const_iterator iter = data.find(name);
463 if(iter != data.end()) {
464 return iter->second;
465 }
467 const char *fname, *slash;
468 if((slash = strrchr(name, '/'))) {
469 fname = slash + 1;
470 } else {
471 fname = name;
472 }
474 std::string path = datafile_path(fname);
475 if(path.empty()) {
476 fprintf(stderr, "can't find data file: %s\n", name);
477 return 0;
478 }
480 Texture *res = create();
481 res->create_default(type);
482 resman_lookup(rman, path.c_str(), res);
483 return res;
484 }
486 // static callbacks
488 Texture *TextureSet::create_tex()
489 {
490 return new Texture;
491 }
493 bool TextureSet::load_tex(Texture *tex, const char *fname)
494 {
495 Image *img = new Image;
496 if(!img->load(fname)) {
497 delete img;
498 return false;
499 }
501 delete tex->img;
502 tex->img = img;
504 return true;
505 }
507 bool TextureSet::done_tex(Texture *tex)
508 {
509 if(!tex->img) {
510 return false;
511 }
513 tex->set_image(*tex->img);
514 return true;
515 }
517 void TextureSet::free_tex(Texture *tex)
518 {
519 delete tex;
520 }