/* render/textures.cc This file is part of the Osirion project and is distributed under the terms of the GNU General Public License version 2 */ #include #include "render/render.h" #include "render/gl.h" #include "render/image.h" #include "render/textures.h" #include "render/state.h" #include "sys/sys.h" #include "core/application.h" #include namespace render { std::map Textures::registry; GLuint Textures::textures[MAXTEXTURES]; GLuint Textures::textures_cubemap_id; math::Vector2f Textures::texture_size[MAXTEXTURES]; std::string textures_cubemapname; void Textures::init() { con_print << "^BLoading textures..." << std::endl; glGenTextures(1, &textures_cubemap_id); if (registry.size()) { clear(); } else { memset(textures, 0, sizeof(textures)); } // "no texture" bitmap load("textures/common/notex"); // gui font if (!load("bitmaps/fonts/gui", false)) { con_error << "Essential file bitmaps/fonts/gui missing" << std::endl; core::application()->shutdown(); } // crosshairs load("bitmaps/pointers/pointer"); load("bitmaps/pointers/aim"); load("bitmaps/pointers/center"); load("bitmaps/pointers/control"); load("bitmaps/pointers/target"); model::Material::set_imageloader_func(Textures::image_loader); } void Textures::shutdown() { model::Material::set_imageloader_func(0); clear(); } void Textures::list() { for (iterator it = registry.begin(); it != registry.end(); it++) { con_print << " " << (*it).first << " " << (*it).second << std::endl; } con_print << registry.size() << " loaded textures" << std::endl; } void Textures::clear() { for (size_t i = 0; i < MAXTEXTURES; i++) { if (textures[i]) { glDeleteTextures(1, &textures[i]); } texture_size[i].clear(); } registry.clear(); memset(textures, 0, sizeof(textures)); // clear cubemap glDeleteTextures(1, &textures_cubemap_id); textures_cubemap_id = 0; textures_cubemapname.clear(); } void Textures::unload(const std::string &name) { iterator it = registry.find(name); if (it != registry.end()) { con_debug << " unloading " << (*it).first << std::endl; size_t id = (*it).second; // do not unload texture id 0, 'unkown texture' if (id && textures[id]) { glDeleteTextures(1, &textures[id]); textures[id] = 0; texture_size[id].clear(); } registry.erase(it); } } void Textures::unload(const size_t id) { // find in map for (iterator it = registry.begin(); it != registry.end(); it++) { if ((*it).second == id) { con_debug << " unloading " << (*it).first << std::endl; size_t id = (*it).second; // do not unload texture id 0, 'unkown texture' if (id && textures[id]) { glDeleteTextures(1, &textures[id]); textures[id] = 0; } registry.erase(it); break; } } } void Textures::load_cubemap(const std::string & name) { if (textures_cubemapname.compare(name) == 0) { // cubemap is already loaded return; } textures_cubemapname.assign(name); const GLenum cube_map[6] = { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z }; Image *cube_texture[6] = { Image::load(name + "_front"), Image::load(name + "_back"), Image::load(name + "_left"), Image::load(name + "_right"), Image::load(name + "_up"), Image::load(name + "_down") }; // load six skybox images and apply the necessary transformations if (cube_texture[0]) { Image *image = cube_texture[0]; if (image->width() != image->height()) { con_warn << " " << name << "_front: non-square sky texture!" << std::endl; } else { unsigned char pixel_data[image->channels()]; for (size_t y = 0; y < image->height(); y++) { for (size_t x = y +1; x < image->width(); x++) { void *src = image->pixel(x, y); void *dst = image->pixel(y, x); memcpy(pixel_data, dst, image->channels()); memcpy(dst, src, image->channels()); memcpy(src, pixel_data, image->channels()); } } } } if (cube_texture[1]) { Image *image = cube_texture[1]; if (image->width() != image->height()) { con_warn << " " << name << "_back: non-square sky texture!" << std::endl; } else { unsigned char pixel_data[image->channels()]; for (size_t y = 0; y < image->height(); y++) { for (size_t x = y +1; x < image->width(); x++) { void *src = image->pixel(image->width() - x - 1, y); void *dst = image->pixel(image->height() - y - 1, x); memcpy(pixel_data, dst, image->channels()); memcpy(dst, src, image->channels()); memcpy(src, pixel_data, image->channels()); } } } } if (cube_texture[2]) { cube_texture[2]->flip_vertical(); } if (cube_texture[3]) { cube_texture[3]->flip_horizontal(); } if (cube_texture[4]) { Image *image = cube_texture[4]; if (image->width() != image->height()) { con_warn << " " << name << "_back: non-square sky texture!" << std::endl; } else { unsigned char pixel_data[image->channels()]; for (size_t y = 0; y < image->height(); y++) { for (size_t x = y +1; x < image->width(); x++) { void *src = image->pixel(x, y); void *dst = image->pixel(y, x); memcpy(pixel_data, dst, image->channels()); memcpy(dst, src, image->channels()); memcpy(src, pixel_data, image->channels()); } } } } if (cube_texture[5]) { Image *image = cube_texture[5]; if (image->width() != image->height()) { con_warn << " " << name << "_down: non-square sky texture!" << std::endl; } else { unsigned char pixel_data[image->channels()]; for (size_t y = 0; y < image->height(); y++) { for (size_t x = y +1; x < image->width(); x++) { void *src = image->pixel(x, y); void *dst = image->pixel(y, x); memcpy(pixel_data, dst, image->channels()); memcpy(dst, src, image->channels()); memcpy(src, pixel_data, image->channels()); } } } } glBindTexture(GL_TEXTURE_CUBE_MAP, textures_cubemap_id); gl::texparameter(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gl::texparameter(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); gl::texparameter(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl::texparameter(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); gl::texparameter(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); gl::texparameter(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 0); for (size_t i = 0; i < 6; i++) { if (cube_texture[i]) { int texture_format; int texture_internalformat; if (cube_texture[i]->channels() == 4) { texture_format = GL_RGBA; texture_internalformat = GL_RGBA8; } else { texture_format = GL_RGB; texture_internalformat = GL_RGB8; } glTexImage2D(cube_map[i], 0, texture_internalformat, cube_texture[i]->width(), cube_texture[i]->height(), 0, texture_format, GL_UNSIGNED_BYTE, cube_texture[i]->ptr()); delete cube_texture[i]; } } } size_t Textures::load(const char *name, const bool filter) { if (name) return load(std::string(name), filter); else return 0; } size_t Textures::load(const std::string &name, const bool filter) { // check if it is already loaded iterator it = registry.find(name); if (it != registry.end()) return (*it).second; // find first available texture size_t id = 0; while ((id < MAXTEXTURES) && (textures[id])) { id++; } if (id == MAXTEXTURES) { con_error << "Texture limit " << MAXTEXTURES << " exceeded!" << std::endl; registry[name] = 0; return 0; } Image *image = Image::load(name); if (!image) { // add to the registry with id 0 (texture not found) registry[name] = 0; return 0; } glGenTextures(1, &textures[id]); glBindTexture(GL_TEXTURE_2D, textures[id]); int texture_format; int texture_internalformat; if (filter) { // scaling functions gl::texparameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); gl::texparameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 4 levels of mipmaps gl::texparameter(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4); if (r_mipmap->value()) { // hardware generated mipmaps (requires OpenGL 1.4) glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); } // enable texture wrapping gl::texparameter(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); gl::texparameter(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } else { // scaling functions gl::texparameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); gl::texparameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // no mipmaps, base level only gl::texparameter(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); gl::texparameter(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl::texparameter(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } //gl::texenv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); if (image->channels() == 4) { texture_format = GL_RGBA; texture_internalformat = GL_RGBA8; } else { texture_format = GL_RGB; texture_internalformat = GL_RGB8; } if (filter && (r_mipmap->value() <= 0)) { gluBuild2DMipmaps(GL_TEXTURE_2D, texture_internalformat, image->width(), image->height(), texture_format, GL_UNSIGNED_BYTE, image->ptr()); } else { glTexImage2D(GL_TEXTURE_2D, 0, texture_internalformat, image->width(), image->height(), 0, texture_format, GL_UNSIGNED_BYTE, image->ptr()); } // add to the registry registry[name] = id; texture_size[id].assign((float) image->width(), (float) image->height()); // delete image data delete image; return id; } size_t Textures::find(const std::string &name) { size_t id = 0; iterator it = registry.find(name); if (it != registry.end()) id = (*it).second; return id; } size_t Textures::bind(const char *name, const bool filter) { if (name) return bind(std::string(name), filter); else return 0; } size_t Textures::bind(const std::string &name, const bool filter) { size_t id = 0; iterator it = registry.find(name); if (it != registry.end()) { id = (*it).second; glBindTexture(GL_TEXTURE_2D, textures[id]); } else { id = load(name, filter); } return id; } size_t Textures::bind(const size_t texture, const bool filter) { size_t id = texture; if (!textures[id]) id = 0; glBindTexture(GL_TEXTURE_2D, textures[id]); return id; } void Textures::image_loader(model::Layer *layer) { if (layer->texture().size() > 0 ) { size_t id = load(layer->texture().c_str()); layer->set_texture_id(id); layer->set_size(texture_size[id]); } } }