]> Dogcows Code - chaz/yoink/blobdiff - src/moof/texture.cc
the massive refactoring effort
[chaz/yoink] / src / moof / texture.cc
diff --git a/src/moof/texture.cc b/src/moof/texture.cc
new file mode 100644 (file)
index 0000000..f8687f0
--- /dev/null
@@ -0,0 +1,409 @@
+
+/*]  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 <stdexcept>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/bind.hpp>
+
+#include "dispatcher.hh"
+#include "manager.hh"
+#include "log.hh"
+#include "opengl.hh"
+#include "script.hh"
+#include "texture.hh"
+#include "video.hh"
+
+
+namespace moof {
+
+
+/**
+ * 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 unload_from_gl()
+       {
+               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 context_recreated()
+       {
+               mObject = gObject = 0;
+               upload_to_gl();
+       }
+
+       /**
+        * 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 power_of_two(int input)
+       {
+               int value = 1;
+
+               while (value < input)
+               {
+                       value <<= 1;
+               }
+               return value;
+       }
+
+
+       static void bind_script_constants(script& script)
+       {
+               script::slot g = script.globals();
+
+               g.set_field("CLAMP",   GL_CLAMP);
+               g.set_field("REPEAT",  GL_REPEAT);
+               g.set_field("LINEAR",  GL_LINEAR);
+               g.set_field("NEAREST", GL_NEAREST);
+               g.set_field("LINEAR_MIPMAP_LINEAR",   GL_LINEAR_MIPMAP_LINEAR);
+               g.set_field("LINEAR_MIPMAP_NEAREST",  GL_LINEAR_MIPMAP_NEAREST);
+               g.set_field("NEAREST_MIPMAP_LINEAR",  GL_NEAREST_MIPMAP_LINEAR);
+               g.set_field("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
+               dispatcher& dispatcher = dispatcher::global();
+               mNewContextDispatch = dispatcher.add_target("video.newcontext",
+                                                       boost::bind(&impl::context_recreated, this));
+       }
+
+       ~impl()
+       {
+               unload_from_gl();
+       }
+
+
+       void init(const std::string& name)
+       {
+               std::string path(name);
+               
+               texture::find_path(path);
+
+               mImage = image::alloc(path);
+               if (!mImage->is_valid())
+               {
+                       throw std::runtime_error("texture not found: " + name);
+               }
+
+               mImage->flip();
+
+               script script;
+
+               bind_script_constants(script);
+               log::import(script);
+
+               if (script.do_string(mImage->comment()) != script::success)
+               {
+                       std::string str;
+                       script[-1].get(str);
+                       log_warning(str);
+               }
+               else
+               {
+                       log_info << "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 upload_to_gl()
+       {
+               if (mObject)
+               {
+                       // already loaded
+                       return;
+               }
+
+               glGenTextures(1, &mObject);
+               glBindTexture(GL_TEXTURE_2D, mObject);
+
+               glTexImage2D
+               //gluBuild2DMipmaps
+               (
+                       GL_TEXTURE_2D,
+                       0,
+                       mImage->mode(),
+                       //3,
+                       mImage->width(),
+                       mImage->height(),
+                       0,
+                       mImage->mode(),
+                       GL_UNSIGNED_BYTE,
+                       mImage->pixels()
+               );
+
+               set_properties();
+       }
+
+
+       /**
+        * Sets some texture properties such as the filters and external
+        * coordinate behavior.
+        */
+
+       void set_properties()
+       {
+               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);
+       }
+
+       void min_filter(GLuint filter)
+       {
+               bind();
+               mMinFilter = filter;
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mMinFilter);
+       }
+
+       void mag_filter(GLuint filter)
+       {
+               bind();
+               mMagFilter = filter;
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mMagFilter);
+       }
+
+       void wrap_s(GLuint wrap)
+       {
+               bind();
+               mWrapS = wrap;
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mWrapS);
+       }
+
+       void wrap_t(GLuint wrap)
+       {
+               bind();
+               mWrapT = wrap;
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mWrapT);
+       }
+
+
+       void bind()
+       {
+               if (mObject == 0)
+               {
+                       upload_to_gl();
+               }
+               if (mObject != gObject)
+               {
+                       glBindTexture(GL_TEXTURE_2D, mObject);
+                       gObject = mObject;
+               }
+       }
+
+
+       bool tile_coordinates(int index, scalar coords[8]) const
+       {
+               // make sure the index represents a real tile
+               if (index < 0 && 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;
+       }
+
+       image_ptr                       mImage;
+
+       GLuint                          mMinFilter;     ///< Minification filter.
+       GLuint                          mMagFilter;     ///< Magnification filter.
+       GLuint                          mWrapS;         ///< Wrapping behavior horizontally.
+       GLuint                          mWrapT;         ///< Wrapping behavior vertically.
+       int                                     mTilesS;
+       int                                     mTilesT;
+
+       GLuint                          mObject;        ///< GL texture handle.
+       static GLuint           gObject;        ///< Global GL texture handle.
+
+       dispatcher::handle      mNewContextDispatch;
+};
+
+GLuint texture::impl::gObject = 0;
+
+
+texture::texture(const std::string& name) : // FIXME: this is really weird
+       image(name),
+       // pass through
+       impl_(texture::impl::instance(name)) {}
+
+
+/**
+ * Bind the GL texture for mapping, etc.
+ */
+
+void texture::bind() const
+{
+       // pass through
+       impl_->bind();
+}
+
+
+/**
+ * Get the texture object, for the curious.
+ */
+
+GLuint texture::object() const
+{
+       // pass through
+       return impl_->mObject;
+}
+
+
+void texture::reset_binding()
+{
+       glBindTexture(GL_TEXTURE_2D, 0);
+       impl::gObject = 0;
+}
+
+
+void texture::min_filter(GLuint filter)
+{
+       // pass through
+       impl_->min_filter(filter);
+}
+
+void texture::mag_filter(GLuint filter)
+{
+       // pass through
+       impl_->mag_filter(filter);
+}
+
+void texture::wrap_s(GLuint wrap)
+{
+       // pass through
+       impl_->wrap_s(wrap);
+}
+
+void texture::wrap_t(GLuint wrap)
+{
+       // pass through
+       impl_->wrap_t(wrap);
+}
+
+
+bool texture::tile_coordinates(int index, scalar coords[8]) const
+{
+       // pass through
+       return impl_->tile_coordinates(index, coords);
+}
+
+bool texture::tile_coordinates(int index, scalar coords[8],
+               orientation orientation) const
+{
+       if (tile_coordinates(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::find_path(std::string& name)
+{
+       return resource::find_path(name, "textures/", "png");
+}
+
+
+} // namespace moof
+
This page took 0.027841 seconds and 4 git commands to generate.