]> Dogcows Code - chaz/yoink/blobdiff - src/Moof/Texture.cc
bugfix: win32 packaging script temp directories
[chaz/yoink] / src / Moof / Texture.cc
index 8aa66c4f1ec7c64c95b3d35016f4ef1e9068593d..7f9f83c335b98eb7f8a39d4e02023ef0b6f68708 100644 (file)
@@ -1,57 +1,44 @@
 
-/*******************************************************************************
-
- Copyright (c) 2009, Charles McGarvey
- All rights reserved.
- Redistribution   and   use  in  source  and  binary  forms,  with  or  without
- modification, are permitted provided that the following conditions are met:
-   * Redistributions  of  source  code  must retain the above copyright notice,
-     this list of conditions and the following disclaimer.
-   * Redistributions  in binary form must reproduce the above copyright notice,
-     this  list of conditions and the following disclaimer in the documentation
-     and/or other materials provided with the distribution.
- THIS  SOFTWARE  IS  PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND  ANY  EXPRESS  OR  IMPLIED  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED.  IN  NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- FOR  ANY  DIRECT,  INDIRECT,  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES  (INCLUDING,  BUT  NOT  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- SERVICES;  LOSS  OF  USE,  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- OR  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-*******************************************************************************/
-
-#include <cstring>             // memcpy
-
+/*]  Copyright (c) 2009-2010, Charles McGarvey  [**************************
+**]  All rights reserved.
+*
+* vi:ts=4 sw=4 tw=75
+*
+* Distributable under the terms and conditions of the 2-clause BSD license;
+* see the file COPYING for a complete text of the license.
+*
+**************************************************************************/
+
+#include <cstdio>              // FILE
+#include <cstring>             // strncmp
+
+#include <boost/algorithm/string.hpp>
 #include <boost/bind.hpp>
 
-#include <SDL/SDL.h>
-#include <SDL/SDL_image.h>
-
-#include "Dispatcher.hh"
-#include "Mippleton.hh"
+#include "Dispatch.hh"
+#include "Error.hh"
+#include "Manager.hh"
+#include "Log.hh"
 #include "OpenGL.hh"
+#include "Script.hh"
 #include "Texture.hh"
