*******************************************************************************/
-#include <cstdlib>
+#include <cstring> // memcpy
#include <boost/bind.hpp>
#include <SDL/SDL_image.h>
#include "Dispatcher.hh"
-#include "Mippleton.hh"
+#include "Exception.hh"
+#include "Library.hh"
+#include "Log.hh"
#include "OpenGL.hh"
#include "Texture.hh"
* objects and avoid having duplicate textures loaded to GL.
*/
-class Texture::TextureImpl : public Mippleton<TextureImpl>
+class Texture::Impl : public Library<Impl>
{
/**
void unloadFromGL()
{
- if (object_)
+ if (mObject)
{
- glDeleteTextures(1, &object_);
- object_ = 0;
+ if (mObject == globalObject_)
+ {
+ globalObject_ = 0;
+ }
+
+ glDeleteTextures(1, &mObject);
+ mObject = 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.
+ * 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(const Notification* note)
{
- unloadFromGL();
+ mObject = globalObject_ = 0;
uploadToGL();
}
return value;
}
+
+ static void flipSurface(SDL_Surface* image)
+ {
+ 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);
+ }
+
public:
/**
* Construction is initialization.
*/
- explicit TextureImpl(const std::string& name) :
- Mippleton<TextureImpl>(name),
- width_(0),
- height_(0),
- mode_(0),
- minFilter_(GL_NEAREST),
- maxFilter_(GL_NEAREST),
- wrapU_(GL_CLAMP),
- wrapV_(GL_CLAMP),
- object_(0)
+ explicit Impl(const std::string& name) :
+ Library<Impl>(name),
+ mContext(0),
+ mWidth(0),
+ mHeight(0),
+ mMode(0),
+ mMinFilter(GL_NEAREST),
+ mMagFilter(GL_NEAREST),
+ mWrapS(GL_CLAMP),
+ mWrapT(GL_CLAMP),
+ mObject(0)
{
- uploadToGL();
+ loadFromFile();
// we want to know when the GL context is recreated
- Dispatcher::instance().addHandler("video.context_recreated",
- boost::bind(&TextureImpl::contextRecreated, this, _1), this);
+ Dispatcher::getInstance().addHandler("video.context_recreated",
+ boost::bind(&Impl::contextRecreated, this, _1), this);
}
- ~TextureImpl()
+ ~Impl()
{
+ if (mContext)
+ {
+ SDL_FreeSurface(mContext);
+ }
+
unloadFromGL();
- Dispatcher::instance().removeHandler(this);
+ Dispatcher::getInstance().removeHandler(this);
}
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.
+
+ 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 of
+ // 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
w, h,
32,
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
- 0x000000FF,
- 0x0000FF00,
- 0x00FF0000,
+ 0x000000FF,
+ 0x0000FF00,
+ 0x00FF0000,
0xFF000000
#else
0xFF000000,
- 0x00FF0000,
- 0x0000FF00,
+ 0x00FF0000,
+ 0x0000FF00,
0x000000FF
#endif
);
SDL_SetAlpha(surface, savedFlags, savedAlpha);
}
- // 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 *pixels = static_cast<Uint8*>(image->pixels);
- Uint16 pitch = image->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);
-
return image;
}
* @return Image data.
*/
- SDL_Surface* loadImageData()
+ void loadFromFile()
{
SDL_Surface* surface;
- surface = IMG_Load(Texture::getPathToResource(getName()).c_str());
+ surface = IMG_Load(Texture::getPath(getName()).c_str());
if (!surface)
{
- throw Texture::Exception("loading failed");
+ logWarning("texture not found: %s", getName().c_str());
+ throw Exception(ErrorCode::FILE_NOT_FOUND, getName().c_str());
}
SDL_Surface* temp = prepareImageForGL(surface);
if (!temp)
{
- throw Texture::Exception("image couldn't be prepared for GL");
+ throw Exception(ErrorCode::UNKNOWN_IMAGE_FORMAT);
}
if (temp->format->BytesPerPixel == 3)
{
- mode_ = GL_RGB;
+ mMode = GL_RGB;
}
else if (temp->format->BytesPerPixel == 4)
{
- mode_ = GL_RGBA;
+ mMode = GL_RGBA;
}
else
{
SDL_FreeSurface(temp);
- throw Texture::Exception("image is not the required 24 or 32 bpp");
+ throw Exception(ErrorCode::UNKNOWN_IMAGE_FORMAT);
}
- width_ = temp->w;
- height_ = temp->h;
+ mWidth = temp->w;
+ mHeight = temp->h;
- return temp;
+ mContext = temp;
}
void uploadToGL()
{
- if (object_)
+ if (mObject)
{
- // Already loaded.
+ // already loaded
return;
}
- SDL_Surface* imageData = loadImageData();
+ if (!mContext) loadFromFile();
- glGenTextures(1, &object_);
- glBindTexture(GL_TEXTURE_2D, object_);
+ glGenTextures(1, &mObject);
+ glBindTexture(GL_TEXTURE_2D, mObject);
glTexImage2D
+ //gluBuild2DMipmaps
(
GL_TEXTURE_2D,
0,
- mode_,
- imageData->w,
- imageData->h,
+ mMode,
+ //3,
+ mContext->w,
+ mContext->h,
0,
- mode_,
+ mMode,
GL_UNSIGNED_BYTE,
- imageData->pixels
+ mContext->pixels
);
setProperties();
- SDL_FreeSurface(imageData);
+ SDL_FreeSurface(mContext);
+ mContext = 0;
}
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_);
+ 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();
+ mMinFilter = filter;
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mMinFilter);
}
+ inline void setMagFilter(GLuint filter)
+ {
+ bind();
+ mMagFilter = filter;
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mMagFilter);
+ }
+
+ inline void setWrapS(GLuint wrap)
+ {
+ bind();
+ mWrapS = wrap;
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mWrapS);
+ }
+
+ inline void setWrapT(GLuint wrap)
+ {
+ bind();
+ mWrapT = wrap;
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mWrapT);
+ }
+
+
+ inline void bind()
+ {
+ if (mObject == 0)
+ {
+ uploadToGL();
+ }
+ if (mObject != globalObject_)
+ {
+ glBindTexture(GL_TEXTURE_2D, mObject);
+ globalObject_ = mObject;
+ }
+ }
- unsigned width_; ///< Horizontal dimension of the image.
- unsigned height_; ///< Vertical dimension.
- 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.
+ SDL_Surface* mContext;
+ unsigned mWidth; ///< Horizontal dimension of the image.
+ unsigned mHeight; ///< Vertical dimension.
+
+ 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 globalObject_; ///< Global GL texture handle.
};
+GLuint Texture::Impl::globalObject_ = 0;
+
Texture::Texture(const std::string& name) :
// pass through
- impl_(Texture::TextureImpl::retain(name), &Texture::TextureImpl::release)
-{}
+ mImpl(Texture::Impl::getInstance(name)) {}
/**
* Bind the GL texture for mapping, etc.
*/
-void Texture::bind()
+void Texture::bind() const
{
- glBindTexture(GL_TEXTURE_2D, getObject());
+ // pass through
+ mImpl->bind();
}
* Get the texture object, for the curious.
*/
-GLuint Texture::getObject()
+GLuint Texture::getObject() const
{
// pass through
- return impl_->object_;
+ return mImpl->mObject;
}
-unsigned Texture::getWidth()
+void Texture::resetBind()
{
- // pass through
- return impl_->width_;
+ glBindTexture(GL_TEXTURE_2D, 0);
+ Impl::globalObject_ = 0;
}
-unsigned Texture::getHeight()
+
+unsigned Texture::getWidth() const
{
// pass through
- return impl_->height_;
+ return mImpl->mWidth;
}
-
-void Texture::setMinFilter(GLuint filter)
+unsigned Texture::getHeight() const
{
- impl_->minFilter_ = filter;
+ // pass through
+ return mImpl->mHeight;
}
-void Texture::setMaxFilter(GLuint filter)
+
+void Texture::setMinFilter(GLuint filter)
{
- impl_->maxFilter_ = filter;
+ // pass through
+ mImpl->setMinFilter(filter);
}
-void Texture::setWrapU(GLuint wrap)
+void Texture::setMagFilter(GLuint filter)
{
- impl_->wrapU_ = wrap;
+ // pass through
+ mImpl->setMagFilter(filter);
}
-void Texture::setWrapV(GLuint wrap)
+void Texture::setWrapS(GLuint wrap)
{
- impl_->wrapV_ = wrap;
+ // pass through
+ mImpl->setWrapS(wrap);
}
-
-void Texture::applyChanges()
+void Texture::setWrapT(GLuint wrap)
{
- bind();
- impl_->setProperties();
+ // pass through
+ mImpl->setWrapT(wrap);
}
-std::string Texture::getPathToResource(const std::string& name)
+std::string Texture::getPath(const std::string& name)
{
- // TODO since this is a generic library class, more than PNG should be
- // supported
- return Resource::getPathToResource("textures/" + name + ".png");
+ std::string path = Resource::getPath("textures/" + name + ".png");
+ return path;
}