X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2FMoof%2FTexture.cc;h=7f9f83c335b98eb7f8a39d4e02023ef0b6f68708;hp=e8f5d86d1f2e65fd85d413c5b93b26bfcedc6ac3;hb=299af4f2047e767e5d79501c26444473bda64c64;hpb=bfa6212d09d8735d8fd5e2638188e4a99f21ada4 diff --git a/src/Moof/Texture.cc b/src/Moof/Texture.cc index e8f5d86..7f9f83c 100644 --- a/src/Moof/Texture.cc +++ b/src/Moof/Texture.cc @@ -1,57 +1,44 @@ -/******************************************************************************* - - 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 // 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 // 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 { /** - * 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 Mippleton +class Texture::Impl : public Manager { /** @@ -60,33 +47,34 @@ class Texture::Impl : public Mippleton 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; } } /** - * 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() { - object_ = 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) @@ -101,26 +89,18 @@ class Texture::Impl : public Mippleton } - 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: @@ -129,60 +109,55 @@ public: * Construction is initialization. */ - explicit Impl(const std::string& name) : - Mippleton(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 + Video* video = Video::current(); + ASSERT(video && "should have a video context set"); // we want to know when the GL context is recreated - Dispatcher::instance().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 (surface_) - { - SDL_FreeSurface(surface_); - } - unloadFromGL(); - - Dispatcher::instance().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 ( @@ -228,50 +203,53 @@ public: 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()) { - 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"); - } + 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()) != Script::SUCCESS) { - mode_ = GL_RGBA; + std::string str; + script[-1].get(str); + logWarning(str); } else { - SDL_FreeSurface(temp); - throw Texture::Exception("incompatible color mode"); + 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"); } - - width_ = temp->w; - height_ = temp->h; - - surface_ = temp; } @@ -282,113 +260,132 @@ public: 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; } /** - * 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() { - 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; + } - 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. + ImageP mImage; - GLuint object_; ///< GL texture handle. - static GLuint globalObject_; ///< Global GL texture handle. + 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::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 - impl_(Texture::Impl::retain(name), &Texture::Impl::release) {} + mImpl(Texture::Impl::getInstance(name)) {} /** @@ -398,7 +395,7 @@ Texture::Texture(const std::string& name) : void Texture::bind() const { // pass through - impl_->bind(); + mImpl->bind(); } @@ -409,63 +406,82 @@ void Texture::bind() const 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) +bool Texture::getPath(std::string& name) { - std::string path = Resource::getPath("textures/" + name + ".png"); - return path; + return Resource::getPath(name, "textures/", "png"); } } // namespace Mf -/** vim: set ts=4 sw=4 tw=80: *************************************************/ -