+#include "Video.hh"
 
 
 namespace Mf {
 
 
 /**
- * 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.
+ * 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 manager so that multiple texture
+ * objects can share the same internal objects and avoid having duplicate
+ * textures loaded to GL.
  */
 
-class Texture::Impl : public Mippleton<Impl>
+class Texture::Impl : public Manager<Impl>
 {
 
        /**
@@ -60,33 +47,34 @@ class Texture::Impl : public Mippleton<Impl>
 
        void unloadFromGL()
        {
-               if (object_)
+               if (mObject)
                {
-                       if (object_ == globalObject_)
+                       if (mObject == gObject)
                        {
-                               globalObject_ = 0;
+                               gObject = 0;
                        }
 
-                       glDeleteTextures(1, &object_);
-                       object_ = 0;
+                       glDeleteTextures(1, &mObject);
+                       mObject = 0;
                }
        }
 
        /**
-        * If the GL context was recreated, we 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.
+        * If the GL context was recreated, we 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)
+       void contextRecreated()
        {
-               object_ = globalObject_ = 0;
+               mObject = gObject = 0;
                uploadToGL();
        }
 
        /**
-        * 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.
+        * 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)
@@ -101,26 +89,18 @@ class Texture::Impl : public Mippleton<Impl>
        }
 
 
-       static void flipSurface(SDL_Surface* image)
+       static void bindScriptConstants(Script& script)
        {
-               unsigned char*  pixels = (Uint8*)(image->pixels);
-
-               unsigned                pitch = image->pitch;
-               unsigned char   line[pitch];
-
-               int             yBegin = 0;
-               int             yEnd = image->h - 1;
-
-               if (SDL_MUSTLOCK(image)) SDL_LockSurface(image);
-               while (yBegin < yEnd)
-               {
-                       memcpy(line,                    pixels + pitch * yBegin, pitch);
-                       memcpy(pixels + pitch * yBegin, pixels + pitch * yEnd,   pitch);
-                       memcpy(pixels + pitch * yEnd,   line,                    pitch);
-                       yBegin++;
-                       yEnd--;
-               }
-               if (SDL_MUSTLOCK(image)) SDL_UnlockSurface(image);
+               Script::Slot g = script.globals();
+
+               g.setField("CLAMP",   GL_CLAMP);
+               g.setField("REPEAT",  GL_REPEAT);
+               g.setField("LINEAR",  GL_LINEAR);
+               g.setField("NEAREST", GL_NEAREST);
+               g.setField("LINEAR_MIPMAP_LINEAR",   GL_LINEAR_MIPMAP_LINEAR);
+               g.setField("LINEAR_MIPMAP_NEAREST",  GL_LINEAR_MIPMAP_NEAREST);
+               g.setField("NEAREST_MIPMAP_LINEAR",  GL_NEAREST_MIPMAP_LINEAR);
+               g.setField("NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST);
        }
 
 public:
@@ -129,60 +109,55 @@ public:
         * Construction is initialization.
         */
 
-       explicit Impl(const std::string& name) :
-               Mippleton<Impl>(name),
-               surface_(0),
-               width_(0),
-               height_(0),
-               mode_(0),
-               minFilter_(GL_NEAREST),
-               magFilter_(GL_NEAREST),
-               wrapS_(GL_CLAMP),
-               wrapT_(GL_CLAMP),
-               object_(0)
+       Impl() :
+               mMinFilter(GL_NEAREST),
+               mMagFilter(GL_NEAREST),
+               mWrapS(GL_CLAMP),
+               mWrapT(GL_CLAMP),
+               mTilesS(1),
+               mTilesT(1),
+               mObject(0)
        {
-               loadFromFile();
+               // make sure we have a video context
+               Video* video = Video::current();
+               ASSERT(video && "should have a video context set");
 
                // we want to know when the GL context is recreated
-               Dispatcher::instance().addHandler("video.context_recreated",
-                               boost::bind(&Impl::contextRecreated, this, _1), this);
+               Dispatch& dispatch = Dispatch::global();
+               mNewContextDispatch = dispatch.addTarget("video.newcontext",
+                                                       boost::bind(&Impl::contextRecreated, this));
        }
 
        ~Impl()
        {
-               if (surface_)
-               {
-                       SDL_FreeSurface(surface_);
-               }
-
                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.
+        * 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)
        {
                int w = powerOfTwo(surface->w);
                int h = powerOfTwo(surface->h);
 
-               // 2. OpenGL textures make more sense within the coordinate system when
-               // they are "upside down," so let's flip it.
+               // 2. OpenGL textures make more sense within the coordinate system
+               // when they are "upside down," so let's flip it.
 
                flipSurface(surface);
 
-               // 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 if
-               // textures are the right size to begin with.
+               // 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 if textures are the right size
+               // to begin with.
 
                SDL_Surface* image = SDL_CreateRGBSurface
                (
@@ -228,50 +203,53 @@ public:
 
                return image;
        }
+       */
 
        /**
-        * Use SDL_image to load images from file.  A surface with the image data is
-        * returned.
+        * Use SDL_image to load images from file.  A surface with the image
+        * data is returned.
         * @return Image data.
         */
 
-       void loadFromFile()
+       void init(const std::string& name)
        {
-               SDL_Surface* surface;
-
-               surface = IMG_Load(Texture::getPath(getName()).c_str());
+               std::string path(name);
+               
+               Texture::getPath(path);
 
-               if (!surface)
+               mImage = Image::alloc(path);
+               if (!mImage->isValid())
                {
-                       throw Texture::Exception("loading from file failed");
+                       logWarning << "texture not found: " << path << std::endl;
+                       Error(Error::RESOURCE_NOT_FOUND, path).raise();
                }
 
-               SDL_Surface* temp = prepareImageForGL(surface);
-               SDL_FreeSurface(surface);
+               mImage->flip();
 
-               if (!temp)
-               {
-                       throw Texture::Exception("uploading to opengl failed");
-               }
+               Script script;
 
-               if (temp->format->BytesPerPixel == 3)
-               {
-                       mode_ = GL_RGB;
-               }
-               else if (temp->format->BytesPerPixel == 4)
+               importLogFunctions(script);
+               bindScriptConstants(script);
+
+               if (script.doString(mImage->getComment()) != Script::SUCCESS)
                {
-                       mode_ = GL_RGBA;
+                       std::string str;
+                       script[-1].get(str);
+                       logWarning(str);
                }
                else
                {
-                       SDL_FreeSurface(temp);
-                       throw Texture::Exception("incompatible color mode");
+                       logInfo << "loading tiles from texture " << path
+                                       << std::endl;
+
+                       Script::Slot globals = script.globals();
+                       globals.get(mTilesS, "tiles_s");
+                       globals.get(mTilesT, "tiles_t");
+                       globals.get(mMinFilter, "min_filter");
+                       globals.get(mMagFilter, "mag_filter");
+                       globals.get(mWrapS, "wrap_s");
+                       globals.get(mWrapT, "wrap_t");
                }
-
-               width_ = temp->w;
-               height_ = temp->h;
-
-               surface_ = temp;
        }
 
 
@@ -282,113 +260,132 @@ public:
 
        void uploadToGL()
        {
-               if (object_)
+               if (mObject)
                {
                        // already loaded
                        return;
                }
 
-               if (!surface_) loadFromFile();
-
-               glGenTextures(1, &object_);
-               glBindTexture(GL_TEXTURE_2D, object_);
+               glGenTextures(1, &mObject);
+               glBindTexture(GL_TEXTURE_2D, mObject);
 
                glTexImage2D
+               //gluBuild2DMipmaps
                (
                        GL_TEXTURE_2D,
                        0,
-                       mode_,
-                       surface_->w,
-                       surface_->h,
+                       mImage->getMode(),
+                       //3,
+                       mImage->getWidth(),
+                       mImage->getHeight(),
                        0,
-                       mode_,
+                       mImage->getMode(),
                        GL_UNSIGNED_BYTE,
-                       surface_->pixels
+                       mImage->getPixels()
                );
 
                setProperties();
-
-               SDL_FreeSurface(surface_);
-               surface_ = 0;
        }
 
 
        /**
-        * Sets some texture properties such as the filters and external coordinate
-        * behavior.
+        * 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, magFilter_);
-               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS_);
-               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT_);
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mMinFilter);
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mMagFilter);
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mWrapS);
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mWrapT);
        }
 
        inline void setMinFilter(GLuint filter)
        {
                bind();
-               minFilter_ = filter;
-               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter_);
+               mMinFilter = filter;
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mMinFilter);
        }
 
        inline void setMagFilter(GLuint filter)
        {
                bind();
-               magFilter_ = filter;
-               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter_);
+               mMagFilter = filter;
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mMagFilter);
        }
 
        inline void setWrapS(GLuint wrap)
        {
                bind();
-               wrapS_ = wrap;
-               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS_);
+               mWrapS = wrap;
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mWrapS);
        }
 
        inline void setWrapT(GLuint wrap)
        {
                bind();
-               wrapT_ = wrap;
-               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT_);
+               mWrapT = wrap;
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mWrapT);
        }
 
 
        inline void bind()
        {
-               if (object_ == 0)
+               if (mObject == 0)
                {
                        uploadToGL();
                }
-               if (object_ != globalObject_)
+               if (mObject != gObject)
                {
-                       glBindTexture(GL_TEXTURE_2D, object_);
-                       globalObject_ = object_;
+                       glBindTexture(GL_TEXTURE_2D, mObject);
+                       gObject = mObject;
                }
        }
 
 
-       SDL_Surface*    surface_;
-       unsigned                width_;                 ///< Horizontal dimension of the image.
-       unsigned                height_;                ///< Vertical dimension.
+       bool getTileCoords(Texture::TileIndex index, Scalar coords[8]) const
+       {
+               // make sure the index represents a real tile
+               if (index >= mTilesS * mTilesT) return false;
+
+               Scalar w = 1.0 / Scalar(mTilesS);
+               Scalar h = 1.0 / Scalar(mTilesT);
+
+               coords[0] = Scalar(index % mTilesS) * w;
+               coords[1] = (Scalar(mTilesT - 1) - Scalar(index / mTilesS)) * h;
+               coords[2] = coords[0] + w;
+               coords[3] = coords[1];
+               coords[4] = coords[2];
+               coords[5] = coords[1] + h;
+               coords[6] = coords[0];
+               coords[7] = coords[5];
+
+               return true;
+       }
 
-       GLuint                  mode_;                  ///< Depth of the image, GL_RGB or GL_RGBA.
-       GLuint                  minFilter_;             ///< Minifcation filter.
-       GLuint                  magFilter_;             ///< Magnification filter.
-       GLuint                  wrapS_;                 ///< Wrapping behavior horizontally.
-       GLuint                  wrapT_;                 ///< Wrapping behavior vertically.
+       ImageP                          mImage;
 
-       GLuint                  object_;                ///< GL texture handle.
-       static GLuint   globalObject_;  ///< Global GL texture handle.
+       GLuint                          mMinFilter;     ///< Minification filter.
+       GLuint                          mMagFilter;     ///< Magnification filter.
+       GLuint                          mWrapS;         ///< Wrapping behavior horizontally.
+       GLuint                          mWrapT;         ///< Wrapping behavior vertically.
+       unsigned                        mTilesS;
+       unsigned                        mTilesT;
+
+       GLuint                          mObject;        ///< GL texture handle.
+       static GLuint           gObject;        ///< Global GL texture handle.
+
+       Dispatch::Handle        mNewContextDispatch;
 };
 
-GLuint Texture::Impl::globalObject_ = 0;
+GLuint Texture::Impl::gObject = 0;
 
 
-Texture::Texture(const std::string& name) :
+Texture::Texture(const std::string& name) : // TODO hmm..
+       Image(name),
        // pass through
-       impl_(Texture::Impl::retain(name), &Texture::Impl::release) {}
+       mImpl(Texture::Impl::getInstance(name)) {}
 
 
 /**
@@ -398,7 +395,7 @@ Texture::Texture(const std::string& name) :
 void Texture::bind() const
 {
        // pass through
-       impl_->bind();
+       mImpl->bind();
 }
 
 
@@ -409,63 +406,82 @@ void Texture::bind() const
 GLuint Texture::getObject() const
 {
        // pass through
-       return impl_->object_;
+       return mImpl->mObject;
 }
 
 
 void Texture::resetBind()
 {
        glBindTexture(GL_TEXTURE_2D, 0);
-       Impl::globalObject_ = 0;
+       Impl::gObject = 0;
 }
 
 
-unsigned Texture::getWidth() const
+void Texture::setMinFilter(GLuint filter)
 {
        // pass through
-       return impl_->width_;
+       mImpl->setMinFilter(filter);
 }
 
-unsigned Texture::getHeight() const
+void Texture::setMagFilter(GLuint filter)
 {
        // pass through
-       return impl_->height_;
+       mImpl->setMagFilter(filter);
 }
 
-
-void Texture::setMinFilter(GLuint filter)
+void Texture::setWrapS(GLuint wrap)
 {
        // pass through
-       impl_->setMinFilter(filter);
+       mImpl->setWrapS(wrap);
 }
 
-void Texture::setMagFilter(GLuint filter)
+void Texture::setWrapT(GLuint wrap)
 {
        // pass through
-       impl_->setMagFilter(filter);
+       mImpl->setWrapT(wrap);
 }
 
-void Texture::setWrapS(GLuint wrap)
+
+bool Texture::getTileCoords(TileIndex index, Scalar coords[8]) const
 {
        // pass through
-       impl_->setWrapS(wrap);
+       return mImpl->getTileCoords(index, coords);
 }
 
-void Texture::setWrapT(GLuint wrap)
+bool Texture::getTileCoords(TileIndex index, Scalar coords[8],
+               Orientation orientation) const
 {
-       // pass through
-       impl_->setWrapT(wrap);
+       if (getTileCoords(index, coords))
+       {
+               if (orientation & FLIP)
+               {
+                       // this looks kinda weird, but it's just swapping in a way that
+                       // doesn't require an intermediate variable
+                       coords[1] = coords[5];
+                       coords[5] = coords[3];
+                       coords[3] = coords[7];
+                       coords[7] = coords[5];
+               }
+               if (orientation & REVERSE)
+               {
+                       coords[0] = coords[2];
+                       coords[2] = coords[6];
+                       coords[4] = coords[6];
+                       coords[6] = coords[0];
+               }
+
+               return true;
+       }
+
+       return false;
 }
 
 
-std::string Texture::getPath(const std::string& name)
+bool Texture::getPath(std::string& name)
 {
-       std::string path = Resource::getPath("textures/" + name + ".png");
-       return path;
+       return Resource::getPath(name, "textures/", "png");
 }
 
 
 } // namespace Mf
 
-/** vim: set ts=4 sw=4 tw=80: *************************************************/
-
This page took 0.03559 seconds and 4 git commands to generate.