*******************************************************************************/
-#include <cstring> // memcpy
+#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 {
* 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.
+ * 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>
{
/**
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;
}
}
* to cache it if the client has plenty of RAM.
*/
- void contextRecreated(const Notification& note)
+ void contextRecreated()
{
- object_ = globalObject_ = 0;
+ mObject = gObject = 0;
uploadToGL();
}
}
- static void flipSurface(SDL_Surface* image)
+ static void bindScriptConstants(Mf::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.push(GL_CLAMP); script.set("CLAMP");
+ script.push(GL_REPEAT); script.set("REPEAT");
+ script.push(GL_LINEAR); script.set("LINEAR");
+ script.push(GL_NEAREST); script.set("NEAREST");
+ script.push(GL_LINEAR_MIPMAP_LINEAR); script.set("LINEAR_MIPMAP_LINEAR");
+ script.push(GL_LINEAR_MIPMAP_NEAREST); script.set("LINEAR_MIPMAP_NEAREST");
+ script.push(GL_NEAREST_MIPMAP_LINEAR); script.set("NEAREST_MIPMAP_LINEAR");
+ script.push(GL_NEAREST_MIPMAP_NEAREST); script.set("NEAREST_MIPMAP_NEAREST");
}
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
+ ASSERT(video && "cannot load textures without a current video context");
// we want to know when the GL context is recreated
- Dispatcher::instance().addHandler("video.context_recreated",
- boost::bind(&Impl::contextRecreated, this, _1), this);
+ mDispatchHandler = core.addHandler("video.newcontext",
+ boost::bind(&Impl::contextRecreated, this));
}
~Impl()
{
- if (surface_)
- {
- SDL_FreeSurface(surface_);
- }
-
unloadFromGL();
-
- Dispatcher::instance().removeHandler(this);
}
* method makes them ready.
*/
+ /*
static SDL_Surface* prepareImageForGL(SDL_Surface* surface)
{
int w = powerOfTwo(surface->w);
return image;
}
+ */
/**
* Use SDL_image to load images from file. A surface with the image data is
* @return Image data.
*/
- void loadFromFile()
+ void init(const std::string& name)
{
- SDL_Surface* surface;
+ std::string path = Texture::getPath(name);
- surface = IMG_Load(Texture::getPath(getName()).c_str());
-
- 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");
- }
+ Mf::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()) != Mf::Script::SUCCESS)
{
- mode_ = GL_RGBA;
+ std::string str;
+ script[-1].get(str);
+ Mf::logWarning(str);
}
else
{
- SDL_FreeSurface(temp);
- throw Texture::Exception("incompatible color mode");
- }
+ Mf::logInfo << "loading tiles from texture " << path << std::endl;
+
+ Mf::Script::Slot globals = script.getGlobalTable();
+ Mf::Script::Slot top = script[-1];
+
+ globals.pushField("tiles_s");
+ top.get(mTilesS);
+
+ globals.pushField("tiles_t");
+ top.get(mTilesT);
- width_ = temp->w;
- height_ = temp->h;
+ globals.pushField("min_filter");
+ top.get(mMinFilter);
- surface_ = temp;
+ globals.pushField("mag_filter");
+ top.get(mMagFilter);
+
+ globals.pushField("wrap_s");
+ top.get(mWrapS);
+
+ globals.pushField("wrap_t");
+ top.get(mWrapT);
+ }
}
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;
}
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;
+ }
+
+ 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 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.
+ GLuint mObject; ///< GL texture handle.
+ static GLuint gObject; ///< Global GL texture handle.
- GLuint object_; ///< GL texture handle.
- static GLuint globalObject_; ///< Global GL texture handle.
+ Dispatch::Handler mDispatchHandler;
};
-GLuint Texture::Impl::globalObject_ = 0;
+GLuint Texture::Impl::gObject = 0;
Texture::Texture(const std::string& name) :
+ Image(Texture::getPath(name)),
// pass through
- impl_(Texture::Impl::retain(name), &Texture::Impl::release) {}
+ mImpl(Texture::Impl::getInstance(name)) {}
/**
void Texture::bind() const
{
// pass through
- impl_->bind();
+ mImpl->bind();
}
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)
{
- std::string path = Resource::getPath("textures/" + name + ".png");
- return path;
+ if (boost::find_last(name, ".png"))
+ {
+ return Resource::getPath(name);
+ }
+ else
+ {
+ std::string path("textures/");
+ path += name;
+ path += ".png";
+ return Resource::getPath(path);
+ }
}