goat3dgfx

annotate src/texture.cc @ 29:9d581abd0bfb

merged
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 02 Mar 2014 02:18:15 +0200
parents 7d6b667821cf
children 253542d715f4
rev   line source
nuclear@9 1 #include <math.h>
nuclear@0 2 #include "texture.h"
nuclear@0 3 #include "image.h"
nuclear@0 4 #include "opengl.h"
nuclear@0 5 #include "imago2.h"
nuclear@0 6 #include "logger.h"
nuclear@0 7 #include "datapath.h"
nuclear@0 8
nuclear@15 9 using namespace goatgfx;
nuclear@15 10
nuclear@0 11 static int glifmt_from_ifmt(unsigned int ifmt);
nuclear@0 12 static int glfmt_from_ifmt(unsigned int ifmt);
nuclear@0 13 static int gltype_from_ifmt(unsigned int ifmt);
nuclear@0 14
nuclear@0 15 static int glifmt_from_imgfmt(Image::Format fmt);
nuclear@0 16
nuclear@24 17 static unsigned int type_to_target(TextureType type);
nuclear@24 18 static TextureType target_to_type(unsigned int targ);
nuclear@24 19
nuclear@0 20 static unsigned int cur_target[8] = {
nuclear@0 21 GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D,
nuclear@0 22 GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D
nuclear@0 23 };
nuclear@0 24
nuclear@24 25 static unsigned int cube_faces[] = {
nuclear@24 26 GL_TEXTURE_CUBE_MAP_POSITIVE_X,
nuclear@24 27 GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
nuclear@24 28 GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
nuclear@24 29 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
nuclear@24 30 GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
nuclear@24 31 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
nuclear@24 32 };
nuclear@15 33
nuclear@24 34
nuclear@24 35 void goatgfx::bind_texture(Texture *tex, int tunit)
nuclear@0 36 {
nuclear@0 37 if(tex) {
nuclear@0 38 tex->bind(tunit);
nuclear@0 39 } else {
nuclear@0 40 glActiveTexture(GL_TEXTURE0 + tunit);
nuclear@0 41 glBindTexture(cur_target[tunit], 0);
nuclear@0 42 glActiveTexture(GL_TEXTURE0);
nuclear@0 43 }
nuclear@0 44 }
nuclear@0 45
nuclear@0 46 Texture::Texture()
nuclear@0 47 {
nuclear@0 48 target = 0;
nuclear@0 49 sz[0] = sz[1] = sz[2] = 0;
nuclear@0 50 texfmt = 0;
nuclear@0 51
nuclear@24 52 img = 0;
nuclear@0 53 glGenTextures(1, &id);
nuclear@0 54 }
nuclear@0 55
nuclear@0 56 Texture::~Texture()
nuclear@0 57 {
nuclear@0 58 if(id) {
nuclear@0 59 glDeleteTextures(1, &id);
nuclear@0 60 }
nuclear@24 61 if(img) {
nuclear@24 62 delete img;
nuclear@24 63 }
nuclear@0 64 }
nuclear@0 65
nuclear@0 66 void Texture::set_wrapping(unsigned int wrap)
nuclear@0 67 {
nuclear@0 68 if(!target) {
nuclear@0 69 return;
nuclear@0 70 }
nuclear@0 71
nuclear@0 72 glBindTexture(target, id);
nuclear@0 73 glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap);
nuclear@0 74 glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap);
nuclear@24 75 glTexParameteri(target, GL_TEXTURE_WRAP_R, wrap);
nuclear@0 76 }
nuclear@0 77
nuclear@0 78 void Texture::set_filtering(unsigned int filt)
nuclear@0 79 {
nuclear@0 80 unsigned int mag_filter;
nuclear@0 81
nuclear@0 82 if(!target) {
nuclear@0 83 return;
nuclear@0 84 }
nuclear@0 85
nuclear@0 86 switch(filt) {
nuclear@0 87 case GL_LINEAR_MIPMAP_NEAREST:
nuclear@0 88 case GL_LINEAR_MIPMAP_LINEAR:
nuclear@0 89 mag_filter = GL_LINEAR;
nuclear@0 90 break;
nuclear@0 91
nuclear@0 92 case GL_NEAREST_MIPMAP_NEAREST:
nuclear@0 93 case GL_NEAREST_MIPMAP_LINEAR:
nuclear@0 94 mag_filter = GL_NEAREST;
nuclear@0 95 break;
nuclear@0 96
nuclear@0 97 default:
nuclear@0 98 mag_filter = filt;
nuclear@0 99 }
nuclear@0 100
nuclear@0 101 set_filtering(filt, mag_filter);
nuclear@0 102 }
nuclear@0 103
nuclear@0 104 void Texture::set_filtering(unsigned int min_filt, unsigned int mag_filt)
nuclear@0 105 {
nuclear@0 106 glBindTexture(target, id);
nuclear@0 107 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filt);
nuclear@0 108 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, mag_filt);
nuclear@0 109 }
nuclear@0 110
nuclear@0 111 unsigned int Texture::get_format() const
nuclear@0 112 {
nuclear@0 113 return texfmt;
nuclear@0 114 }
nuclear@0 115
nuclear@0 116 int Texture::get_size(int dim) const
nuclear@0 117 {
nuclear@0 118 if(dim < 0 || dim >= 3) {
nuclear@0 119 return 0;
nuclear@0 120 }
nuclear@0 121 return sz[dim];
nuclear@0 122 }
nuclear@0 123
nuclear@0 124 unsigned int Texture::get_id() const
nuclear@0 125 {
nuclear@0 126 return id;
nuclear@0 127 }
nuclear@0 128
nuclear@24 129 TextureType Texture::get_type() const
nuclear@24 130 {
nuclear@24 131 return target_to_type(target);
nuclear@24 132 }
nuclear@24 133
nuclear@0 134 void Texture::bind(int tex_unit) const
nuclear@0 135 {
nuclear@0 136 glActiveTexture(GL_TEXTURE0 + tex_unit);
nuclear@0 137 glBindTexture(target, id);
nuclear@0 138 glActiveTexture(GL_TEXTURE0);
nuclear@0 139
nuclear@0 140 cur_target[tex_unit] = target;
nuclear@0 141 }
nuclear@0 142
nuclear@0 143
nuclear@24 144 void Texture::create(int xsz, int ysz, TextureType textype, unsigned int ifmt)
nuclear@24 145 {
nuclear@24 146 if(textype == TEX_CUBE && xsz != ysz) {
nuclear@24 147 error_log("trying to create cubemap with different width and height (%dx%d)\n", xsz, ysz);
nuclear@24 148 return;
nuclear@24 149 }
nuclear@0 150
nuclear@0 151 int fmt = glfmt_from_ifmt(ifmt);
nuclear@0 152 int type = gltype_from_ifmt(ifmt);
nuclear@0 153
nuclear@24 154 target = type_to_target(textype);
nuclear@24 155
nuclear@24 156 glBindTexture(target, id);
nuclear@24 157 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
nuclear@24 158 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
nuclear@24 159
nuclear@24 160 switch(type) {
nuclear@24 161 case TEX_2D:
nuclear@24 162 glTexImage2D(GL_TEXTURE_2D, 0, glifmt_from_ifmt(ifmt), xsz, ysz, 0, fmt, type, 0);
nuclear@24 163 break;
nuclear@24 164
nuclear@24 165 case TEX_CUBE:
nuclear@24 166 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
nuclear@24 167 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
nuclear@24 168 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
nuclear@24 169 for(int i=0; i<6; i++) {
nuclear@24 170 glTexImage2D(cube_faces[i], 0, ifmt, xsz, ysz, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
nuclear@24 171 }
nuclear@24 172 break;
nuclear@24 173 }
nuclear@24 174
nuclear@0 175 CHECKGLERR;
nuclear@0 176 sz[0] = xsz;
nuclear@0 177 sz[1] = ysz;
nuclear@0 178 texfmt = ifmt;
nuclear@0 179 }
nuclear@0 180
nuclear@24 181 void Texture::set_image(const Image &img, int idx)
nuclear@24 182 {
nuclear@24 183 if(idx >= 0 && idx < 6) {
nuclear@24 184 set_image_cube(img, idx);
nuclear@24 185 } else {
nuclear@24 186 if(!set_image_cube(img)) {
nuclear@24 187 set_image_2d(img);
nuclear@24 188 }
nuclear@24 189 }
nuclear@24 190 }
nuclear@24 191
nuclear@24 192 void Texture::set_image_2d(const Image &img)
nuclear@0 193 {
nuclear@0 194 texfmt = glifmt_from_imgfmt(img.get_format());
nuclear@0 195 unsigned int fmt = glfmt_from_ifmt(texfmt);
nuclear@0 196 unsigned int type = gltype_from_ifmt(texfmt);
nuclear@0 197
nuclear@0 198 sz[0] = img.get_width();
nuclear@0 199 sz[1] = img.get_height();
nuclear@0 200
nuclear@0 201 glBindTexture(GL_TEXTURE_2D, id);
nuclear@0 202 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
nuclear@0 203 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
nuclear@0 204 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
nuclear@0 205 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
nuclear@0 206
nuclear@0 207 #ifdef __GLEW_H__
nuclear@0 208 if(GLEW_SGIS_generate_mipmap) {
nuclear@0 209 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
nuclear@0 210 #endif
nuclear@0 211 glTexImage2D(GL_TEXTURE_2D, 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels());
nuclear@0 212 #ifdef __GLEW_H__
nuclear@0 213 } else {
nuclear@0 214 gluBuild2DMipmaps(GL_TEXTURE_2D, texfmt, sz[0], sz[1], fmt, type, img.get_pixels());
nuclear@0 215 }
nuclear@0 216 #endif
nuclear@0 217
nuclear@0 218 #ifdef GL_ES_VERSION_2_0
nuclear@0 219 glGenerateMipmap(GL_TEXTURE_2D);
nuclear@0 220 #endif
nuclear@0 221 }
nuclear@0 222
nuclear@24 223 bool Texture::set_image_cube(const Image &img, int idx)
nuclear@0 224 {
nuclear@24 225 if(idx < 0 || idx >= 6) {
nuclear@0 226 return false;
nuclear@0 227 }
nuclear@0 228
nuclear@6 229 texfmt = glifmt_from_imgfmt(img.get_format());
nuclear@6 230 unsigned int fmt = glfmt_from_ifmt(texfmt);
nuclear@6 231 unsigned int type = gltype_from_ifmt(texfmt);
nuclear@6 232
nuclear@6 233 sz[0] = img.get_width();
nuclear@6 234 sz[1] = img.get_height();
nuclear@6 235
nuclear@6 236 glBindTexture(GL_TEXTURE_CUBE_MAP, id);
nuclear@6 237 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
nuclear@6 238 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
nuclear@6 239 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
nuclear@6 240 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
nuclear@9 241 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
nuclear@6 242
nuclear@6 243 glTexImage2D(cube_faces[idx], 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels());
nuclear@24 244 return true;
nuclear@0 245 }
nuclear@0 246
nuclear@24 247 bool Texture::set_image_cube(const Image &img)
nuclear@0 248 {
nuclear@6 249 static const float one_third = 1.0 / 3.0;
nuclear@6 250 static const float two_thirds = 2.0 / 3.0;
nuclear@6 251 static const float hcross[2][6] = {
nuclear@6 252 {0.5, 0.0, 0.25, 0.25, 0.25, 0.75}, {one_third, one_third, 0.0, two_thirds, one_third, one_third} };
nuclear@6 253 static const float vcross[2][6] = {
nuclear@6 254 {two_thirds, 0.0, one_third, one_third, one_third, one_third}, {0.25, 0.25, 0.0, 0.5, 0.25, 0.75} };
nuclear@6 255 static const float hsix[2][6] = {
nuclear@6 256 {0.0, 0.0, one_third, one_third, two_thirds, two_thirds}, {0.0, 0.5, 0.0, 0.5, 0.0, 0.5} };
nuclear@6 257
nuclear@6 258 int xsz = img.get_width();
nuclear@6 259 int ysz = img.get_height();
nuclear@6 260
nuclear@6 261 if(xsz / 4 == ysz / 3) {
nuclear@6 262 // horizontal cross, assume the vertical bit is center-left
nuclear@24 263 return set_cube_multi(img, hcross[0], hcross[1], xsz / 4);
nuclear@6 264 }
nuclear@6 265 if(xsz / 3 == ysz / 4) {
nuclear@6 266 // vertical cross, assume the horizontal bit is center-top (180-rotated image 5)
nuclear@24 267 return set_cube_multi(img, vcross[0], vcross[1], ysz / 4, (1 << 5));
nuclear@6 268 }
nuclear@6 269 if(xsz / 3 == ysz / 2) {
nuclear@6 270 // horizontal sixpack
nuclear@24 271 return set_cube_multi(img, hsix[0], hsix[1], ysz / 2);
nuclear@6 272 }
nuclear@6 273
nuclear@6 274 return false;
nuclear@0 275 }
nuclear@0 276
nuclear@24 277
nuclear@24 278 bool Texture::load(const char *fname)
nuclear@0 279 {
nuclear@24 280 Image img;
nuclear@24 281 if(!img.load(fname)) {
nuclear@24 282 error_log("failed to load 2D texture: %s\n", fname);
nuclear@24 283 return false;
nuclear@24 284 }
nuclear@24 285 set_image(img);
nuclear@24 286
nuclear@24 287 info_log("loaded 2D texture: %s\n", fname);
nuclear@24 288 return true;
nuclear@0 289 }
nuclear@0 290
nuclear@24 291 bool Texture::load_cube(const char *fname)
nuclear@24 292 {
nuclear@24 293 Image img;
nuclear@24 294 if(!img.load(fname)) {
nuclear@24 295 return false;
nuclear@24 296 }
nuclear@24 297 return set_image_cube(img);
nuclear@24 298 }
nuclear@24 299
nuclear@24 300 bool Texture::set_cube_multi(const Image &img, const float *xoffsets, const float *yoffsets, float sz,
nuclear@6 301 unsigned int rotmask)
nuclear@6 302 {
nuclear@6 303 for(int i=0; i<6; i++) {
nuclear@6 304 Image face;
nuclear@6 305
nuclear@6 306 int xoffs = xoffsets[i] * img.get_width();
nuclear@6 307 int yoffs = yoffsets[i] * img.get_height();
nuclear@6 308
nuclear@6 309 if(!face.set_pixels(sz, sz, img.get_pixels(), xoffs, yoffs, img.get_width(), img.get_format())) {
nuclear@6 310 return false;
nuclear@6 311 }
nuclear@6 312
nuclear@6 313 if(rotmask & (1 << i)) {
nuclear@6 314 face.rotate_180();
nuclear@6 315 }
nuclear@24 316 set_image_cube(face, i);
nuclear@6 317 }
nuclear@6 318 return true;
nuclear@6 319 }
nuclear@6 320
nuclear@0 321 static int glifmt_from_ifmt(unsigned int ifmt)
nuclear@0 322 {
nuclear@0 323 #ifdef GL_ES_VERSION_2_0
nuclear@0 324 switch(ifmt) {
nuclear@0 325 case GL_LUMINANCE16F:
nuclear@0 326 case GL_LUMINANCE32F:
nuclear@0 327 ifmt = GL_LUMINANCE;
nuclear@0 328 break;
nuclear@0 329
nuclear@0 330 case GL_RGB16F:
nuclear@0 331 case GL_RGB32F:
nuclear@0 332 ifmt = GL_RGB;
nuclear@0 333 break;
nuclear@0 334
nuclear@0 335 case GL_RGBA16F:
nuclear@0 336 case GL_RGBA32F:
nuclear@0 337 ifmt = GL_RGBA;
nuclear@0 338 break;
nuclear@0 339
nuclear@0 340 default:
nuclear@0 341 break;
nuclear@0 342 }
nuclear@0 343 #endif
nuclear@0 344 return ifmt; // by default just pass it through...
nuclear@0 345 }
nuclear@0 346
nuclear@0 347 static int glfmt_from_ifmt(unsigned int ifmt)
nuclear@0 348 {
nuclear@0 349 switch(ifmt) {
nuclear@0 350 case GL_LUMINANCE16F:
nuclear@0 351 case GL_LUMINANCE32F:
nuclear@0 352 return GL_LUMINANCE;
nuclear@0 353
nuclear@0 354 case GL_RGB16F:
nuclear@0 355 case GL_RGB32F:
nuclear@0 356 return GL_RGB;
nuclear@0 357
nuclear@0 358 case GL_RGBA16F:
nuclear@0 359 case GL_RGBA32F:
nuclear@0 360 return GL_RGBA;
nuclear@0 361
nuclear@0 362 default:
nuclear@0 363 break;
nuclear@0 364 }
nuclear@0 365 return ifmt;
nuclear@0 366 }
nuclear@0 367
nuclear@0 368 static int gltype_from_ifmt(unsigned int ifmt)
nuclear@0 369 {
nuclear@0 370 switch(ifmt) {
nuclear@0 371 case GL_RGB16F:
nuclear@0 372 case GL_RGBA16F:
nuclear@0 373 case GL_LUMINANCE16F:
nuclear@0 374 #ifdef GL_ES_VERSION_2_0
nuclear@0 375 return GL_HALF_FLOAT_OES;
nuclear@0 376 #endif
nuclear@0 377 case GL_RGB32F:
nuclear@0 378 case GL_RGBA32F:
nuclear@0 379 case GL_LUMINANCE32F:
nuclear@0 380 return GL_FLOAT;
nuclear@0 381
nuclear@0 382 default:
nuclear@0 383 break;
nuclear@0 384 }
nuclear@0 385 return GL_UNSIGNED_BYTE;
nuclear@0 386 }
nuclear@0 387
nuclear@0 388 static int glifmt_from_imgfmt(Image::Format fmt)
nuclear@0 389 {
nuclear@0 390 switch(fmt) {
nuclear@0 391 case Image::FMT_GREY:
nuclear@0 392 return GL_LUMINANCE;
nuclear@0 393 case Image::FMT_GREY_FLOAT:
nuclear@0 394 return GL_LUMINANCE16F;
nuclear@0 395 case Image::FMT_RGB:
nuclear@0 396 return GL_RGB;
nuclear@0 397 case Image::FMT_RGB_FLOAT:
nuclear@0 398 return GL_RGB16F;
nuclear@0 399 case Image::FMT_RGBA:
nuclear@0 400 return GL_RGBA;
nuclear@0 401 case Image::FMT_RGBA_FLOAT:
nuclear@0 402 return GL_RGBA16F;
nuclear@0 403 default:
nuclear@0 404 break;
nuclear@0 405 }
nuclear@0 406 return 0;
nuclear@0 407 }
nuclear@0 408
nuclear@24 409 static unsigned int type_to_target(TextureType type)
nuclear@24 410 {
nuclear@24 411 return type == TEX_CUBE ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
nuclear@24 412 }
nuclear@24 413
nuclear@24 414 static TextureType target_to_type(unsigned int targ)
nuclear@24 415 {
nuclear@24 416 return targ == GL_TEXTURE_CUBE_MAP ? TEX_CUBE : TEX_2D;
nuclear@24 417 }
nuclear@24 418
nuclear@0 419 // ---- TextureSet ----
nuclear@24 420 TextureSet::TextureSet()
nuclear@24 421 : DataSet<Texture*>(create_tex, load_tex, done_tex, free_tex)
nuclear@24 422 {
nuclear@24 423 }
nuclear@24 424
nuclear@24 425 // static callbacks
nuclear@24 426
nuclear@24 427 Texture *TextureSet::create_tex()
nuclear@24 428 {
nuclear@24 429 return new Texture;
nuclear@24 430 }
nuclear@24 431
nuclear@24 432 bool TextureSet::load_tex(Texture *tex, const char *fname)
nuclear@24 433 {
nuclear@24 434 Image *img = new Image;
nuclear@24 435 if(!img->load(fname)) {
nuclear@24 436 delete img;
nuclear@24 437 return false;
nuclear@24 438 }
nuclear@24 439
nuclear@24 440 delete tex->img;
nuclear@24 441 tex->img = img;
nuclear@24 442
nuclear@24 443 return true;
nuclear@24 444 }
nuclear@24 445
nuclear@24 446 bool TextureSet::done_tex(Texture *tex)
nuclear@24 447 {
nuclear@24 448 if(!tex->img) {
nuclear@24 449 return false;
nuclear@24 450 }
nuclear@24 451
nuclear@24 452 tex->set_image(*tex->img);
nuclear@24 453 return true;
nuclear@24 454 }
nuclear@24 455
nuclear@24 456 void TextureSet::free_tex(Texture *tex)
nuclear@0 457 {
nuclear@0 458 delete tex;
nuclear@0 459 }