vrshoot
diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/texture.cc Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,437 @@ 1.4 +#include <math.h> 1.5 +#include "texture.h" 1.6 +#include "image.h" 1.7 +#include "opengl.h" 1.8 +#include "imago2.h" 1.9 +#include "logger.h" 1.10 +#include "datapath.h" 1.11 + 1.12 +static int glifmt_from_ifmt(unsigned int ifmt); 1.13 +static int glfmt_from_ifmt(unsigned int ifmt); 1.14 +static int gltype_from_ifmt(unsigned int ifmt); 1.15 + 1.16 +static int glifmt_from_imgfmt(Image::Format fmt); 1.17 + 1.18 +static unsigned int cur_target[8] = { 1.19 + GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, 1.20 + GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D 1.21 +}; 1.22 + 1.23 +void set_texture(Texture *tex, int tunit) 1.24 +{ 1.25 + if(tex) { 1.26 + tex->bind(tunit); 1.27 + } else { 1.28 + glActiveTexture(GL_TEXTURE0 + tunit); 1.29 + glBindTexture(cur_target[tunit], 0); 1.30 + glActiveTexture(GL_TEXTURE0); 1.31 + } 1.32 +} 1.33 + 1.34 +Texture *load_texture(const char *fname) 1.35 +{ 1.36 + TextureCube *texcube = new TextureCube; 1.37 + if(texcube->load(fname)) { 1.38 + return texcube; 1.39 + } 1.40 + delete texcube; 1.41 + 1.42 + Texture2D *tex = new Texture2D; 1.43 + if(tex->load(fname)) { 1.44 + return tex; 1.45 + } 1.46 + delete tex; 1.47 + return 0; 1.48 +} 1.49 + 1.50 +Texture::Texture() 1.51 +{ 1.52 + target = 0; 1.53 + sz[0] = sz[1] = sz[2] = 0; 1.54 + texfmt = 0; 1.55 + 1.56 + glGenTextures(1, &id); 1.57 +} 1.58 + 1.59 +Texture::~Texture() 1.60 +{ 1.61 + if(id) { 1.62 + glDeleteTextures(1, &id); 1.63 + } 1.64 +} 1.65 + 1.66 +void Texture::set_wrapping(unsigned int wrap) 1.67 +{ 1.68 + if(!target) { 1.69 + return; 1.70 + } 1.71 + 1.72 + glBindTexture(target, id); 1.73 + glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap); 1.74 + glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap); 1.75 +} 1.76 + 1.77 +void Texture::set_filtering(unsigned int filt) 1.78 +{ 1.79 + unsigned int mag_filter; 1.80 + 1.81 + if(!target) { 1.82 + return; 1.83 + } 1.84 + 1.85 + switch(filt) { 1.86 + case GL_LINEAR_MIPMAP_NEAREST: 1.87 + case GL_LINEAR_MIPMAP_LINEAR: 1.88 + mag_filter = GL_LINEAR; 1.89 + break; 1.90 + 1.91 + case GL_NEAREST_MIPMAP_NEAREST: 1.92 + case GL_NEAREST_MIPMAP_LINEAR: 1.93 + mag_filter = GL_NEAREST; 1.94 + break; 1.95 + 1.96 + default: 1.97 + mag_filter = filt; 1.98 + } 1.99 + 1.100 + set_filtering(filt, mag_filter); 1.101 +} 1.102 + 1.103 +void Texture::set_filtering(unsigned int min_filt, unsigned int mag_filt) 1.104 +{ 1.105 + glBindTexture(target, id); 1.106 + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filt); 1.107 + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, mag_filt); 1.108 +} 1.109 + 1.110 +unsigned int Texture::get_format() const 1.111 +{ 1.112 + return texfmt; 1.113 +} 1.114 + 1.115 +int Texture::get_size(int dim) const 1.116 +{ 1.117 + if(dim < 0 || dim >= 3) { 1.118 + return 0; 1.119 + } 1.120 + return sz[dim]; 1.121 +} 1.122 + 1.123 +unsigned int Texture::get_id() const 1.124 +{ 1.125 + return id; 1.126 +} 1.127 + 1.128 +void Texture::bind(int tex_unit) const 1.129 +{ 1.130 + glActiveTexture(GL_TEXTURE0 + tex_unit); 1.131 + glBindTexture(target, id); 1.132 + glActiveTexture(GL_TEXTURE0); 1.133 + 1.134 + cur_target[tex_unit] = target; 1.135 +} 1.136 + 1.137 + 1.138 +// ---- Texture2D ---- 1.139 + 1.140 +Texture2D::Texture2D() 1.141 +{ 1.142 + target = GL_TEXTURE_2D; 1.143 +} 1.144 + 1.145 +void Texture2D::create(int xsz, int ysz, unsigned int ifmt) 1.146 +{ 1.147 + int fmt = glfmt_from_ifmt(ifmt); 1.148 + int type = gltype_from_ifmt(ifmt); 1.149 + 1.150 + glBindTexture(GL_TEXTURE_2D, id); 1.151 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 1.152 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 1.153 + glTexImage2D(GL_TEXTURE_2D, 0, glifmt_from_ifmt(ifmt), xsz, ysz, 0, fmt, type, 0); 1.154 + CHECKGLERR; 1.155 + sz[0] = xsz; 1.156 + sz[1] = ysz; 1.157 + texfmt = ifmt; 1.158 +} 1.159 + 1.160 +void Texture2D::set_image(const Image &img, int idx) 1.161 +{ 1.162 + texfmt = glifmt_from_imgfmt(img.get_format()); 1.163 + unsigned int fmt = glfmt_from_ifmt(texfmt); 1.164 + unsigned int type = gltype_from_ifmt(texfmt); 1.165 + 1.166 + sz[0] = img.get_width(); 1.167 + sz[1] = img.get_height(); 1.168 + 1.169 + glBindTexture(GL_TEXTURE_2D, id); 1.170 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 1.171 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 1.172 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 1.173 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 1.174 + 1.175 +#ifdef __GLEW_H__ 1.176 + if(GLEW_SGIS_generate_mipmap) { 1.177 + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); 1.178 +#endif 1.179 + glTexImage2D(GL_TEXTURE_2D, 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels()); 1.180 +#ifdef __GLEW_H__ 1.181 + } else { 1.182 + gluBuild2DMipmaps(GL_TEXTURE_2D, texfmt, sz[0], sz[1], fmt, type, img.get_pixels()); 1.183 + } 1.184 +#endif 1.185 + 1.186 +#ifdef GL_ES_VERSION_2_0 1.187 + glGenerateMipmap(GL_TEXTURE_2D); 1.188 +#endif 1.189 +} 1.190 + 1.191 +bool Texture2D::load(const char *fname) 1.192 +{ 1.193 + Image img; 1.194 + if(!img.load(fname)) { 1.195 + error_log("failed to load 2D texture: %s\n", fname); 1.196 + return false; 1.197 + } 1.198 + set_image(img); 1.199 + 1.200 + info_log("loaded 2D texture: %s\n", fname); 1.201 + return true; 1.202 +} 1.203 + 1.204 +bool Texture2D::save(const char *fname) const 1.205 +{ 1.206 +#ifndef GL_ES_VERSION_2_0 1.207 + unsigned char *pixels = new unsigned char[sz[0] * sz[1] * 4]; 1.208 + 1.209 + glBindTexture(GL_TEXTURE_2D, id); 1.210 + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 1.211 + 1.212 + if(img_save_pixels(fname, pixels, sz[0], sz[1]) == -1) { 1.213 + error_log("failed to save 2D texture: %s\n", fname); 1.214 + delete [] pixels; 1.215 + return false; 1.216 + } 1.217 + 1.218 + info_log("saved 2D texture: %s\n", fname); 1.219 + delete [] pixels; 1.220 + return true; 1.221 +#else 1.222 + return false; // TODO 1.223 +#endif 1.224 +} 1.225 + 1.226 +// ---- TextureCube ---- 1.227 +static unsigned int cube_faces[] = { 1.228 + GL_TEXTURE_CUBE_MAP_POSITIVE_X, 1.229 + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 1.230 + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 1.231 + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 1.232 + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 1.233 + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 1.234 +}; 1.235 + 1.236 +TextureCube::TextureCube() 1.237 +{ 1.238 + target = GL_TEXTURE_CUBE_MAP; 1.239 +} 1.240 + 1.241 +void TextureCube::create(int xsz, int ysz, unsigned int ifmt) 1.242 +{ 1.243 + if(xsz != ysz) { 1.244 + error_log("trying to create cubemap with different width and height (%dx%d)\n", xsz, ysz); 1.245 + return; 1.246 + } 1.247 + 1.248 + texfmt = ifmt; 1.249 + 1.250 + glBindTexture(GL_TEXTURE_CUBE_MAP, id); 1.251 + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 1.252 + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 1.253 + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 1.254 + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 1.255 + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); 1.256 + 1.257 + for(int i=0; i<6; i++) { 1.258 + glTexImage2D(cube_faces[i], 0, ifmt, xsz, ysz, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); 1.259 + } 1.260 +} 1.261 + 1.262 +void TextureCube::set_image(const Image &img, int idx) 1.263 +{ 1.264 + texfmt = glifmt_from_imgfmt(img.get_format()); 1.265 + unsigned int fmt = glfmt_from_ifmt(texfmt); 1.266 + unsigned int type = gltype_from_ifmt(texfmt); 1.267 + 1.268 + sz[0] = img.get_width(); 1.269 + sz[1] = img.get_height(); 1.270 + 1.271 + glBindTexture(GL_TEXTURE_CUBE_MAP, id); 1.272 + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 1.273 + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 1.274 + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 1.275 + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 1.276 + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); 1.277 + 1.278 + glTexImage2D(cube_faces[idx], 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels()); 1.279 +} 1.280 + 1.281 +bool TextureCube::load(const char *fname) 1.282 +{ 1.283 + static const float one_third = 1.0 / 3.0; 1.284 + static const float two_thirds = 2.0 / 3.0; 1.285 + static const float hcross[2][6] = { 1.286 + {0.5, 0.0, 0.25, 0.25, 0.25, 0.75}, {one_third, one_third, 0.0, two_thirds, one_third, one_third} }; 1.287 + static const float vcross[2][6] = { 1.288 + {two_thirds, 0.0, one_third, one_third, one_third, one_third}, {0.25, 0.25, 0.0, 0.5, 0.25, 0.75} }; 1.289 + static const float hsix[2][6] = { 1.290 + {0.0, 0.0, one_third, one_third, two_thirds, two_thirds}, {0.0, 0.5, 0.0, 0.5, 0.0, 0.5} }; 1.291 + 1.292 + Image img; 1.293 + if(!img.load(fname)) { 1.294 + return false; 1.295 + } 1.296 + 1.297 + int xsz = img.get_width(); 1.298 + int ysz = img.get_height(); 1.299 + 1.300 + if(xsz / 4 == ysz / 3) { 1.301 + // horizontal cross, assume the vertical bit is center-left 1.302 + return load_multi(img, hcross[0], hcross[1], xsz / 4); 1.303 + } 1.304 + if(xsz / 3 == ysz / 4) { 1.305 + // vertical cross, assume the horizontal bit is center-top (180-rotated image 5) 1.306 + return load_multi(img, vcross[0], vcross[1], ysz / 4, (1 << 5)); 1.307 + } 1.308 + if(xsz / 3 == ysz / 2) { 1.309 + // horizontal sixpack 1.310 + return load_multi(img, hsix[0], hsix[1], ysz / 2); 1.311 + } 1.312 + 1.313 + error_log("failed to load %s: unknown cubemap configuration\n", fname); 1.314 + return false; 1.315 +} 1.316 + 1.317 +bool TextureCube::save(const char *fname) const 1.318 +{ 1.319 + return false; // TODO 1.320 +} 1.321 + 1.322 +bool TextureCube::load_multi(const Image &img, const float *xoffsets, const float *yoffsets, float sz, 1.323 + unsigned int rotmask) 1.324 +{ 1.325 + for(int i=0; i<6; i++) { 1.326 + Image face; 1.327 + 1.328 + int xoffs = xoffsets[i] * img.get_width(); 1.329 + int yoffs = yoffsets[i] * img.get_height(); 1.330 + 1.331 + if(!face.set_pixels(sz, sz, img.get_pixels(), xoffs, yoffs, img.get_width(), img.get_format())) { 1.332 + return false; 1.333 + } 1.334 + 1.335 + if(rotmask & (1 << i)) { 1.336 + face.rotate_180(); 1.337 + } 1.338 + set_image(face, i); 1.339 + } 1.340 + return true; 1.341 +} 1.342 + 1.343 +static int glifmt_from_ifmt(unsigned int ifmt) 1.344 +{ 1.345 +#ifdef GL_ES_VERSION_2_0 1.346 + switch(ifmt) { 1.347 + case GL_LUMINANCE16F: 1.348 + case GL_LUMINANCE32F: 1.349 + ifmt = GL_LUMINANCE; 1.350 + break; 1.351 + 1.352 + case GL_RGB16F: 1.353 + case GL_RGB32F: 1.354 + ifmt = GL_RGB; 1.355 + break; 1.356 + 1.357 + case GL_RGBA16F: 1.358 + case GL_RGBA32F: 1.359 + ifmt = GL_RGBA; 1.360 + break; 1.361 + 1.362 + default: 1.363 + break; 1.364 + } 1.365 +#endif 1.366 + return ifmt; // by default just pass it through... 1.367 +} 1.368 + 1.369 +static int glfmt_from_ifmt(unsigned int ifmt) 1.370 +{ 1.371 + switch(ifmt) { 1.372 + case GL_LUMINANCE16F: 1.373 + case GL_LUMINANCE32F: 1.374 + return GL_LUMINANCE; 1.375 + 1.376 + case GL_RGB16F: 1.377 + case GL_RGB32F: 1.378 + return GL_RGB; 1.379 + 1.380 + case GL_RGBA16F: 1.381 + case GL_RGBA32F: 1.382 + return GL_RGBA; 1.383 + 1.384 + default: 1.385 + break; 1.386 + } 1.387 + return ifmt; 1.388 +} 1.389 + 1.390 +static int gltype_from_ifmt(unsigned int ifmt) 1.391 +{ 1.392 + switch(ifmt) { 1.393 + case GL_RGB16F: 1.394 + case GL_RGBA16F: 1.395 + case GL_LUMINANCE16F: 1.396 +#ifdef GL_ES_VERSION_2_0 1.397 + return GL_HALF_FLOAT_OES; 1.398 +#endif 1.399 + case GL_RGB32F: 1.400 + case GL_RGBA32F: 1.401 + case GL_LUMINANCE32F: 1.402 + return GL_FLOAT; 1.403 + 1.404 + default: 1.405 + break; 1.406 + } 1.407 + return GL_UNSIGNED_BYTE; 1.408 +} 1.409 + 1.410 +static int glifmt_from_imgfmt(Image::Format fmt) 1.411 +{ 1.412 + switch(fmt) { 1.413 + case Image::FMT_GREY: 1.414 + return GL_LUMINANCE; 1.415 + case Image::FMT_GREY_FLOAT: 1.416 + return GL_LUMINANCE16F; 1.417 + case Image::FMT_RGB: 1.418 + return GL_RGB; 1.419 + case Image::FMT_RGB_FLOAT: 1.420 + return GL_RGB16F; 1.421 + case Image::FMT_RGBA: 1.422 + return GL_RGBA; 1.423 + case Image::FMT_RGBA_FLOAT: 1.424 + return GL_RGBA16F; 1.425 + default: 1.426 + break; 1.427 + } 1.428 + return 0; 1.429 +} 1.430 + 1.431 +// ---- TextureSet ---- 1.432 +static void destroy_texture(Texture *tex) 1.433 +{ 1.434 + delete tex; 1.435 +} 1.436 + 1.437 +TextureSet::TextureSet() 1.438 + : DataSet<Texture*>(load_texture, destroy_texture) 1.439 +{ 1.440 +}