]> Dogcows Code - chaz/yoink/blobdiff - src/texture.cc
big batch of progress
[chaz/yoink] / src / texture.cc
index 2f8f97a1c366f3befe9f8838f721ed3c608c5e9f..6d3e912925b8f1376018b9cc612fde8db1d4677a 100644 (file)
@@ -33,6 +33,8 @@
 #include <SDL/SDL.h>
 #include <SDL/SDL_image.h>
 
 #include <SDL/SDL.h>
 #include <SDL/SDL_image.h>
 
+#include "mippleton.hh"
+
 #include "dispatcher.hh"
 #include "opengl.hh"
 
 #include "dispatcher.hh"
 #include "opengl.hh"
 
 namespace dc {
 
 
 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<texture_impl>
 {
 {
+
+       /**
+        * Delete the texture (if it is loaded) from GL.
+        */
+
        void unloadFromGL()
        {
        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();
        }
 
        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)
        {
 
        static int powerOfTwo(int input)
        {
@@ -91,17 +97,55 @@ public:
                return value;
        }
 
                return value;
        }
 
+public:
+
+       /**
+        * Construction is initialization.
+        */
+
+       explicit texture_impl(const std::string& name) :
+               mippleton<texture_impl>(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)
        {
        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.
                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
                (
 
                SDL_Surface* image = SDL_CreateRGBSurface
                (
@@ -145,7 +189,8 @@ public:
                        SDL_SetAlpha(surface, savedFlags, savedAlpha);
                }
 
                        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];
 
 
                Uint8 line[image->pitch];
 
@@ -168,138 +213,192 @@ public:
                return image;
        }
 
                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;
 
        {
                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");
                }
 
 
                if (!surface)
                {
                        throw texture::exception("loading failed");
                }
 
-               imageData = prepareImageForGL(surface);
+               SDL_Surface* temp = prepareImageForGL(surface);
                SDL_FreeSurface(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
                {
                }
                else
                {
-                       SDL_FreeSurface(imageData);
+                       SDL_FreeSurface(temp);
                        throw texture::exception("image is not the required 24 or 32 bpp");
                }
 
                        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()
        {
        void uploadToGL()
        {
-               if (object)
+               if (object_)
                {
                        // Already loaded.
                        return;
                }
 
                {
                        // 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,
 
                glTexImage2D
                (
                        GL_TEXTURE_2D,
                        0,
-                       mode,
+                       mode_,
                        imageData->w,
                        imageData->h,
                        0,
                        imageData->w,
                        imageData->h,
                        0,
-                       mode,
+                       mode_,
                        GL_UNSIGNED_BYTE,
                        imageData->pixels
                );
 
                        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());
 }
 
 
 void texture::bind()
 {
        glBindTexture(GL_TEXTURE_2D, getObject());
 }
 
+
+/**
+ * Get the texture object, for the curious.
+ */
+
 GLuint texture::getObject()
 {
 GLuint texture::getObject()
 {
-       if (!impl->object)
-       {
-               impl->uploadToGL();
-       }
-
-       return impl->object;
+       // pass through
+       return impl->object_;
 }
 
 
 unsigned texture::getWidth()
 {
 }
 
 
 unsigned texture::getWidth()
 {
-       if (!impl->object)
-       {
-               impl->uploadToGL();
-       }
-
-       return impl->width;
+       // pass through
+       return impl->width_;
 }
 
 unsigned texture::getHeight()
 {
 }
 
 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
 
 }
 
 
 } // namespace dc
 
+/** vim: set ts=4 sw=4 tw=80: *************************************************/
+
This page took 0.027692 seconds and 4 git commands to generate.