nuclear@9: #include nuclear@0: #include "texture.h" nuclear@0: #include "image.h" nuclear@0: #include "opengl.h" nuclear@0: #include "imago2.h" nuclear@0: #include "logger.h" nuclear@0: #include "datapath.h" nuclear@0: nuclear@15: using namespace goatgfx; nuclear@15: nuclear@0: static int glifmt_from_ifmt(unsigned int ifmt); nuclear@0: static int glfmt_from_ifmt(unsigned int ifmt); nuclear@0: static int gltype_from_ifmt(unsigned int ifmt); nuclear@0: nuclear@0: static int glifmt_from_imgfmt(Image::Format fmt); nuclear@0: nuclear@0: static unsigned int cur_target[8] = { nuclear@0: GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, nuclear@0: GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D, GL_TEXTURE_2D nuclear@0: }; nuclear@0: nuclear@15: namespace goatgfx { nuclear@15: nuclear@0: void set_texture(Texture *tex, int tunit) nuclear@0: { nuclear@0: if(tex) { nuclear@0: tex->bind(tunit); nuclear@0: } else { nuclear@0: glActiveTexture(GL_TEXTURE0 + tunit); nuclear@0: glBindTexture(cur_target[tunit], 0); nuclear@0: glActiveTexture(GL_TEXTURE0); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: Texture *load_texture(const char *fname) nuclear@0: { nuclear@0: TextureCube *texcube = new TextureCube; nuclear@0: if(texcube->load(fname)) { nuclear@0: return texcube; nuclear@0: } nuclear@0: delete texcube; nuclear@0: nuclear@0: Texture2D *tex = new Texture2D; nuclear@0: if(tex->load(fname)) { nuclear@0: return tex; nuclear@0: } nuclear@0: delete tex; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@15: } // namespace goatgfx nuclear@0: nuclear@0: Texture::Texture() nuclear@0: { nuclear@0: target = 0; nuclear@0: sz[0] = sz[1] = sz[2] = 0; nuclear@0: texfmt = 0; nuclear@0: nuclear@0: glGenTextures(1, &id); nuclear@0: } nuclear@0: nuclear@0: Texture::~Texture() nuclear@0: { nuclear@0: if(id) { nuclear@0: glDeleteTextures(1, &id); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void Texture::set_wrapping(unsigned int wrap) nuclear@0: { nuclear@0: if(!target) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: glBindTexture(target, id); nuclear@0: glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap); nuclear@0: glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap); nuclear@0: } nuclear@0: nuclear@0: void Texture::set_filtering(unsigned int filt) nuclear@0: { nuclear@0: unsigned int mag_filter; nuclear@0: nuclear@0: if(!target) { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: switch(filt) { nuclear@0: case GL_LINEAR_MIPMAP_NEAREST: nuclear@0: case GL_LINEAR_MIPMAP_LINEAR: nuclear@0: mag_filter = GL_LINEAR; nuclear@0: break; nuclear@0: nuclear@0: case GL_NEAREST_MIPMAP_NEAREST: nuclear@0: case GL_NEAREST_MIPMAP_LINEAR: nuclear@0: mag_filter = GL_NEAREST; nuclear@0: break; nuclear@0: nuclear@0: default: nuclear@0: mag_filter = filt; nuclear@0: } nuclear@0: nuclear@0: set_filtering(filt, mag_filter); nuclear@0: } nuclear@0: nuclear@0: void Texture::set_filtering(unsigned int min_filt, unsigned int mag_filt) nuclear@0: { nuclear@0: glBindTexture(target, id); nuclear@0: glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filt); nuclear@0: glTexParameteri(target, GL_TEXTURE_MAG_FILTER, mag_filt); nuclear@0: } nuclear@0: nuclear@0: unsigned int Texture::get_format() const nuclear@0: { nuclear@0: return texfmt; nuclear@0: } nuclear@0: nuclear@0: int Texture::get_size(int dim) const nuclear@0: { nuclear@0: if(dim < 0 || dim >= 3) { nuclear@0: return 0; nuclear@0: } nuclear@0: return sz[dim]; nuclear@0: } nuclear@0: nuclear@0: unsigned int Texture::get_id() const nuclear@0: { nuclear@0: return id; nuclear@0: } nuclear@0: nuclear@0: void Texture::bind(int tex_unit) const nuclear@0: { nuclear@0: glActiveTexture(GL_TEXTURE0 + tex_unit); nuclear@0: glBindTexture(target, id); nuclear@0: glActiveTexture(GL_TEXTURE0); nuclear@0: nuclear@0: cur_target[tex_unit] = target; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ---- Texture2D ---- nuclear@0: nuclear@0: Texture2D::Texture2D() nuclear@0: { nuclear@0: target = GL_TEXTURE_2D; nuclear@0: } nuclear@0: nuclear@0: void Texture2D::create(int xsz, int ysz, unsigned int ifmt) nuclear@0: { nuclear@0: int fmt = glfmt_from_ifmt(ifmt); nuclear@0: int type = gltype_from_ifmt(ifmt); nuclear@0: nuclear@0: glBindTexture(GL_TEXTURE_2D, id); nuclear@0: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); nuclear@0: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); nuclear@0: glTexImage2D(GL_TEXTURE_2D, 0, glifmt_from_ifmt(ifmt), xsz, ysz, 0, fmt, type, 0); nuclear@0: CHECKGLERR; nuclear@0: sz[0] = xsz; nuclear@0: sz[1] = ysz; nuclear@0: texfmt = ifmt; nuclear@0: } nuclear@0: nuclear@0: void Texture2D::set_image(const Image &img, int idx) nuclear@0: { nuclear@0: texfmt = glifmt_from_imgfmt(img.get_format()); nuclear@0: unsigned int fmt = glfmt_from_ifmt(texfmt); nuclear@0: unsigned int type = gltype_from_ifmt(texfmt); nuclear@0: nuclear@0: sz[0] = img.get_width(); nuclear@0: sz[1] = img.get_height(); nuclear@0: nuclear@0: glBindTexture(GL_TEXTURE_2D, id); nuclear@0: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); nuclear@0: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); nuclear@0: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); nuclear@0: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); nuclear@0: nuclear@0: #ifdef __GLEW_H__ nuclear@0: if(GLEW_SGIS_generate_mipmap) { nuclear@0: glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); nuclear@0: #endif nuclear@0: glTexImage2D(GL_TEXTURE_2D, 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels()); nuclear@0: #ifdef __GLEW_H__ nuclear@0: } else { nuclear@0: gluBuild2DMipmaps(GL_TEXTURE_2D, texfmt, sz[0], sz[1], fmt, type, img.get_pixels()); nuclear@0: } nuclear@0: #endif nuclear@0: nuclear@0: #ifdef GL_ES_VERSION_2_0 nuclear@0: glGenerateMipmap(GL_TEXTURE_2D); nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: bool Texture2D::load(const char *fname) nuclear@0: { nuclear@0: Image img; nuclear@2: if(!img.load(fname)) { nuclear@0: error_log("failed to load 2D texture: %s\n", fname); nuclear@0: return false; nuclear@0: } nuclear@0: set_image(img); nuclear@0: nuclear@0: info_log("loaded 2D texture: %s\n", fname); nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: bool Texture2D::save(const char *fname) const nuclear@0: { nuclear@0: #ifndef GL_ES_VERSION_2_0 nuclear@0: unsigned char *pixels = new unsigned char[sz[0] * sz[1] * 4]; nuclear@0: nuclear@0: glBindTexture(GL_TEXTURE_2D, id); nuclear@0: glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); nuclear@0: nuclear@0: if(img_save_pixels(fname, pixels, sz[0], sz[1]) == -1) { nuclear@0: error_log("failed to save 2D texture: %s\n", fname); nuclear@0: delete [] pixels; nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: info_log("saved 2D texture: %s\n", fname); nuclear@0: delete [] pixels; nuclear@0: return true; nuclear@0: #else nuclear@0: return false; // TODO nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: // ---- TextureCube ---- nuclear@0: static unsigned int cube_faces[] = { nuclear@0: GL_TEXTURE_CUBE_MAP_POSITIVE_X, nuclear@0: GL_TEXTURE_CUBE_MAP_NEGATIVE_X, nuclear@0: GL_TEXTURE_CUBE_MAP_POSITIVE_Y, nuclear@0: GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, nuclear@0: GL_TEXTURE_CUBE_MAP_POSITIVE_Z, nuclear@0: GL_TEXTURE_CUBE_MAP_NEGATIVE_Z nuclear@0: }; nuclear@0: nuclear@0: TextureCube::TextureCube() nuclear@0: { nuclear@0: target = GL_TEXTURE_CUBE_MAP; nuclear@0: } nuclear@0: nuclear@0: void TextureCube::create(int xsz, int ysz, unsigned int ifmt) nuclear@0: { nuclear@0: if(xsz != ysz) { nuclear@0: error_log("trying to create cubemap with different width and height (%dx%d)\n", xsz, ysz); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: texfmt = ifmt; nuclear@0: nuclear@0: glBindTexture(GL_TEXTURE_CUBE_MAP, id); nuclear@6: glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); nuclear@6: glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); nuclear@6: glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); nuclear@6: glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); nuclear@9: glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); nuclear@0: nuclear@0: for(int i=0; i<6; i++) { nuclear@0: glTexImage2D(cube_faces[i], 0, ifmt, xsz, ysz, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void TextureCube::set_image(const Image &img, int idx) nuclear@0: { nuclear@6: texfmt = glifmt_from_imgfmt(img.get_format()); nuclear@6: unsigned int fmt = glfmt_from_ifmt(texfmt); nuclear@6: unsigned int type = gltype_from_ifmt(texfmt); nuclear@6: nuclear@6: sz[0] = img.get_width(); nuclear@6: sz[1] = img.get_height(); nuclear@6: nuclear@6: glBindTexture(GL_TEXTURE_CUBE_MAP, id); nuclear@6: glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); nuclear@6: glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); nuclear@6: glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); nuclear@6: glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); nuclear@9: glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); nuclear@6: nuclear@6: glTexImage2D(cube_faces[idx], 0, texfmt, sz[0], sz[1], 0, fmt, type, img.get_pixels()); nuclear@0: } nuclear@0: nuclear@0: bool TextureCube::load(const char *fname) nuclear@0: { nuclear@6: static const float one_third = 1.0 / 3.0; nuclear@6: static const float two_thirds = 2.0 / 3.0; nuclear@6: static const float hcross[2][6] = { nuclear@6: {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: static const float vcross[2][6] = { nuclear@6: {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: static const float hsix[2][6] = { nuclear@6: {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: nuclear@6: Image img; nuclear@6: if(!img.load(fname)) { nuclear@6: return false; nuclear@6: } nuclear@6: nuclear@6: int xsz = img.get_width(); nuclear@6: int ysz = img.get_height(); nuclear@6: nuclear@6: if(xsz / 4 == ysz / 3) { nuclear@6: // horizontal cross, assume the vertical bit is center-left nuclear@6: return load_multi(img, hcross[0], hcross[1], xsz / 4); nuclear@6: } nuclear@6: if(xsz / 3 == ysz / 4) { nuclear@6: // vertical cross, assume the horizontal bit is center-top (180-rotated image 5) nuclear@6: return load_multi(img, vcross[0], vcross[1], ysz / 4, (1 << 5)); nuclear@6: } nuclear@6: if(xsz / 3 == ysz / 2) { nuclear@6: // horizontal sixpack nuclear@6: return load_multi(img, hsix[0], hsix[1], ysz / 2); nuclear@6: } nuclear@6: nuclear@6: error_log("failed to load %s: unknown cubemap configuration\n", fname); nuclear@6: return false; nuclear@0: } nuclear@0: nuclear@0: bool TextureCube::save(const char *fname) const nuclear@0: { nuclear@0: return false; // TODO nuclear@0: } nuclear@0: nuclear@6: bool TextureCube::load_multi(const Image &img, const float *xoffsets, const float *yoffsets, float sz, nuclear@6: unsigned int rotmask) nuclear@6: { nuclear@6: for(int i=0; i<6; i++) { nuclear@6: Image face; nuclear@6: nuclear@6: int xoffs = xoffsets[i] * img.get_width(); nuclear@6: int yoffs = yoffsets[i] * img.get_height(); nuclear@6: nuclear@6: if(!face.set_pixels(sz, sz, img.get_pixels(), xoffs, yoffs, img.get_width(), img.get_format())) { nuclear@6: return false; nuclear@6: } nuclear@6: nuclear@6: if(rotmask & (1 << i)) { nuclear@6: face.rotate_180(); nuclear@6: } nuclear@6: set_image(face, i); nuclear@6: } nuclear@6: return true; nuclear@6: } nuclear@6: nuclear@0: static int glifmt_from_ifmt(unsigned int ifmt) nuclear@0: { nuclear@0: #ifdef GL_ES_VERSION_2_0 nuclear@0: switch(ifmt) { nuclear@0: case GL_LUMINANCE16F: nuclear@0: case GL_LUMINANCE32F: nuclear@0: ifmt = GL_LUMINANCE; nuclear@0: break; nuclear@0: nuclear@0: case GL_RGB16F: nuclear@0: case GL_RGB32F: nuclear@0: ifmt = GL_RGB; nuclear@0: break; nuclear@0: nuclear@0: case GL_RGBA16F: nuclear@0: case GL_RGBA32F: nuclear@0: ifmt = GL_RGBA; nuclear@0: break; nuclear@0: nuclear@0: default: nuclear@0: break; nuclear@0: } nuclear@0: #endif nuclear@0: return ifmt; // by default just pass it through... nuclear@0: } nuclear@0: nuclear@0: static int glfmt_from_ifmt(unsigned int ifmt) nuclear@0: { nuclear@0: switch(ifmt) { nuclear@0: case GL_LUMINANCE16F: nuclear@0: case GL_LUMINANCE32F: nuclear@0: return GL_LUMINANCE; nuclear@0: nuclear@0: case GL_RGB16F: nuclear@0: case GL_RGB32F: nuclear@0: return GL_RGB; nuclear@0: nuclear@0: case GL_RGBA16F: nuclear@0: case GL_RGBA32F: nuclear@0: return GL_RGBA; nuclear@0: nuclear@0: default: nuclear@0: break; nuclear@0: } nuclear@0: return ifmt; nuclear@0: } nuclear@0: nuclear@0: static int gltype_from_ifmt(unsigned int ifmt) nuclear@0: { nuclear@0: switch(ifmt) { nuclear@0: case GL_RGB16F: nuclear@0: case GL_RGBA16F: nuclear@0: case GL_LUMINANCE16F: nuclear@0: #ifdef GL_ES_VERSION_2_0 nuclear@0: return GL_HALF_FLOAT_OES; nuclear@0: #endif nuclear@0: case GL_RGB32F: nuclear@0: case GL_RGBA32F: nuclear@0: case GL_LUMINANCE32F: nuclear@0: return GL_FLOAT; nuclear@0: nuclear@0: default: nuclear@0: break; nuclear@0: } nuclear@0: return GL_UNSIGNED_BYTE; nuclear@0: } nuclear@0: nuclear@0: static int glifmt_from_imgfmt(Image::Format fmt) nuclear@0: { nuclear@0: switch(fmt) { nuclear@0: case Image::FMT_GREY: nuclear@0: return GL_LUMINANCE; nuclear@0: case Image::FMT_GREY_FLOAT: nuclear@0: return GL_LUMINANCE16F; nuclear@0: case Image::FMT_RGB: nuclear@0: return GL_RGB; nuclear@0: case Image::FMT_RGB_FLOAT: nuclear@0: return GL_RGB16F; nuclear@0: case Image::FMT_RGBA: nuclear@0: return GL_RGBA; nuclear@0: case Image::FMT_RGBA_FLOAT: nuclear@0: return GL_RGBA16F; nuclear@0: default: nuclear@0: break; nuclear@0: } nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: // ---- TextureSet ---- nuclear@0: static void destroy_texture(Texture *tex) nuclear@0: { nuclear@0: delete tex; nuclear@0: } nuclear@0: nuclear@0: TextureSet::TextureSet() nuclear@0: : DataSet(load_texture, destroy_texture) nuclear@0: { nuclear@0: }