goat3dgfx

annotate src/texture.cc @ 15:7d6b667821cf

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