+++ /dev/null
-
-/*] 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 "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 manager so that multiple texture
- * objects can share the same internal objects and avoid having duplicate
- * textures loaded to GL.
- */
-
-class Texture::Impl : public Manager<Impl>
-{
-
- /**
- * Delete the texture (if it is loaded) from GL.
- */
-
- void unloadFromGL()
- {
- if (mObject)
- {
- if (mObject == gObject)
- {
- gObject = 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.
- */
-
- void contextRecreated()
- {
- 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.
- */
-
- static int powerOfTwo(int input)
- {
- int value = 1;
-
- while (value < input)
- {
- value <<= 1;
- }
- return value;
- }
-
-
- static void bindScriptConstants(Script& script)
- {
- 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.
- */
-
- Impl() :
- mMinFilter(GL_NEAREST),
- mMagFilter(GL_NEAREST),
- mWrapS(GL_CLAMP),
- mWrapT(GL_CLAMP),
- mTilesS(1),
- mTilesT(1),
- mObject(0)
- {
- // 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
- Dispatch& dispatch = Dispatch::global();
- mNewContextDispatch = dispatch.addTarget("video.newcontext",
- boost::bind(&Impl::contextRecreated, this));
- }
-
- ~Impl()
- {
- unloadFromGL();
- }
-
-
- /**
- * 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.
-
- 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.
-
- SDL_Surface* image = SDL_CreateRGBSurface
- (
- SDL_SWSURFACE,
- w, h,
- 32,
-#if SDL_BYTEORDER == SDL_LIL_ENDIAN
- 0x000000FF,
- 0x0000FF00,
- 0x00FF0000,
- 0xFF000000
-#else
- 0xFF000000,
- 0x00FF0000,
- 0x0000FF00,
- 0x000000FF
-#endif
- );
-
- if (!image)
- {
- return 0;
- }
-
- Uint32 savedFlags = surface->flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
- Uint8 savedAlpha = surface->format->alpha;
- if (savedFlags & SDL_SRCALPHA)
- {
- SDL_SetAlpha(surface, 0, 0);
- }
-
- SDL_Rect srcArea, destArea;
- srcArea.x = 0; destArea.x = 0;
- srcArea.y = 0; destArea.y = h - surface->h;
- srcArea.w = surface->w;
- srcArea.h = surface->h;
- SDL_BlitSurface(surface, &srcArea, image, &destArea);
-
- if (savedFlags & SDL_SRCALPHA)
- {
- SDL_SetAlpha(surface, savedFlags, savedAlpha);
- }
-
- return image;
- }
- */
-
- /**
- * Use SDL_image to load images from file. A surface with the image
- * data is returned.
- * @return Image data.
- */
-
- void init(const std::string& name)
- {
- std::string path(name);
-
- Texture::getPath(path);
-
- mImage = Image::alloc(path);
- if (!mImage->isValid())
- {
- logWarning << "texture not found: " << path << std::endl;
- Error(Error::RESOURCE_NOT_FOUND, path).raise();
- }
-
- mImage->flip();
-
- Script script;
-
- importLogFunctions(script);
- bindScriptConstants(script);
-
- if (script.doString(mImage->getComment()) != Script::SUCCESS)
- {
- std::string str;
- script[-1].get(str);
- logWarning(str);
- }
- else
- {
- 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");
- }
- }
-
-
- /**
- * 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()
- {
- if (mObject)
- {
- // already loaded
- return;
- }
-
- glGenTextures(1, &mObject);
- glBindTexture(GL_TEXTURE_2D, mObject);
-
- glTexImage2D
- //gluBuild2DMipmaps
- (
- GL_TEXTURE_2D,
- 0,
- mImage->getMode(),
- //3,
- mImage->getWidth(),
- mImage->getHeight(),
- 0,
- mImage->getMode(),
- GL_UNSIGNED_BYTE,
- mImage->getPixels()
- );
-
- setProperties();
- }
-
-
- /**
- * Sets some texture properties such as the filters and external
- * coordinate behavior.
- */
-
- void setProperties()
- {
- 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 != gObject)
- {
- glBindTexture(GL_TEXTURE_2D, mObject);
- gObject = mObject;
- }
- }
-
-
- 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 mObject; ///< GL texture handle.
- static GLuint gObject; ///< Global GL texture handle.
-
- Dispatch::Handle mNewContextDispatch;
-};
-
-GLuint Texture::Impl::gObject = 0;
-
-
-Texture::Texture(const std::string& name) : // TODO hmm..
- Image(name),
- // pass through
- mImpl(Texture::Impl::getInstance(name)) {}
-
-
-/**
- * Bind the GL texture for mapping, etc.
- */
-
-void Texture::bind() const
-{
- // pass through
- mImpl->bind();
-}
-
-
-/**
- * Get the texture object, for the curious.
- */
-
-GLuint Texture::getObject() const
-{
- // pass through
- return mImpl->mObject;
-}
-
-
-void Texture::resetBind()
-{
- glBindTexture(GL_TEXTURE_2D, 0);
- Impl::gObject = 0;
-}
-
-
-void Texture::setMinFilter(GLuint filter)
-{
- // pass through
- mImpl->setMinFilter(filter);
-}
-
-void Texture::setMagFilter(GLuint filter)
-{
- // pass through
- mImpl->setMagFilter(filter);
-}
-
-void Texture::setWrapS(GLuint wrap)
-{
- // pass through
- mImpl->setWrapS(wrap);
-}
-
-void Texture::setWrapT(GLuint wrap)
-{
- // pass through
- mImpl->setWrapT(wrap);
-}
-
-
-bool Texture::getTileCoords(TileIndex index, Scalar coords[8]) const
-{
- // 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];
- }
-
- return true;
- }
-
- return false;
-}
-
-
-bool Texture::getPath(std::string& name)
-{
- return Resource::getPath(name, "textures/", "png");
-}
-
-
-} // namespace Mf
-