X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2Ftexture.cc;h=6d3e912925b8f1376018b9cc612fde8db1d4677a;hp=2f8f97a1c366f3befe9f8838f721ed3c608c5e9f;hb=7d15b919681bb9ec0088b4b27c6abf62d6dfb9b1;hpb=0fffd0097d7b496454413e57b398c903ecc252e4 diff --git a/src/texture.cc b/src/texture.cc index 2f8f97a..6d3e912 100644 --- a/src/texture.cc +++ b/src/texture.cc @@ -33,6 +33,8 @@ #include #include +#include "mippleton.hh" + #include "dispatcher.hh" #include "opengl.hh" @@ -42,43 +44,47 @@ namespace dc { -class texture_impl +/** + * The texture implementation just contains all the information about the image + * which is worth having in memory. The image data itself is not worth keeping + * in memory if the texture has been loaded to GL, but the name of the resource + * is retained so that it can be reloaded if necessary. The implementation is a + * mippleton so that multiple texture objects can share the same internal + * objects and avoid having duplicate textures loaded to GL. + */ + +class texture::texture_impl : public mippleton { + + /** + * Delete the texture (if it is loaded) from GL. + */ + void unloadFromGL() { - if (object) + if (object_) { - glDeleteTextures(1, &object); - object = 0; + glDeleteTextures(1, &object_); + object_ = 0; } } + /** + * If the GL context was recreated, we probably need to reload the texture. + * This may involve reading it from disk again, but hopefully the OS was + * smart enough to cache it if the client has plenty of RAM. + */ + void contextRecreated(const notification& note) { unloadFromGL(); uploadToGL(); } -public: - texture_impl(texture* outside, bool keepInMemory) - : interface(outside), keepData(keepInMemory), object(0), imageData(0) - { - dispatcher::instance().addHandler("video.context_recreated", - boost::bind(&texture_impl::contextRecreated, this, _1), - this); - } - - ~texture_impl() - { - dispatcher::instance().removeHandler(this); - - if (imageData) - { - SDL_FreeSurface(imageData); - } - unloadFromGL(); - } - + /** + * This is a helper method used by some of the texture loading code. It + * returns the first power of two which is greater than the input value. + */ static int powerOfTwo(int input) { @@ -91,17 +97,55 @@ public: return value; } +public: + + /** + * Construction is initialization. + */ + + explicit texture_impl(const std::string& name) : + mippleton(name), + width_(0), + height_(0), + mode_(0), + minFilter_(GL_NEAREST), + maxFilter_(GL_NEAREST), + wrapU_(GL_CLAMP), + wrapV_(GL_CLAMP), + object_(0) + { + uploadToGL(); + + // we want to know when the GL context is recreated + dispatcher::instance().addHandler("video.context_recreated", + boost::bind(&texture_impl::contextRecreated, this, _1), this); + } + + ~texture_impl() + { + unloadFromGL(); + + dispatcher::instance().removeHandler(this); + } + + + /** + * Adapted from some public domain code. This stuff is common enough that + * it really should be included in SDL_image... We need this because images + * loaded with SDL_image aren't exactly GL-ready right out of the box. This + * method makes them ready. + */ + static SDL_Surface* prepareImageForGL(SDL_Surface* surface) { - // Adapted from some public domain code. This stuff is common enough - // that it really should be included in SDL_image... We need this - // because images loaded with SDL_image aren't exactly OpenGL-ready - // right out of the box. - int w = powerOfTwo(surface->w); int h = powerOfTwo(surface->h); // 1. OpenGL images must (generally) have dimensions of a power-of-two. + // If this one doesn't, we can at least be more friendly by expanding + // the dimensions so that they are, though there will be some empty + // space within the range of normal texture coordinates. It's better of + // textures are the right size to begin with. SDL_Surface* image = SDL_CreateRGBSurface ( @@ -145,7 +189,8 @@ public: SDL_SetAlpha(surface, savedFlags, savedAlpha); } - // 2. OpenGL textures make more sense when they are "upside down." + // 2. OpenGL textures make more sense within the coordinate system when + // they are "upside down," so let's flip it. Uint8 line[image->pitch]; @@ -168,138 +213,192 @@ public: return image; } + /** + * Use SDL_image to load images from file. A surface with the image data is + * returned. + * @return Image data. + */ - void loadImageData() + SDL_Surface* loadImageData() { SDL_Surface* surface; - surface = IMG_Load(interface->getPathToFile().c_str()); + surface = IMG_Load(texture::getPathToResource(getName()).c_str()); if (!surface) { throw texture::exception("loading failed"); } - imageData = prepareImageForGL(surface); + SDL_Surface* temp = prepareImageForGL(surface); SDL_FreeSurface(surface); - if (!imageData) + if (!temp) { - throw texture::exception(""); + throw texture::exception("image couldn't be prepared for GL"); } - if (imageData->format->BytesPerPixel == 3) + if (temp->format->BytesPerPixel == 3) { - mode = GL_RGB; + mode_ = GL_RGB; } - else if (imageData->format->BytesPerPixel == 4) + else if (temp->format->BytesPerPixel == 4) { - mode = GL_RGBA; + mode_ = GL_RGBA; } else { - SDL_FreeSurface(imageData); + SDL_FreeSurface(temp); throw texture::exception("image is not the required 24 or 32 bpp"); } - width = imageData->w; - height = imageData->h; + width_ = temp->w; + height_ = temp->h; + + return temp; } + /** + * Upload the image to GL so that it will be accessible by a much more + * manageable handle and hopefully reside in video memory. + */ + void uploadToGL() { - if (object) + if (object_) { // Already loaded. return; } - if (!imageData) - { - loadImageData(); - } + SDL_Surface* imageData = loadImageData(); - glGenTextures(1, &object); - - glBindTexture(GL_TEXTURE_2D, object); + glGenTextures(1, &object_); + glBindTexture(GL_TEXTURE_2D, object_); glTexImage2D ( GL_TEXTURE_2D, 0, - mode, + mode_, imageData->w, imageData->h, 0, - mode, + mode_, GL_UNSIGNED_BYTE, imageData->pixels ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + setProperties(); - if (!keepData) - { - SDL_FreeSurface(imageData); - imageData = 0; - } + SDL_FreeSurface(imageData); } - unsigned width; - unsigned height; - int mode; - GLuint object; + /** + * Sets some texture properties such as the filters and external coordinate + * behavior. + */ + + void setProperties() + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, maxFilter_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapU_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapV_); + } + - bool keepData; - SDL_Surface* imageData; + unsigned width_; ///< Horizontal dimension of the image. + unsigned height_; ///< Vertical dimension. - texture* interface; + GLuint mode_; ///< Depth of the image, GL_RGB or GL_RGBA. + GLuint minFilter_; ///< Filter. + GLuint maxFilter_; ///< Filter. + GLuint wrapU_; ///< Wrapping behavior horizontally. + GLuint wrapV_; ///< Wrapping behavior vertically. + GLuint object_; ///< GL texture handle. }; -texture::texture(const std::string& name, bool keepInMemory) - : resource(name), impl(new texture_impl(this, keepInMemory)) {} +texture::texture(const std::string& name) : + // pass through + impl(texture::texture_impl::retain(name), &texture::texture_impl::release) +{} + +/** + * Bind the GL texture for mapping, etc. + */ void texture::bind() { glBindTexture(GL_TEXTURE_2D, getObject()); } + +/** + * Get the texture object, for the curious. + */ + GLuint texture::getObject() { - if (!impl->object) - { - impl->uploadToGL(); - } - - return impl->object; + // pass through + return impl->object_; } unsigned texture::getWidth() { - if (!impl->object) - { - impl->uploadToGL(); - } - - return impl->width; + // pass through + return impl->width_; } unsigned texture::getHeight() { - if (!impl->object) - { - impl->uploadToGL(); - } + // pass through + return impl->height_; +} + - return impl->height; +void texture::setMinFilter(GLuint filter) +{ + impl->minFilter_ = filter; +} + +void texture::setMaxFilter(GLuint filter) +{ + impl->maxFilter_ = filter; +} + +void texture::setWrapU(GLuint wrap) +{ + impl->wrapU_ = wrap; +} + +void texture::setWrapV(GLuint wrap) +{ + impl->wrapV_ = wrap; +} + + +void texture::applyChanges() +{ + bind(); + impl->setProperties(); +} + + +std::string texture::getPathToResource(const std::string& name) +{ + // TODO since this is a generic library class, more than PNG should be + // supported + return resource::getPathToResource("textures/" + name + ".png"); } } // namespace dc +/** vim: set ts=4 sw=4 tw=80: *************************************************/ +