X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2FMoof%2FTexture.cc;h=801acbae227f89d7a58970a1e97ed836a008391a;hp=44a0b423719d6dfbb10c1be2ae98630a97c6e05c;hb=e973a129b5b83b628ba3f09e8c95682fc74080cd;hpb=c2321281bf12a7efaedde930422c7ddbc92080d4 diff --git a/src/Moof/Texture.cc b/src/Moof/Texture.cc index 44a0b42..801acba 100644 --- a/src/Moof/Texture.cc +++ b/src/Moof/Texture.cc @@ -26,17 +26,20 @@ *******************************************************************************/ -#include +#include // FILE +#include // strncmp +#include #include -#include -#include - -#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 { @@ -47,11 +50,11 @@ 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::TextureImpl : public Mippleton +class Texture::Impl : public Manager { /** @@ -60,22 +63,27 @@ class Texture::TextureImpl : public Mippleton void unloadFromGL() { - if (object_) + if (mObject) { - glDeleteTextures(1, &object_); - object_ = 0; + if (mObject == gObject) + { + gObject = 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() { - unloadFromGL(); + mObject = gObject = 0; uploadToGL(); } @@ -95,35 +103,45 @@ class Texture::TextureImpl : public Mippleton return value; } + + static void bindScriptConstants(Mf::Script& script) + { + 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 TextureImpl(const std::string& name) : - Mippleton(name), - width_(0), - height_(0), - mode_(0), - minFilter_(GL_NEAREST), - maxFilter_(GL_NEAREST), - wrapU_(GL_CLAMP), - wrapV_(GL_CLAMP), - object_(0) + Impl() : + mMinFilter(GL_NEAREST), + mMagFilter(GL_NEAREST), + mWrapS(GL_CLAMP), + mWrapT(GL_CLAMP), + mTilesS(1), + mTilesT(1), + mObject(0) { - uploadToGL(); + // 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(&TextureImpl::contextRecreated, this, _1), this); + mDispatchHandler = core.addHandler("video.newcontext", + boost::bind(&Impl::contextRecreated, this)); } - ~TextureImpl() + ~Impl() { unloadFromGL(); - - Dispatcher::instance().removeHandler(this); } @@ -134,15 +152,21 @@ public: * 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 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 @@ -151,14 +175,14 @@ public: w, h, 32, #if SDL_BYTEORDER == SDL_LIL_ENDIAN - 0x000000FF, - 0x0000FF00, - 0x00FF0000, + 0x000000FF, + 0x0000FF00, + 0x00FF0000, 0xFF000000 #else 0xFF000000, - 0x00FF0000, - 0x0000FF00, + 0x00FF0000, + 0x0000FF00, 0x000000FF #endif ); @@ -187,29 +211,9 @@ public: 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(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; } + */ /** * Use SDL_image to load images from file. A surface with the image data is @@ -217,43 +221,55 @@ public: * @return Image data. */ - SDL_Surface* loadImageData() + void init(const std::string& name) { - SDL_Surface* surface; - - surface = IMG_Load(Texture::getPathToResource(getName()).c_str()); + std::string path = Texture::getPath(name); - if (!surface) + mImage = Image::alloc(path); + if (!mImage->isValid()) { - throw Texture::Exception("loading 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("image couldn't be prepared for GL"); - } + 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("image is not the required 24 or 32 bpp"); - } + 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); + + globals.pushField("min_filter"); + top.get(mMinFilter); + + globals.pushField("mag_filter"); + top.get(mMagFilter); - width_ = temp->w; - height_ = temp->h; + globals.pushField("wrap_s"); + top.get(mWrapS); - return temp; + globals.pushField("wrap_t"); + top.get(mWrapT); + } } @@ -264,33 +280,31 @@ public: void uploadToGL() { - if (object_) + if (mObject) { - // Already loaded. + // already loaded return; } - SDL_Surface* imageData = loadImageData(); - - 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, + mImage->getMode(), + //3, + mImage->getWidth(), + mImage->getHeight(), 0, - mode_, + mImage->getMode(), GL_UNSIGNED_BYTE, - imageData->pixels + mImage->getPixels() ); setProperties(); - - SDL_FreeSurface(imageData); } @@ -301,38 +315,108 @@ public: 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 != 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; - unsigned width_; ///< Horizontal dimension of the image. - unsigned height_; ///< Vertical dimension. + 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_; ///< Filter. - GLuint maxFilter_; ///< Filter. - GLuint wrapU_; ///< Wrapping behavior horizontally. - GLuint wrapV_; ///< Wrapping behavior vertically. - GLuint object_; ///< GL texture handle. + GLuint mObject; ///< GL texture handle. + static GLuint gObject; ///< Global GL texture handle. + + Dispatch::Handler mDispatchHandler; }; +GLuint Texture::Impl::gObject = 0; + Texture::Texture(const std::string& name) : + Image(Texture::getPath(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(); } @@ -340,59 +424,93 @@ void Texture::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::gObject = 0; } -unsigned Texture::getHeight() + +void Texture::setMinFilter(GLuint filter) { // pass through - return impl_->height_; + mImpl->setMinFilter(filter); } - -void Texture::setMinFilter(GLuint filter) +void Texture::setMagFilter(GLuint filter) { - impl_->minFilter_ = filter; + // pass through + mImpl->setMagFilter(filter); } -void Texture::setMaxFilter(GLuint filter) +void Texture::setWrapS(GLuint wrap) { - impl_->maxFilter_ = filter; + // pass through + mImpl->setWrapS(wrap); } -void Texture::setWrapU(GLuint wrap) +void Texture::setWrapT(GLuint wrap) { - impl_->wrapU_ = wrap; + // pass through + mImpl->setWrapT(wrap); } -void Texture::setWrapV(GLuint wrap) + +bool Texture::getTileCoords(TileIndex index, Scalar coords[8]) const { - impl_->wrapV_ = wrap; + // pass through + return mImpl->getTileCoords(index, coords); } - -void Texture::applyChanges() +bool Texture::getTileCoords(TileIndex index, Scalar coords[8], + Orientation orientation) const { - bind(); - impl_->setProperties(); + 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::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"); + if (boost::find_last(name, ".png")) + { + return Resource::getPath(name); + } + else + { + std::string path("textures/"); + path += name; + path += ".png"; + return Resource::getPath(path); + } }