-/*******************************************************************************
-
- 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 "Library.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 Library<Impl>
+class Texture::Impl : public Manager<Impl>
{
/**
{
if (mObject)
{
- if (mObject == globalObject_)
+ if (mObject == gObject)
{
- globalObject_ = 0;
+ gObject = 0;
}
glDeleteTextures(1, &mObject);
}
/**
- * 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()
{
- mObject = 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)
}
- 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:
* Construction is initialization.
*/
- explicit Impl(const std::string& name) :
- Library<Impl>(name),
- mContext(0),
- mWidth(0),
- mHeight(0),
- mMode(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::getInstance().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 (mContext)
- {
- SDL_FreeSurface(mContext);
- }
-
unloadFromGL();
-
- Dispatcher::getInstance().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
(
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())
{
- logWarning("texture not found: %s", getName().c_str());
- throw Exception(Exception::FILE_NOT_FOUND);
+ 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 Exception(Exception::OPENGL_ERROR);
- }
+ Script script;
- if (temp->format->BytesPerPixel == 3)
- {
- mMode = GL_RGB;
- }
- else if (temp->format->BytesPerPixel == 4)
+ importLogFunctions(script);
+ bindScriptConstants(script);
+
+ if (script.doString(mImage->getComment()) != Script::SUCCESS)
{
- mMode = GL_RGBA;
+ std::string str;
+ script[-1].get(str);
+ logWarning(str);
}
else
{
- SDL_FreeSurface(temp);
- throw Exception(Exception::BAD_IMAGE_FORMAT);
+ 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");
}
-
- mWidth = temp->w;
- mHeight = temp->h;
-
- mContext = temp;
}
return;
}
- if (!mContext) loadFromFile();
-
glGenTextures(1, &mObject);
glBindTexture(GL_TEXTURE_2D, mObject);
(
GL_TEXTURE_2D,
0,
- mMode,
+ mImage->getMode(),
//3,
- mContext->w,
- mContext->h,
+ mImage->getWidth(),
+ mImage->getHeight(),
0,
- mMode,
+ mImage->getMode(),
GL_UNSIGNED_BYTE,
- mContext->pixels
+ mImage->getPixels()
);
setProperties();
-
- SDL_FreeSurface(mContext);
- mContext = 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()
{
uploadToGL();
}
- if (mObject != globalObject_)
+ if (mObject != gObject)
{
glBindTexture(GL_TEXTURE_2D, mObject);
- globalObject_ = mObject;
+ gObject = mObject;
}
}
- SDL_Surface* mContext;
- unsigned mWidth; ///< Horizontal dimension of the image.
- unsigned mHeight; ///< 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;
+ }
+
+ ImageP mImage;
+
+ GLuint mMinFilter; ///< Minification filter.
+ GLuint mMagFilter; ///< Magnification filter.
+ GLuint mWrapS; ///< Wrapping behavior horizontally.
+ GLuint mWrapT; ///< Wrapping behavior vertically.
+ unsigned mTilesS;
+ unsigned mTilesT;
- GLuint mMode; ///< Depth of the image, GL_RGB or GL_RGBA.
- GLuint mMinFilter; ///< Minifcation filter.
- GLuint mMagFilter; ///< Magnification filter.
- GLuint mWrapS; ///< Wrapping behavior horizontally.
- GLuint mWrapT; ///< Wrapping behavior vertically.
+ GLuint mObject; ///< GL texture handle.
+ static GLuint gObject; ///< Global GL texture handle.
- GLuint mObject; ///< GL texture handle.
- static GLuint globalObject_; ///< 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
mImpl(Texture::Impl::getInstance(name)) {}
void Texture::resetBind()
{
glBindTexture(GL_TEXTURE_2D, 0);
- Impl::globalObject_ = 0;
-}
-
-
-unsigned Texture::getWidth() const
-{
- // pass through
- return mImpl->mWidth;
-}
-
-unsigned Texture::getHeight() const
-{
- // pass through
- return mImpl->mHeight;
+ Impl::gObject = 0;
}
}
-std::string Texture::getPath(const std::string& name)
+bool Texture::getTileCoords(TileIndex index, Scalar coords[8]) const
{
- std::string path = Resource::getPath("textures/" + name + ".png");
- return path;
+ // pass through
+ return mImpl->getTileCoords(index, coords);
}
+bool Texture::getTileCoords(TileIndex index, Scalar coords[8],
+ Orientation orientation) const
+{
+ 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];
+ }
-} // namespace Mf
+ return true;
+ }
+
+ return false;
+}
-/** vim: set ts=4 sw=4 tw=80: *************************************************/
+
+bool Texture::getPath(std::string& name)
+{
+ return Resource::getPath(name, "textures/", "png");
+}
+
+
+} // namespace Mf