Replace std::list to std::vector into tile.cpp (m_texture_trash) and move tile.hpp...
authorLoic Blot <loic.blot@unix-experience.fr>
Thu, 5 Mar 2015 10:52:57 +0000 (11:52 +0100)
committerLoic Blot <loic.blot@unix-experience.fr>
Thu, 5 Mar 2015 10:59:40 +0000 (11:59 +0100)
29 files changed:
build/android/jni/Android.mk
src/CMakeLists.txt
src/camera.h
src/client/CMakeLists.txt
src/client/tile.cpp [new file with mode: 0644]
src/client/tile.h [new file with mode: 0644]
src/content_cao.cpp
src/content_cso.cpp
src/content_mapblock.cpp
src/game.cpp
src/guiChatConsole.cpp
src/guiEngine.cpp
src/guiEngine.h
src/guiFormSpecMenu.cpp
src/guiTable.cpp
src/hud.cpp
src/itemdef.cpp
src/mapblock_mesh.h
src/nodedef.cpp
src/nodedef.h
src/particles.cpp
src/particles.h
src/shader.cpp
src/sky.cpp
src/subgame.cpp
src/tile.cpp [deleted file]
src/tile.h [deleted file]
src/touchscreengui.h
src/wieldmesh.cpp

index c3982c370f447136ad16ae4df1473a8c576a2713..a35084433cc7d7b2ecaa2a7d92e5050969828726 100644 (file)
@@ -196,7 +196,6 @@ LOCAL_SRC_FILES :=                                \
                jni/src/staticobject.cpp                  \
                jni/src/subgame.cpp                       \
                jni/src/test.cpp                          \
-               jni/src/tile.cpp                          \
                jni/src/tool.cpp                          \
                jni/src/treegen.cpp                       \
                jni/src/version.cpp                       \
@@ -214,7 +213,8 @@ LOCAL_SRC_FILES :=                                \
                jni/src/database-leveldb.cpp              \
                jni/src/settings.cpp                      \
                jni/src/wieldmesh.cpp                     \
-               jni/src/client/clientlauncher.cpp
+               jni/src/client/clientlauncher.cpp         \
+               jni/src/client/tile.cpp
 
 # Network
 LOCAL_SRC_FILES +=                                \
index 6aa1a35a17cc1d82eebc7f01997815159d3b181a..d56ec18c0539ac43ef135d5673b6104e0598fb6c 100644 (file)
@@ -499,7 +499,6 @@ set(minetest_SRCS
        particles.cpp
        shader.cpp
        sky.cpp
-       tile.cpp
        wieldmesh.cpp
        ${minetest_SCRIPT_SRCS}
 )
index 3f10b87d7887ffd26cd74627ab88a915b9ecd870..b5373886087b6e5d6ea2e99a661f4cd9beeae920 100644 (file)
@@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "irrlichttypes_extrabloated.h"
 #include "inventory.h"
 #include "mesh.h"
-#include "tile.h"
+#include "client/tile.h"
 #include "util/numeric.h"
 #include <ICameraSceneNode.h>
 
index 8cd981aca96294b19bfba94944e679103a6e8c8c..288acf14cb19e7603b300000861eb1bf11728eb2 100644 (file)
@@ -1,4 +1,5 @@
 set(client_SRCS
        ${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp
        PARENT_SCOPE
-)
\ No newline at end of file
+)
diff --git a/src/client/tile.cpp b/src/client/tile.cpp
new file mode 100644 (file)
index 0000000..e5d02de
--- /dev/null
@@ -0,0 +1,1954 @@
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "tile.h"
+
+#include <ICameraSceneNode.h>
+#include "util/string.h"
+#include "util/container.h"
+#include "util/thread.h"
+#include "util/numeric.h"
+#include "irrlichttypes_extrabloated.h"
+#include "debug.h"
+#include "main.h" // for g_settings
+#include "filesys.h"
+#include "settings.h"
+#include "mesh.h"
+#include "log.h"
+#include "gamedef.h"
+#include "strfnd.h"
+#include "util/string.h" // for parseColorString()
+
+#ifdef __ANDROID__
+#include <GLES/gl.h>
+#endif
+
+/*
+       A cache from texture name to texture path
+*/
+MutexedMap<std::string, std::string> g_texturename_to_path_cache;
+
+/*
+       Replaces the filename extension.
+       eg:
+               std::string image = "a/image.png"
+               replace_ext(image, "jpg")
+               -> image = "a/image.jpg"
+       Returns true on success.
+*/
+static bool replace_ext(std::string &path, const char *ext)
+{
+       if (ext == NULL)
+               return false;
+       // Find place of last dot, fail if \ or / found.
+       s32 last_dot_i = -1;
+       for (s32 i=path.size()-1; i>=0; i--)
+       {
+               if (path[i] == '.')
+               {
+                       last_dot_i = i;
+                       break;
+               }
+
+               if (path[i] == '\\' || path[i] == '/')
+                       break;
+       }
+       // If not found, return an empty string
+       if (last_dot_i == -1)
+               return false;
+       // Else make the new path
+       path = path.substr(0, last_dot_i+1) + ext;
+       return true;
+}
+
+/*
+       Find out the full path of an image by trying different filename
+       extensions.
+
+       If failed, return "".
+*/
+std::string getImagePath(std::string path)
+{
+       // A NULL-ended list of possible image extensions
+       const char *extensions[] = {
+               "png", "jpg", "bmp", "tga",
+               "pcx", "ppm", "psd", "wal", "rgb",
+               NULL
+       };
+       // If there is no extension, add one
+       if (removeStringEnd(path, extensions) == "")
+               path = path + ".png";
+       // Check paths until something is found to exist
+       const char **ext = extensions;
+       do{
+               bool r = replace_ext(path, *ext);
+               if (r == false)
+                       return "";
+               if (fs::PathExists(path))
+                       return path;
+       }
+       while((++ext) != NULL);
+
+       return "";
+}
+
+/*
+       Gets the path to a texture by first checking if the texture exists
+       in texture_path and if not, using the data path.
+
+       Checks all supported extensions by replacing the original extension.
+
+       If not found, returns "".
+
+       Utilizes a thread-safe cache.
+*/
+std::string getTexturePath(const std::string &filename)
+{
+       std::string fullpath = "";
+       /*
+               Check from cache
+       */
+       bool incache = g_texturename_to_path_cache.get(filename, &fullpath);
+       if (incache)
+               return fullpath;
+
+       /*
+               Check from texture_path
+       */
+       std::string texture_path = g_settings->get("texture_path");
+       if (texture_path != "")
+       {
+               std::string testpath = texture_path + DIR_DELIM + filename;
+               // Check all filename extensions. Returns "" if not found.
+               fullpath = getImagePath(testpath);
+       }
+
+       /*
+               Check from default data directory
+       */
+       if (fullpath == "")
+       {
+               std::string base_path = porting::path_share + DIR_DELIM + "textures"
+                               + DIR_DELIM + "base" + DIR_DELIM + "pack";
+               std::string testpath = base_path + DIR_DELIM + filename;
+               // Check all filename extensions. Returns "" if not found.
+               fullpath = getImagePath(testpath);
+       }
+
+       // Add to cache (also an empty result is cached)
+       g_texturename_to_path_cache.set(filename, fullpath);
+
+       // Finally return it
+       return fullpath;
+}
+
+void clearTextureNameCache()
+{
+       g_texturename_to_path_cache.clear();
+}
+
+/*
+       Stores internal information about a texture.
+*/
+
+struct TextureInfo
+{
+       std::string name;
+       video::ITexture *texture;
+
+       TextureInfo(
+                       const std::string &name_,
+                       video::ITexture *texture_=NULL
+               ):
+               name(name_),
+               texture(texture_)
+       {
+       }
+};
+
+/*
+       SourceImageCache: A cache used for storing source images.
+*/
+
+class SourceImageCache
+{
+public:
+       ~SourceImageCache() {
+               for (std::map<std::string, video::IImage*>::iterator iter = m_images.begin();
+                               iter != m_images.end(); iter++) {
+                       iter->second->drop();
+               }
+               m_images.clear();
+       }
+       void insert(const std::string &name, video::IImage *img,
+                       bool prefer_local, video::IVideoDriver *driver)
+       {
+               assert(img);
+               // Remove old image
+               std::map<std::string, video::IImage*>::iterator n;
+               n = m_images.find(name);
+               if (n != m_images.end()){
+                       if (n->second)
+                               n->second->drop();
+               }
+
+               video::IImage* toadd = img;
+               bool need_to_grab = true;
+
+               // Try to use local texture instead if asked to
+               if (prefer_local){
+                       std::string path = getTexturePath(name);
+                       if (path != ""){
+                               video::IImage *img2 = driver->createImageFromFile(path.c_str());
+                               if (img2){
+                                       toadd = img2;
+                                       need_to_grab = false;
+                               }
+                       }
+               }
+
+               if (need_to_grab)
+                       toadd->grab();
+               m_images[name] = toadd;
+       }
+       video::IImage* get(const std::string &name)
+       {
+               std::map<std::string, video::IImage*>::iterator n;
+               n = m_images.find(name);
+               if (n != m_images.end())
+                       return n->second;
+               return NULL;
+       }
+       // Primarily fetches from cache, secondarily tries to read from filesystem
+       video::IImage* getOrLoad(const std::string &name, IrrlichtDevice *device)
+       {
+               std::map<std::string, video::IImage*>::iterator n;
+               n = m_images.find(name);
+               if (n != m_images.end()){
+                       n->second->grab(); // Grab for caller
+                       return n->second;
+               }
+               video::IVideoDriver* driver = device->getVideoDriver();
+               std::string path = getTexturePath(name);
+               if (path == ""){
+                       infostream<<"SourceImageCache::getOrLoad(): No path found for \""
+                                       <<name<<"\""<<std::endl;
+                       return NULL;
+               }
+               infostream<<"SourceImageCache::getOrLoad(): Loading path \""<<path
+                               <<"\""<<std::endl;
+               video::IImage *img = driver->createImageFromFile(path.c_str());
+
+               if (img){
+                       m_images[name] = img;
+                       img->grab(); // Grab for caller
+               }
+               return img;
+       }
+private:
+       std::map<std::string, video::IImage*> m_images;
+};
+
+/*
+       TextureSource
+*/
+
+class TextureSource : public IWritableTextureSource
+{
+public:
+       TextureSource(IrrlichtDevice *device);
+       virtual ~TextureSource();
+
+       /*
+               Example case:
+               Now, assume a texture with the id 1 exists, and has the name
+               "stone.png^mineral1".
+               Then a random thread calls getTextureId for a texture called
+               "stone.png^mineral1^crack0".
+               ...Now, WTF should happen? Well:
+               - getTextureId strips off stuff recursively from the end until
+                 the remaining part is found, or nothing is left when
+                 something is stripped out
+
+               But it is slow to search for textures by names and modify them
+               like that?
+               - ContentFeatures is made to contain ids for the basic plain
+                 textures
+               - Crack textures can be slow by themselves, but the framework
+                 must be fast.
+
+               Example case #2:
+               - Assume a texture with the id 1 exists, and has the name
+                 "stone.png^mineral_coal.png".
+               - Now getNodeTile() stumbles upon a node which uses
+                 texture id 1, and determines that MATERIAL_FLAG_CRACK
+                 must be applied to the tile
+               - MapBlockMesh::animate() finds the MATERIAL_FLAG_CRACK and
+                 has received the current crack level 0 from the client. It
+                 finds out the name of the texture with getTextureName(1),
+                 appends "^crack0" to it and gets a new texture id with
+                 getTextureId("stone.png^mineral_coal.png^crack0").
+
+       */
+
+       /*
+               Gets a texture id from cache or
+               - if main thread, generates the texture, adds to cache and returns id.
+               - if other thread, adds to request queue and waits for main thread.
+
+               The id 0 points to a NULL texture. It is returned in case of error.
+       */
+       u32 getTextureId(const std::string &name);
+
+       // Finds out the name of a cached texture.
+       std::string getTextureName(u32 id);
+
+       /*
+               If texture specified by the name pointed by the id doesn't
+               exist, create it, then return the cached texture.
+
+               Can be called from any thread. If called from some other thread
+               and not found in cache, the call is queued to the main thread
+               for processing.
+       */
+       video::ITexture* getTexture(u32 id);
+
+       video::ITexture* getTexture(const std::string &name, u32 *id);
+
+       // Returns a pointer to the irrlicht device
+       virtual IrrlichtDevice* getDevice()
+       {
+               return m_device;
+       }
+
+       bool isKnownSourceImage(const std::string &name)
+       {
+               bool is_known = false;
+               bool cache_found = m_source_image_existence.get(name, &is_known);
+               if (cache_found)
+                       return is_known;
+               // Not found in cache; find out if a local file exists
+               is_known = (getTexturePath(name) != "");
+               m_source_image_existence.set(name, is_known);
+               return is_known;
+       }
+
+       // Processes queued texture requests from other threads.
+       // Shall be called from the main thread.
+       void processQueue();
+
+       // Insert an image into the cache without touching the filesystem.
+       // Shall be called from the main thread.
+       void insertSourceImage(const std::string &name, video::IImage *img);
+
+       // Rebuild images and textures from the current set of source images
+       // Shall be called from the main thread.
+       void rebuildImagesAndTextures();
+
+       // Render a mesh to a texture.
+       // Returns NULL if render-to-texture failed.
+       // Shall be called from the main thread.
+       video::ITexture* generateTextureFromMesh(
+                       const TextureFromMeshParams &params);
+
+       // Generates an image from a full string like
+       // "stone.png^mineral_coal.png^[crack:1:0".
+       // Shall be called from the main thread.
+       video::IImage* generateImage(const std::string &name);
+
+       video::ITexture* getNormalTexture(const std::string &name);
+private:
+
+       // The id of the thread that is allowed to use irrlicht directly
+       threadid_t m_main_thread;
+       // The irrlicht device
+       IrrlichtDevice *m_device;
+
+       // Cache of source images
+       // This should be only accessed from the main thread
+       SourceImageCache m_sourcecache;
+
+       // Generate a texture
+       u32 generateTexture(const std::string &name);
+
+       // Generate image based on a string like "stone.png" or "[crack:1:0".
+       // if baseimg is NULL, it is created. Otherwise stuff is made on it.
+       bool generateImagePart(std::string part_of_name, video::IImage *& baseimg);
+
+       // Thread-safe cache of what source images are known (true = known)
+       MutexedMap<std::string, bool> m_source_image_existence;
+
+       // A texture id is index in this array.
+       // The first position contains a NULL texture.
+       std::vector<TextureInfo> m_textureinfo_cache;
+       // Maps a texture name to an index in the former.
+       std::map<std::string, u32> m_name_to_id;
+       // The two former containers are behind this mutex
+       JMutex m_textureinfo_cache_mutex;
+
+       // Queued texture fetches (to be processed by the main thread)
+       RequestQueue<std::string, u32, u8, u8> m_get_texture_queue;
+
+       // Textures that have been overwritten with other ones
+       // but can't be deleted because the ITexture* might still be used
+       std::vector<video::ITexture*> m_texture_trash;
+
+       // Cached settings needed for making textures from meshes
+       bool m_setting_trilinear_filter;
+       bool m_setting_bilinear_filter;
+       bool m_setting_anisotropic_filter;
+};
+
+IWritableTextureSource* createTextureSource(IrrlichtDevice *device)
+{
+       return new TextureSource(device);
+}
+
+TextureSource::TextureSource(IrrlichtDevice *device):
+               m_device(device)
+{
+       assert(m_device);
+
+       m_main_thread = get_current_thread_id();
+
+       // Add a NULL TextureInfo as the first index, named ""
+       m_textureinfo_cache.push_back(TextureInfo(""));
+       m_name_to_id[""] = 0;
+
+       // Cache some settings
+       // Note: Since this is only done once, the game must be restarted
+       // for these settings to take effect
+       m_setting_trilinear_filter = g_settings->getBool("trilinear_filter");
+       m_setting_bilinear_filter = g_settings->getBool("bilinear_filter");
+       m_setting_anisotropic_filter = g_settings->getBool("anisotropic_filter");
+}
+
+TextureSource::~TextureSource()
+{
+       video::IVideoDriver* driver = m_device->getVideoDriver();
+
+       unsigned int textures_before = driver->getTextureCount();
+
+       for (std::vector<TextureInfo>::iterator iter =
+                       m_textureinfo_cache.begin();
+                       iter != m_textureinfo_cache.end(); iter++)
+       {
+               //cleanup texture
+               if (iter->texture)
+                       driver->removeTexture(iter->texture);
+       }
+       m_textureinfo_cache.clear();
+
+       for (std::vector<video::ITexture*>::iterator iter =
+                       m_texture_trash.begin(); iter != m_texture_trash.end();
+                       iter++) {
+               video::ITexture *t = *iter;
+
+               //cleanup trashed texture
+               driver->removeTexture(t);
+       }
+
+       infostream << "~TextureSource() "<< textures_before << "/"
+                       << driver->getTextureCount() << std::endl;
+}
+
+u32 TextureSource::getTextureId(const std::string &name)
+{
+       //infostream<<"getTextureId(): \""<<name<<"\""<<std::endl;
+
+       {
+               /*
+                       See if texture already exists
+               */
+               JMutexAutoLock lock(m_textureinfo_cache_mutex);
+               std::map<std::string, u32>::iterator n;
+               n = m_name_to_id.find(name);
+               if (n != m_name_to_id.end())
+               {
+                       return n->second;
+               }
+       }
+
+       /*
+               Get texture
+       */
+       if (get_current_thread_id() == m_main_thread)
+       {
+               return generateTexture(name);
+       }
+       else
+       {
+               infostream<<"getTextureId(): Queued: name=\""<<name<<"\""<<std::endl;
+
+               // We're gonna ask the result to be put into here
+               static ResultQueue<std::string, u32, u8, u8> result_queue;
+
+               // Throw a request in
+               m_get_texture_queue.add(name, 0, 0, &result_queue);
+
+               /*infostream<<"Waiting for texture from main thread, name=\""
+                               <<name<<"\""<<std::endl;*/
+
+               try
+               {
+                       while(true) {
+                               // Wait result for a second
+                               GetResult<std::string, u32, u8, u8>
+                                       result = result_queue.pop_front(1000);
+
+                               if (result.key == name) {
+                                       return result.item;
+                               }
+                       }
+               }
+               catch(ItemNotFoundException &e)
+               {
+                       errorstream<<"Waiting for texture " << name << " timed out."<<std::endl;
+                       return 0;
+               }
+       }
+
+       infostream<<"getTextureId(): Failed"<<std::endl;
+
+       return 0;
+}
+
+// Draw an image on top of an another one, using the alpha channel of the
+// source image
+static void blit_with_alpha(video::IImage *src, video::IImage *dst,
+               v2s32 src_pos, v2s32 dst_pos, v2u32 size);
+
+// Like blit_with_alpha, but only modifies destination pixels that
+// are fully opaque
+static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
+               v2s32 src_pos, v2s32 dst_pos, v2u32 size);
+
+// Like blit_with_alpha overlay, but uses an int to calculate the ratio
+// and modifies any destination pixels that are not fully transparent
+static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst,
+               v2s32 src_pos, v2s32 dst_pos, v2u32 size, int ratio);
+
+// Apply a mask to an image
+static void apply_mask(video::IImage *mask, video::IImage *dst,
+               v2s32 mask_pos, v2s32 dst_pos, v2u32 size);
+
+// Draw or overlay a crack
+static void draw_crack(video::IImage *crack, video::IImage *dst,
+               bool use_overlay, s32 frame_count, s32 progression,
+               video::IVideoDriver *driver);
+
+// Brighten image
+void brighten(video::IImage *image);
+// Parse a transform name
+u32 parseImageTransform(const std::string& s);
+// Apply transform to image dimension
+core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim);
+// Apply transform to image data
+void imageTransform(u32 transform, video::IImage *src, video::IImage *dst);
+
+/*
+       This method generates all the textures
+*/
+u32 TextureSource::generateTexture(const std::string &name)
+{
+       //infostream << "generateTexture(): name=\"" << name << "\"" << std::endl;
+
+       // Empty name means texture 0
+       if (name == "") {
+               infostream<<"generateTexture(): name is empty"<<std::endl;
+               return 0;
+       }
+
+       {
+               /*
+                       See if texture already exists
+               */
+               JMutexAutoLock lock(m_textureinfo_cache_mutex);
+               std::map<std::string, u32>::iterator n;
+               n = m_name_to_id.find(name);
+               if (n != m_name_to_id.end()) {
+                       return n->second;
+               }
+       }
+
+       /*
+               Calling only allowed from main thread
+       */
+       if (get_current_thread_id() != m_main_thread) {
+               errorstream<<"TextureSource::generateTexture() "
+                               "called not from main thread"<<std::endl;
+               return 0;
+       }
+
+       video::IVideoDriver *driver = m_device->getVideoDriver();
+       assert(driver);
+
+       video::IImage *img = generateImage(name);
+
+       video::ITexture *tex = NULL;
+
+       if (img != NULL) {
+#ifdef __ANDROID__
+               img = Align2Npot2(img, driver);
+#endif
+               // Create texture from resulting image
+               tex = driver->addTexture(name.c_str(), img);
+               img->drop();
+       }
+
+       /*
+               Add texture to caches (add NULL textures too)
+       */
+
+       JMutexAutoLock lock(m_textureinfo_cache_mutex);
+
+       u32 id = m_textureinfo_cache.size();
+       TextureInfo ti(name, tex);
+       m_textureinfo_cache.push_back(ti);
+       m_name_to_id[name] = id;
+
+       return id;
+}
+
+std::string TextureSource::getTextureName(u32 id)
+{
+       JMutexAutoLock lock(m_textureinfo_cache_mutex);
+
+       if (id >= m_textureinfo_cache.size())
+       {
+               errorstream<<"TextureSource::getTextureName(): id="<<id
+                               <<" >= m_textureinfo_cache.size()="
+                               <<m_textureinfo_cache.size()<<std::endl;
+               return "";
+       }
+
+       return m_textureinfo_cache[id].name;
+}
+
+video::ITexture* TextureSource::getTexture(u32 id)
+{
+       JMutexAutoLock lock(m_textureinfo_cache_mutex);
+
+       if (id >= m_textureinfo_cache.size())
+               return NULL;
+
+       return m_textureinfo_cache[id].texture;
+}
+
+video::ITexture* TextureSource::getTexture(const std::string &name, u32 *id)
+{
+       u32 actual_id = getTextureId(name);
+       if (id){
+               *id = actual_id;
+       }
+       return getTexture(actual_id);
+}
+
+void TextureSource::processQueue()
+{
+       /*
+               Fetch textures
+       */
+       //NOTE this is only thread safe for ONE consumer thread!
+       if (!m_get_texture_queue.empty())
+       {
+               GetRequest<std::string, u32, u8, u8>
+                               request = m_get_texture_queue.pop();
+
+               /*infostream<<"TextureSource::processQueue(): "
+                               <<"got texture request with "
+                               <<"name=\""<<request.key<<"\""
+                               <<std::endl;*/
+
+               m_get_texture_queue.pushResult(request, generateTexture(request.key));
+       }
+}
+
+void TextureSource::insertSourceImage(const std::string &name, video::IImage *img)
+{
+       //infostream<<"TextureSource::insertSourceImage(): name="<<name<<std::endl;
+
+       assert(get_current_thread_id() == m_main_thread);
+
+       m_sourcecache.insert(name, img, true, m_device->getVideoDriver());
+       m_source_image_existence.set(name, true);
+}
+
+void TextureSource::rebuildImagesAndTextures()
+{
+       JMutexAutoLock lock(m_textureinfo_cache_mutex);
+
+       video::IVideoDriver* driver = m_device->getVideoDriver();
+       assert(driver != 0);
+
+       // Recreate textures
+       for (u32 i=0; i<m_textureinfo_cache.size(); i++){
+               TextureInfo *ti = &m_textureinfo_cache[i];
+               video::IImage *img = generateImage(ti->name);
+#ifdef __ANDROID__
+               img = Align2Npot2(img, driver);
+               assert(img->getDimension().Height == npot2(img->getDimension().Height));
+               assert(img->getDimension().Width == npot2(img->getDimension().Width));
+#endif
+               // Create texture from resulting image
+               video::ITexture *t = NULL;
+               if (img) {
+                       t = driver->addTexture(ti->name.c_str(), img);
+                       img->drop();
+               }
+               video::ITexture *t_old = ti->texture;
+               // Replace texture
+               ti->texture = t;
+
+               if (t_old)
+                       m_texture_trash.push_back(t_old);
+       }
+}
+
+video::ITexture* TextureSource::generateTextureFromMesh(
+               const TextureFromMeshParams &params)
+{
+       video::IVideoDriver *driver = m_device->getVideoDriver();
+       assert(driver);
+
+#ifdef __ANDROID__
+       const GLubyte* renderstr = glGetString(GL_RENDERER);
+       std::string renderer((char*) renderstr);
+
+       // use no render to texture hack
+       if (
+               (renderer.find("Adreno") != std::string::npos) ||
+               (renderer.find("Mali") != std::string::npos) ||
+               (renderer.find("Immersion") != std::string::npos) ||
+               (renderer.find("Tegra") != std::string::npos) ||
+               g_settings->getBool("inventory_image_hack")
+               ) {
+               // Get a scene manager
+               scene::ISceneManager *smgr_main = m_device->getSceneManager();
+               assert(smgr_main);
+               scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
+               assert(smgr);
+
+               const float scaling = 0.2;
+
+               scene::IMeshSceneNode* meshnode =
+                               smgr->addMeshSceneNode(params.mesh, NULL,
+                                               -1, v3f(0,0,0), v3f(0,0,0),
+                                               v3f(1.0 * scaling,1.0 * scaling,1.0 * scaling), true);
+               meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
+               meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
+               meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter);
+               meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter);
+               meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter);
+
+               scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
+                               params.camera_position, params.camera_lookat);
+               // second parameter of setProjectionMatrix (isOrthogonal) is ignored
+               camera->setProjectionMatrix(params.camera_projection_matrix, false);
+
+               smgr->setAmbientLight(params.ambient_light);
+               smgr->addLightSceneNode(0,
+                               params.light_position,
+                               params.light_color,
+                               params.light_radius*scaling);
+
+               core::dimension2d<u32> screen = driver->getScreenSize();
+
+               // Render scene
+               driver->beginScene(true, true, video::SColor(0,0,0,0));
+               driver->clearZBuffer();
+               smgr->drawAll();
+
+               core::dimension2d<u32> partsize(screen.Width * scaling,screen.Height * scaling);
+
+               irr::video::IImage* rawImage =
+                               driver->createImage(irr::video::ECF_A8R8G8B8, partsize);
+
+               u8* pixels = static_cast<u8*>(rawImage->lock());
+               if (!pixels)
+               {
+                       rawImage->drop();
+                       return NULL;
+               }
+
+               core::rect<s32> source(
+                               screen.Width /2 - (screen.Width  * (scaling / 2)),
+                               screen.Height/2 - (screen.Height * (scaling / 2)),
+                               screen.Width /2 + (screen.Width  * (scaling / 2)),
+                               screen.Height/2 + (screen.Height * (scaling / 2))
+                       );
+
+               glReadPixels(source.UpperLeftCorner.X, source.UpperLeftCorner.Y,
+                               partsize.Width, partsize.Height, GL_RGBA,
+                               GL_UNSIGNED_BYTE, pixels);
+
+               driver->endScene();
+
+               // Drop scene manager
+               smgr->drop();
+
+               unsigned int pixelcount = partsize.Width*partsize.Height;
+
+               u8* runptr = pixels;
+               for (unsigned int i=0; i < pixelcount; i++) {
+
+                       u8 B = *runptr;
+                       u8 G = *(runptr+1);
+                       u8 R = *(runptr+2);
+                       u8 A = *(runptr+3);
+
+                       //BGRA -> RGBA
+                       *runptr = R;
+                       runptr ++;
+                       *runptr = G;
+                       runptr ++;
+                       *runptr = B;
+                       runptr ++;
+                       *runptr = A;
+                       runptr ++;
+               }
+
+               video::IImage* inventory_image =
+                               driver->createImage(irr::video::ECF_A8R8G8B8, params.dim);
+
+               rawImage->copyToScaling(inventory_image);
+               rawImage->drop();
+
+               video::ITexture *rtt = driver->addTexture(params.rtt_texture_name.c_str(), inventory_image);
+               inventory_image->drop();
+
+               if (rtt == NULL) {
+                       errorstream << "TextureSource::generateTextureFromMesh(): failed to recreate texture from image: " << params.rtt_texture_name << std::endl;
+                       return NULL;
+               }
+
+               driver->makeColorKeyTexture(rtt, v2s32(0,0));
+
+               if (params.delete_texture_on_shutdown)
+                       m_texture_trash.push_back(rtt);
+
+               return rtt;
+       }
+#endif
+
+       if (driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
+       {
+               static bool warned = false;
+               if (!warned)
+               {
+                       errorstream<<"TextureSource::generateTextureFromMesh(): "
+                               <<"EVDF_RENDER_TO_TARGET not supported."<<std::endl;
+                       warned = true;
+               }
+               return NULL;
+       }
+
+       // Create render target texture
+       video::ITexture *rtt = driver->addRenderTargetTexture(
+                       params.dim, params.rtt_texture_name.c_str(),
+                       video::ECF_A8R8G8B8);
+       if (rtt == NULL)
+       {
+               errorstream<<"TextureSource::generateTextureFromMesh(): "
+                       <<"addRenderTargetTexture returned NULL."<<std::endl;
+               return NULL;
+       }
+
+       // Set render target
+       if (!driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0))) {
+               driver->removeTexture(rtt);
+               errorstream<<"TextureSource::generateTextureFromMesh(): "
+                       <<"failed to set render target"<<std::endl;
+               return NULL;
+       }
+
+       // Get a scene manager
+       scene::ISceneManager *smgr_main = m_device->getSceneManager();
+       assert(smgr_main);
+       scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
+       assert(smgr);
+
+       scene::IMeshSceneNode* meshnode =
+                       smgr->addMeshSceneNode(params.mesh, NULL,
+                                       -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true);
+       meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
+       meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
+       meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter);
+       meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter);
+       meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter);
+
+       scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
+                       params.camera_position, params.camera_lookat);
+       // second parameter of setProjectionMatrix (isOrthogonal) is ignored
+       camera->setProjectionMatrix(params.camera_projection_matrix, false);
+
+       smgr->setAmbientLight(params.ambient_light);
+       smgr->addLightSceneNode(0,
+                       params.light_position,
+                       params.light_color,
+                       params.light_radius);
+
+       // Render scene
+       driver->beginScene(true, true, video::SColor(0,0,0,0));
+       smgr->drawAll();
+       driver->endScene();
+
+       // Drop scene manager
+       smgr->drop();
+
+       // Unset render target
+       driver->setRenderTarget(0, false, true, 0);
+
+       if (params.delete_texture_on_shutdown)
+               m_texture_trash.push_back(rtt);
+
+       return rtt;
+}
+
+video::IImage* TextureSource::generateImage(const std::string &name)
+{
+       /*
+               Get the base image
+       */
+
+       const char separator = '^';
+       const char paren_open = '(';
+       const char paren_close = ')';
+
+       // Find last separator in the name
+       s32 last_separator_pos = -1;
+       u8 paren_bal = 0;
+       for (s32 i = name.size() - 1; i >= 0; i--) {
+               switch(name[i]) {
+               case separator:
+                       if (paren_bal == 0) {
+                               last_separator_pos = i;
+                               i = -1; // break out of loop
+                       }
+                       break;
+               case paren_open:
+                       if (paren_bal == 0) {
+                               errorstream << "generateImage(): unbalanced parentheses"
+                                               << "(extranous '(') while generating texture \""
+                                               << name << "\"" << std::endl;
+                               return NULL;
+                       }
+                       paren_bal--;
+                       break;
+               case paren_close:
+                       paren_bal++;
+                       break;
+               default:
+                       break;
+               }
+       }
+       if (paren_bal > 0) {
+               errorstream << "generateImage(): unbalanced parentheses"
+                               << "(missing matching '(') while generating texture \""
+                               << name << "\"" << std::endl;
+               return NULL;
+       }
+
+
+       video::IImage *baseimg = NULL;
+
+       /*
+               If separator was found, make the base image
+               using a recursive call.
+       */
+       if (last_separator_pos != -1) {
+               baseimg = generateImage(name.substr(0, last_separator_pos));
+       }
+
+
+       video::IVideoDriver* driver = m_device->getVideoDriver();
+       assert(driver);
+
+       /*
+               Parse out the last part of the name of the image and act
+               according to it
+       */
+
+       std::string last_part_of_name = name.substr(last_separator_pos + 1);
+
+       /* 
+               If this name is enclosed in parentheses, generate it
+               and blit it onto the base image
+       */
+       if (last_part_of_name[0] == paren_open
+                       && last_part_of_name[last_part_of_name.size() - 1] == paren_close) {
+               std::string name2 = last_part_of_name.substr(1,
+                               last_part_of_name.size() - 2);
+               video::IImage *tmp = generateImage(name2);
+               if (!tmp) {
+                       errorstream << "generateImage(): "
+                               "Failed to generate \"" << name2 << "\""
+                               << std::endl;
+                       return NULL;
+               }
+               core::dimension2d<u32> dim = tmp->getDimension();
+               if (!baseimg)
+                       baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
+               blit_with_alpha(tmp, baseimg, v2s32(0, 0), v2s32(0, 0), dim);
+               tmp->drop();
+       } else if (!generateImagePart(last_part_of_name, baseimg)) {
+               // Generate image according to part of name
+               errorstream << "generateImage(): "
+                               "Failed to generate \"" << last_part_of_name << "\""
+                               << std::endl;
+       }
+
+       // If no resulting image, print a warning
+       if (baseimg == NULL) {
+               errorstream << "generateImage(): baseimg is NULL (attempted to"
+                               " create texture \"" << name << "\")" << std::endl;
+       }
+
+       return baseimg;
+}
+
+#ifdef __ANDROID__
+#include <GLES/gl.h>
+/**
+ * Check and align image to npot2 if required by hardware
+ * @param image image to check for npot2 alignment
+ * @param driver driver to use for image operations
+ * @return image or copy of image aligned to npot2
+ */
+video::IImage * Align2Npot2(video::IImage * image,
+               video::IVideoDriver* driver)
+{
+       if (image == NULL) {
+               return image;
+       }
+
+       core::dimension2d<u32> dim = image->getDimension();
+
+       std::string extensions = (char*) glGetString(GL_EXTENSIONS);
+       if (extensions.find("GL_OES_texture_npot") != std::string::npos) {
+               return image;
+       }
+
+       unsigned int height = npot2(dim.Height);
+       unsigned int width  = npot2(dim.Width);
+
+       if ((dim.Height == height) &&
+                       (dim.Width == width)) {
+               return image;
+       }
+
+       if (dim.Height > height) {
+               height *= 2;
+       }
+
+       if (dim.Width > width) {
+               width *= 2;
+       }
+
+       video::IImage *targetimage =
+                       driver->createImage(video::ECF_A8R8G8B8,
+                                       core::dimension2d<u32>(width, height));
+
+       if (targetimage != NULL) {
+               image->copyToScaling(targetimage);
+       }
+       image->drop();
+       return targetimage;
+}
+
+#endif
+
+bool TextureSource::generateImagePart(std::string part_of_name,
+               video::IImage *& baseimg)
+{
+       video::IVideoDriver* driver = m_device->getVideoDriver();
+       assert(driver);
+
+       // Stuff starting with [ are special commands
+       if (part_of_name.size() == 0 || part_of_name[0] != '[')
+       {
+               video::IImage *image = m_sourcecache.getOrLoad(part_of_name, m_device);
+#ifdef __ANDROID__
+               image = Align2Npot2(image, driver);
+#endif
+               if (image == NULL) {
+                       if (part_of_name != "") {
+                               if (part_of_name.find("_normal.png") == std::string::npos){
+                                       errorstream<<"generateImage(): Could not load image \""
+                                               <<part_of_name<<"\""<<" while building texture"<<std::endl;
+                                       errorstream<<"generateImage(): Creating a dummy"
+                                               <<" image for \""<<part_of_name<<"\""<<std::endl;
+                               } else {
+                                       infostream<<"generateImage(): Could not load normal map \""
+                                               <<part_of_name<<"\""<<std::endl;
+                                       infostream<<"generateImage(): Creating a dummy"
+                                               <<" normal map for \""<<part_of_name<<"\""<<std::endl;
+                               }
+                       }
+
+                       // Just create a dummy image
+                       //core::dimension2d<u32> dim(2,2);
+                       core::dimension2d<u32> dim(1,1);
+                       image = driver->createImage(video::ECF_A8R8G8B8, dim);
+                       assert(image);
+                       /*image->setPixel(0,0, video::SColor(255,255,0,0));
+                       image->setPixel(1,0, video::SColor(255,0,255,0));
+                       image->setPixel(0,1, video::SColor(255,0,0,255));
+                       image->setPixel(1,1, video::SColor(255,255,0,255));*/
+                       image->setPixel(0,0, video::SColor(255,myrand()%256,
+                                       myrand()%256,myrand()%256));
+                       /*image->setPixel(1,0, video::SColor(255,myrand()%256,
+                                       myrand()%256,myrand()%256));
+                       image->setPixel(0,1, video::SColor(255,myrand()%256,
+                                       myrand()%256,myrand()%256));
+                       image->setPixel(1,1, video::SColor(255,myrand()%256,
+                                       myrand()%256,myrand()%256));*/
+               }
+
+               // If base image is NULL, load as base.
+               if (baseimg == NULL)
+               {
+                       //infostream<<"Setting "<<part_of_name<<" as base"<<std::endl;
+                       /*
+                               Copy it this way to get an alpha channel.
+                               Otherwise images with alpha cannot be blitted on
+                               images that don't have alpha in the original file.
+                       */
+                       core::dimension2d<u32> dim = image->getDimension();
+                       baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
+                       image->copyTo(baseimg);
+               }
+               // Else blit on base.
+               else
+               {
+                       //infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
+                       // Size of the copied area
+                       core::dimension2d<u32> dim = image->getDimension();
+                       //core::dimension2d<u32> dim(16,16);
+                       // Position to copy the blitted to in the base image
+                       core::position2d<s32> pos_to(0,0);
+                       // Position to copy the blitted from in the blitted image
+                       core::position2d<s32> pos_from(0,0);
+                       // Blit
+                       /*image->copyToWithAlpha(baseimg, pos_to,
+                                       core::rect<s32>(pos_from, dim),
+                                       video::SColor(255,255,255,255),
+                                       NULL);*/
+                       blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
+               }
+               //cleanup
+               image->drop();
+       }
+       else
+       {
+               // A special texture modification
+
+               /*infostream<<"generateImage(): generating special "
+                               <<"modification \""<<part_of_name<<"\""
+                               <<std::endl;*/
+
+               /*
+                       [crack:N:P
+                       [cracko:N:P
+                       Adds a cracking texture
+                       N = animation frame count, P = crack progression
+               */
+               if (part_of_name.substr(0,6) == "[crack")
+               {
+                       if (baseimg == NULL) {
+                               errorstream<<"generateImagePart(): baseimg == NULL "
+                                               <<"for part_of_name=\""<<part_of_name
+                                               <<"\", cancelling."<<std::endl;
+                               return false;
+                       }
+
+                       // Crack image number and overlay option
+                       bool use_overlay = (part_of_name[6] == 'o');
+                       Strfnd sf(part_of_name);
+                       sf.next(":");
+                       s32 frame_count = stoi(sf.next(":"));
+                       s32 progression = stoi(sf.next(":"));
+
+                       /*
+                               Load crack image.
+
+                               It is an image with a number of cracking stages
+                               horizontally tiled.
+                       */
+                       video::IImage *img_crack = m_sourcecache.getOrLoad(
+                                       "crack_anylength.png", m_device);
+
+                       if (img_crack && progression >= 0)
+                       {
+                               draw_crack(img_crack, baseimg,
+                                               use_overlay, frame_count,
+                                               progression, driver);
+                               img_crack->drop();
+                       }
+               }
+               /*
+                       [combine:WxH:X,Y=filename:X,Y=filename2
+                       Creates a bigger texture from an amount of smaller ones
+               */
+               else if (part_of_name.substr(0,8) == "[combine")
+               {
+                       Strfnd sf(part_of_name);
+                       sf.next(":");
+                       u32 w0 = stoi(sf.next("x"));
+                       u32 h0 = stoi(sf.next(":"));
+                       //infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
+                       core::dimension2d<u32> dim(w0,h0);
+                       if (baseimg == NULL) {
+                               baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
+                               baseimg->fill(video::SColor(0,0,0,0));
+                       }
+                       while (sf.atend() == false) {
+                               u32 x = stoi(sf.next(","));
+                               u32 y = stoi(sf.next("="));
+                               std::string filename = sf.next(":");
+                               infostream<<"Adding \""<<filename
+                                               <<"\" to combined ("<<x<<","<<y<<")"
+                                               <<std::endl;
+                               video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
+                               if (img) {
+                                       core::dimension2d<u32> dim = img->getDimension();
+                                       infostream<<"Size "<<dim.Width
+                                                       <<"x"<<dim.Height<<std::endl;
+                                       core::position2d<s32> pos_base(x, y);
+                                       video::IImage *img2 =
+                                                       driver->createImage(video::ECF_A8R8G8B8, dim);
+                                       img->copyTo(img2);
+                                       img->drop();
+                                       /*img2->copyToWithAlpha(baseimg, pos_base,
+                                                       core::rect<s32>(v2s32(0,0), dim),
+                                                       video::SColor(255,255,255,255),
+                                                       NULL);*/
+                                       blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim);
+                                       img2->drop();
+                               } else {
+                                       errorstream << "generateImagePart(): Failed to load image \""
+                                               << filename << "\" for [combine" << std::endl;
+                               }
+                       }
+               }
+               /*
+                       "[brighten"
+               */
+               else if (part_of_name.substr(0,9) == "[brighten")
+               {
+                       if (baseimg == NULL) {
+                               errorstream<<"generateImagePart(): baseimg==NULL "
+                                               <<"for part_of_name=\""<<part_of_name
+                                               <<"\", cancelling."<<std::endl;
+                               return false;
+                       }
+
+                       brighten(baseimg);
+               }
+               /*
+                       "[noalpha"
+                       Make image completely opaque.
+                       Used for the leaves texture when in old leaves mode, so
+                       that the transparent parts don't look completely black
+                       when simple alpha channel is used for rendering.
+               */
+               else if (part_of_name.substr(0,8) == "[noalpha")
+               {
+                       if (baseimg == NULL){
+                               errorstream<<"generateImagePart(): baseimg==NULL "
+                                               <<"for part_of_name=\""<<part_of_name
+                                               <<"\", cancelling."<<std::endl;
+                               return false;
+                       }
+
+                       core::dimension2d<u32> dim = baseimg->getDimension();
+
+                       // Set alpha to full
+                       for (u32 y=0; y<dim.Height; y++)
+                       for (u32 x=0; x<dim.Width; x++)
+                       {
+                               video::SColor c = baseimg->getPixel(x,y);
+                               c.setAlpha(255);
+                               baseimg->setPixel(x,y,c);
+                       }
+               }
+               /*
+                       "[makealpha:R,G,B"
+                       Convert one color to transparent.
+               */
+               else if (part_of_name.substr(0,11) == "[makealpha:")
+               {
+                       if (baseimg == NULL) {
+                               errorstream<<"generateImagePart(): baseimg == NULL "
+                                               <<"for part_of_name=\""<<part_of_name
+                                               <<"\", cancelling."<<std::endl;
+                               return false;
+                       }
+
+                       Strfnd sf(part_of_name.substr(11));
+                       u32 r1 = stoi(sf.next(","));
+                       u32 g1 = stoi(sf.next(","));
+                       u32 b1 = stoi(sf.next(""));
+                       std::string filename = sf.next("");
+
+                       core::dimension2d<u32> dim = baseimg->getDimension();
+
+                       /*video::IImage *oldbaseimg = baseimg;
+                       baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
+                       oldbaseimg->copyTo(baseimg);
+                       oldbaseimg->drop();*/
+
+                       // Set alpha to full
+                       for (u32 y=0; y<dim.Height; y++)
+                       for (u32 x=0; x<dim.Width; x++)
+                       {
+                               video::SColor c = baseimg->getPixel(x,y);
+                               u32 r = c.getRed();
+                               u32 g = c.getGreen();
+                               u32 b = c.getBlue();
+                               if (!(r == r1 && g == g1 && b == b1))
+                                       continue;
+                               c.setAlpha(0);
+                               baseimg->setPixel(x,y,c);
+                       }
+               }
+               /*
+                       "[transformN"
+                       Rotates and/or flips the image.
+
+                       N can be a number (between 0 and 7) or a transform name.
+                       Rotations are counter-clockwise.
+                       0  I      identity
+                       1  R90    rotate by 90 degrees
+                       2  R180   rotate by 180 degrees
+                       3  R270   rotate by 270 degrees
+                       4  FX     flip X
+                       5  FXR90  flip X then rotate by 90 degrees
+                       6  FY     flip Y
+                       7  FYR90  flip Y then rotate by 90 degrees
+
+                       Note: Transform names can be concatenated to produce
+                       their product (applies the first then the second).
+                       The resulting transform will be equivalent to one of the
+                       eight existing ones, though (see: dihedral group).
+               */
+               else if (part_of_name.substr(0,10) == "[transform")
+               {
+                       if (baseimg == NULL) {
+                               errorstream<<"generateImagePart(): baseimg == NULL "
+                                               <<"for part_of_name=\""<<part_of_name
+                                               <<"\", cancelling."<<std::endl;
+                               return false;
+                       }
+
+                       u32 transform = parseImageTransform(part_of_name.substr(10));
+                       core::dimension2d<u32> dim = imageTransformDimension(
+                                       transform, baseimg->getDimension());
+                       video::IImage *image = driver->createImage(
+                                       baseimg->getColorFormat(), dim);
+                       assert(image);
+                       imageTransform(transform, baseimg, image);
+                       baseimg->drop();
+                       baseimg = image;
+               }
+               /*
+                       [inventorycube{topimage{leftimage{rightimage
+                       In every subimage, replace ^ with &.
+                       Create an "inventory cube".
+                       NOTE: This should be used only on its own.
+                       Example (a grass block (not actually used in game):
+                       "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
+               */
+               else if (part_of_name.substr(0,14) == "[inventorycube")
+               {
+                       if (baseimg != NULL){
+                               errorstream<<"generateImagePart(): baseimg != NULL "
+                                               <<"for part_of_name=\""<<part_of_name
+                                               <<"\", cancelling."<<std::endl;
+                               return false;
+                       }
+
+                       str_replace(part_of_name, '&', '^');
+                       Strfnd sf(part_of_name);
+                       sf.next("{");
+                       std::string imagename_top = sf.next("{");
+                       std::string imagename_left = sf.next("{");
+                       std::string imagename_right = sf.next("{");
+
+                       // Generate images for the faces of the cube
+                       video::IImage *img_top = generateImage(imagename_top);
+                       video::IImage *img_left = generateImage(imagename_left);
+                       video::IImage *img_right = generateImage(imagename_right);
+
+                       if (img_top == NULL || img_left == NULL || img_right == NULL) {
+                               errorstream << "generateImagePart(): Failed to create textures"
+                                               << " for inventorycube \"" << part_of_name << "\""
+                                               << std::endl;
+                               baseimg = generateImage(imagename_top);
+                               return true;
+                       }
+
+#ifdef __ANDROID__
+                       assert(img_top->getDimension().Height == npot2(img_top->getDimension().Height));
+                       assert(img_top->getDimension().Width == npot2(img_top->getDimension().Width));
+
+                       assert(img_left->getDimension().Height == npot2(img_left->getDimension().Height));
+                       assert(img_left->getDimension().Width == npot2(img_left->getDimension().Width));
+
+                       assert(img_right->getDimension().Height == npot2(img_right->getDimension().Height));
+                       assert(img_right->getDimension().Width == npot2(img_right->getDimension().Width));
+#endif
+
+                       // Create textures from images
+                       video::ITexture *texture_top = driver->addTexture(
+                                       (imagename_top + "__temp__").c_str(), img_top);
+                       video::ITexture *texture_left = driver->addTexture(
+                                       (imagename_left + "__temp__").c_str(), img_left);
+                       video::ITexture *texture_right = driver->addTexture(
+                                       (imagename_right + "__temp__").c_str(), img_right);
+                       assert(texture_top && texture_left && texture_right);
+
+                       // Drop images
+                       img_top->drop();
+                       img_left->drop();
+                       img_right->drop();
+
+                       /*
+                               Draw a cube mesh into a render target texture
+                       */
+                       scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1));
+                       setMeshColor(cube, video::SColor(255, 255, 255, 255));
+                       cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top);
+                       cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top);
+                       cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right);
+                       cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right);
+                       cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left);
+                       cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left);
+
+                       TextureFromMeshParams params;
+                       params.mesh = cube;
+                       params.dim.set(64, 64);
+                       params.rtt_texture_name = part_of_name + "_RTT";
+                       // We will delete the rtt texture ourselves
+                       params.delete_texture_on_shutdown = false;
+                       params.camera_position.set(0, 1.0, -1.5);
+                       params.camera_position.rotateXZBy(45);
+                       params.camera_lookat.set(0, 0, 0);
+                       // Set orthogonal projection
+                       params.camera_projection_matrix.buildProjectionMatrixOrthoLH(
+                                       1.65, 1.65, 0, 100);
+
+                       params.ambient_light.set(1.0, 0.2, 0.2, 0.2);
+                       params.light_position.set(10, 100, -50);
+                       params.light_color.set(1.0, 0.5, 0.5, 0.5);
+                       params.light_radius = 1000;
+
+                       video::ITexture *rtt = generateTextureFromMesh(params);
+
+                       // Drop mesh
+                       cube->drop();
+
+                       // Free textures
+                       driver->removeTexture(texture_top);
+                       driver->removeTexture(texture_left);
+                       driver->removeTexture(texture_right);
+
+                       if (rtt == NULL) {
+                               baseimg = generateImage(imagename_top);
+                               return true;
+                       }
+
+                       // Create image of render target
+                       video::IImage *image = driver->createImage(rtt, v2s32(0, 0), params.dim);
+                       assert(image);
+
+                       // Cleanup texture
+                       driver->removeTexture(rtt);
+
+                       baseimg = driver->createImage(video::ECF_A8R8G8B8, params.dim);
+
+                       if (image) {
+                               image->copyTo(baseimg);
+                               image->drop();
+                       }
+               }
+               /*
+                       [lowpart:percent:filename
+                       Adds the lower part of a texture
+               */
+               else if (part_of_name.substr(0,9) == "[lowpart:")
+               {
+                       Strfnd sf(part_of_name);
+                       sf.next(":");
+                       u32 percent = stoi(sf.next(":"));
+                       std::string filename = sf.next(":");
+                       //infostream<<"power part "<<percent<<"%% of "<<filename<<std::endl;
+
+                       if (baseimg == NULL)
+                               baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16));
+                       video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
+                       if (img)
+                       {
+                               core::dimension2d<u32> dim = img->getDimension();
+                               core::position2d<s32> pos_base(0, 0);
+                               video::IImage *img2 =
+                                               driver->createImage(video::ECF_A8R8G8B8, dim);
+                               img->copyTo(img2);
+                               img->drop();
+                               core::position2d<s32> clippos(0, 0);
+                               clippos.Y = dim.Height * (100-percent) / 100;
+                               core::dimension2d<u32> clipdim = dim;
+                               clipdim.Height = clipdim.Height * percent / 100 + 1;
+                               core::rect<s32> cliprect(clippos, clipdim);
+                               img2->copyToWithAlpha(baseimg, pos_base,
+                                               core::rect<s32>(v2s32(0,0), dim),
+                                               video::SColor(255,255,255,255),
+                                               &cliprect);
+                               img2->drop();
+                       }
+               }
+               /*
+                       [verticalframe:N:I
+                       Crops a frame of a vertical animation.
+                       N = frame count, I = frame index
+               */
+               else if (part_of_name.substr(0,15) == "[verticalframe:")
+               {
+                       Strfnd sf(part_of_name);
+                       sf.next(":");
+                       u32 frame_count = stoi(sf.next(":"));
+                       u32 frame_index = stoi(sf.next(":"));
+
+                       if (baseimg == NULL){
+                               errorstream<<"generateImagePart(): baseimg != NULL "
+                                               <<"for part_of_name=\""<<part_of_name
+                                               <<"\", cancelling."<<std::endl;
+                               return false;
+                       }
+
+                       v2u32 frame_size = baseimg->getDimension();
+                       frame_size.Y /= frame_count;
+
+                       video::IImage *img = driver->createImage(video::ECF_A8R8G8B8,
+                                       frame_size);
+                       if (!img){
+                               errorstream<<"generateImagePart(): Could not create image "
+                                               <<"for part_of_name=\""<<part_of_name
+                                               <<"\", cancelling."<<std::endl;
+                               return false;
+                       }
+
+                       // Fill target image with transparency
+                       img->fill(video::SColor(0,0,0,0));
+
+                       core::dimension2d<u32> dim = frame_size;
+                       core::position2d<s32> pos_dst(0, 0);
+                       core::position2d<s32> pos_src(0, frame_index * frame_size.Y);
+                       baseimg->copyToWithAlpha(img, pos_dst,
+                                       core::rect<s32>(pos_src, dim),
+                                       video::SColor(255,255,255,255),
+                                       NULL);
+                       // Replace baseimg
+                       baseimg->drop();
+                       baseimg = img;
+               }
+               /*
+                       [mask:filename
+                       Applies a mask to an image
+               */
+               else if (part_of_name.substr(0,6) == "[mask:")
+               {
+                       if (baseimg == NULL) {
+                               errorstream << "generateImage(): baseimg == NULL "
+                                               << "for part_of_name=\"" << part_of_name
+                                               << "\", cancelling." << std::endl;
+                               return false;
+                       }
+                       Strfnd sf(part_of_name);
+                       sf.next(":");
+                       std::string filename = sf.next(":");
+
+                       video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
+                       if (img) {
+                               apply_mask(img, baseimg, v2s32(0, 0), v2s32(0, 0),
+                                               img->getDimension());
+                       } else {
+                               errorstream << "generateImage(): Failed to load \""
+                                               << filename << "\".";
+                       }
+               }
+               /*
+                       [colorize:color
+                       Overlays image with given color
+                       color = color as ColorString
+               */
+               else if (part_of_name.substr(0,10) == "[colorize:") {
+                       Strfnd sf(part_of_name);
+                       sf.next(":");
+                       std::string color_str = sf.next(":");
+                       std::string ratio_str = sf.next(":");
+
+                       if (baseimg == NULL) {
+                               errorstream << "generateImagePart(): baseimg != NULL "
+                                               << "for part_of_name=\"" << part_of_name
+                                               << "\", cancelling." << std::endl;
+                               return false;
+                       }
+
+                       video::SColor color;
+                       int ratio = -1;
+
+                       if (!parseColorString(color_str, color, false))
+                               return false;
+
+                       if (is_number(ratio_str))
+                               ratio = mystoi(ratio_str, 0, 255);
+
+                       core::dimension2d<u32> dim = baseimg->getDimension();
+                       video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim);
+
+                       if (!img) {
+                               errorstream << "generateImagePart(): Could not create image "
+                                               << "for part_of_name=\"" << part_of_name
+                                               << "\", cancelling." << std::endl;
+                               return false;
+                       }
+
+                       img->fill(video::SColor(color));
+                       // Overlay the colored image
+                       blit_with_interpolate_overlay(img, baseimg, v2s32(0,0), v2s32(0,0), dim, ratio);
+                       img->drop();
+               }
+               else
+               {
+                       errorstream << "generateImagePart(): Invalid "
+                                       " modification: \"" << part_of_name << "\"" << std::endl;
+               }
+       }
+
+       return true;
+}
+
+/*
+       Draw an image on top of an another one, using the alpha channel of the
+       source image
+
+       This exists because IImage::copyToWithAlpha() doesn't seem to always
+       work.
+*/
+static void blit_with_alpha(video::IImage *src, video::IImage *dst,
+               v2s32 src_pos, v2s32 dst_pos, v2u32 size)
+{
+       for (u32 y0=0; y0<size.Y; y0++)
+       for (u32 x0=0; x0<size.X; x0++)
+       {
+               s32 src_x = src_pos.X + x0;
+               s32 src_y = src_pos.Y + y0;
+               s32 dst_x = dst_pos.X + x0;
+               s32 dst_y = dst_pos.Y + y0;
+               video::SColor src_c = src->getPixel(src_x, src_y);
+               video::SColor dst_c = dst->getPixel(dst_x, dst_y);
+               dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
+               dst->setPixel(dst_x, dst_y, dst_c);
+       }
+}
+
+/*
+       Draw an image on top of an another one, using the alpha channel of the
+       source image; only modify fully opaque pixels in destinaion
+*/
+static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
+               v2s32 src_pos, v2s32 dst_pos, v2u32 size)
+{
+       for (u32 y0=0; y0<size.Y; y0++)
+       for (u32 x0=0; x0<size.X; x0++)
+       {
+               s32 src_x = src_pos.X + x0;
+               s32 src_y = src_pos.Y + y0;
+               s32 dst_x = dst_pos.X + x0;
+               s32 dst_y = dst_pos.Y + y0;
+               video::SColor src_c = src->getPixel(src_x, src_y);
+               video::SColor dst_c = dst->getPixel(dst_x, dst_y);
+               if (dst_c.getAlpha() == 255 && src_c.getAlpha() != 0)
+               {
+                       dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
+                       dst->setPixel(dst_x, dst_y, dst_c);
+               }
+       }
+}
+
+/*
+       Draw an image on top of an another one, using the specified ratio
+       modify all partially-opaque pixels in the destination.
+*/
+static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst,
+               v2s32 src_pos, v2s32 dst_pos, v2u32 size, int ratio)
+{
+       for (u32 y0 = 0; y0 < size.Y; y0++)
+       for (u32 x0 = 0; x0 < size.X; x0++)
+       {
+               s32 src_x = src_pos.X + x0;
+               s32 src_y = src_pos.Y + y0;
+               s32 dst_x = dst_pos.X + x0;
+               s32 dst_y = dst_pos.Y + y0;
+               video::SColor src_c = src->getPixel(src_x, src_y);
+               video::SColor dst_c = dst->getPixel(dst_x, dst_y);
+               if (dst_c.getAlpha() > 0 && src_c.getAlpha() != 0)
+               {
+                       if (ratio == -1)
+                               dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
+                       else
+                               dst_c = src_c.getInterpolated(dst_c, (float)ratio/255.0f);
+                       dst->setPixel(dst_x, dst_y, dst_c);
+               }
+       }
+}
+
+/*
+       Apply mask to destination
+*/
+static void apply_mask(video::IImage *mask, video::IImage *dst,
+               v2s32 mask_pos, v2s32 dst_pos, v2u32 size)
+{
+       for (u32 y0 = 0; y0 < size.Y; y0++) {
+               for (u32 x0 = 0; x0 < size.X; x0++) {
+                       s32 mask_x = x0 + mask_pos.X;
+                       s32 mask_y = y0 + mask_pos.Y;
+                       s32 dst_x = x0 + dst_pos.X;
+                       s32 dst_y = y0 + dst_pos.Y;
+                       video::SColor mask_c = mask->getPixel(mask_x, mask_y);
+                       video::SColor dst_c = dst->getPixel(dst_x, dst_y);
+                       dst_c.color &= mask_c.color;
+                       dst->setPixel(dst_x, dst_y, dst_c);
+               }
+       }
+}
+
+static void draw_crack(video::IImage *crack, video::IImage *dst,
+               bool use_overlay, s32 frame_count, s32 progression,
+               video::IVideoDriver *driver)
+{
+       // Dimension of destination image
+       core::dimension2d<u32> dim_dst = dst->getDimension();
+       // Dimension of original image
+       core::dimension2d<u32> dim_crack = crack->getDimension();
+       // Count of crack stages
+       s32 crack_count = dim_crack.Height / dim_crack.Width;
+       // Limit frame_count
+       if (frame_count > (s32) dim_dst.Height)
+               frame_count = dim_dst.Height;
+       if (frame_count < 1)
+               frame_count = 1;
+       // Limit progression
+       if (progression > crack_count-1)
+               progression = crack_count-1;
+       // Dimension of a single crack stage
+       core::dimension2d<u32> dim_crack_cropped(
+               dim_crack.Width,
+               dim_crack.Width
+       );
+       // Dimension of the scaled crack stage,
+       // which is the same as the dimension of a single destination frame
+       core::dimension2d<u32> dim_crack_scaled(
+               dim_dst.Width,
+               dim_dst.Height / frame_count
+       );
+       // Create cropped and scaled crack images
+       video::IImage *crack_cropped = driver->createImage(
+                       video::ECF_A8R8G8B8, dim_crack_cropped);
+       video::IImage *crack_scaled = driver->createImage(
+                       video::ECF_A8R8G8B8, dim_crack_scaled);
+
+       if (crack_cropped && crack_scaled)
+       {
+               // Crop crack image
+               v2s32 pos_crack(0, progression*dim_crack.Width);
+               crack->copyTo(crack_cropped,
+                               v2s32(0,0),
+                               core::rect<s32>(pos_crack, dim_crack_cropped));
+               // Scale crack image by copying
+               crack_cropped->copyToScaling(crack_scaled);
+               // Copy or overlay crack image onto each frame
+               for (s32 i = 0; i < frame_count; ++i)
+               {
+                       v2s32 dst_pos(0, dim_crack_scaled.Height * i);
+                       if (use_overlay)
+                       {
+                               blit_with_alpha_overlay(crack_scaled, dst,
+                                               v2s32(0,0), dst_pos,
+                                               dim_crack_scaled);
+                       }
+                       else
+                       {
+                               blit_with_alpha(crack_scaled, dst,
+                                               v2s32(0,0), dst_pos,
+                                               dim_crack_scaled);
+                       }
+               }
+       }
+
+       if (crack_scaled)
+               crack_scaled->drop();
+
+       if (crack_cropped)
+               crack_cropped->drop();
+}
+
+void brighten(video::IImage *image)
+{
+       if (image == NULL)
+               return;
+
+       core::dimension2d<u32> dim = image->getDimension();
+
+       for (u32 y=0; y<dim.Height; y++)
+       for (u32 x=0; x<dim.Width; x++)
+       {
+               video::SColor c = image->getPixel(x,y);
+               c.setRed(0.5 * 255 + 0.5 * (float)c.getRed());
+               c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen());
+               c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue());
+               image->setPixel(x,y,c);
+       }
+}
+
+u32 parseImageTransform(const std::string& s)
+{
+       int total_transform = 0;
+
+       std::string transform_names[8];
+       transform_names[0] = "i";
+       transform_names[1] = "r90";
+       transform_names[2] = "r180";
+       transform_names[3] = "r270";
+       transform_names[4] = "fx";
+       transform_names[6] = "fy";
+
+       std::size_t pos = 0;
+       while(pos < s.size())
+       {
+               int transform = -1;
+               for (int i = 0; i <= 7; ++i)
+               {
+                       const std::string &name_i = transform_names[i];
+
+                       if (s[pos] == ('0' + i))
+                       {
+                               transform = i;
+                               pos++;
+                               break;
+                       }
+                       else if (!(name_i.empty()) &&
+                               lowercase(s.substr(pos, name_i.size())) == name_i)
+                       {
+                               transform = i;
+                               pos += name_i.size();
+                               break;
+                       }
+               }
+               if (transform < 0)
+                       break;
+
+               // Multiply total_transform and transform in the group D4
+               int new_total = 0;
+               if (transform < 4)
+                       new_total = (transform + total_transform) % 4;
+               else
+                       new_total = (transform - total_transform + 8) % 4;
+               if ((transform >= 4) ^ (total_transform >= 4))
+                       new_total += 4;
+
+               total_transform = new_total;
+       }
+       return total_transform;
+}
+
+core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim)
+{
+       if (transform % 2 == 0)
+               return dim;
+       else
+               return core::dimension2d<u32>(dim.Height, dim.Width);
+}
+
+void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
+{
+       if (src == NULL || dst == NULL)
+               return;
+
+       core::dimension2d<u32> srcdim = src->getDimension();
+       core::dimension2d<u32> dstdim = dst->getDimension();
+
+       assert(dstdim == imageTransformDimension(transform, srcdim));
+       assert(transform <= 7);
+
+       /*
+               Compute the transformation from source coordinates (sx,sy)
+               to destination coordinates (dx,dy).
+       */
+       int sxn = 0;
+       int syn = 2;
+       if (transform == 0)         // identity
+               sxn = 0, syn = 2;  //   sx = dx, sy = dy
+       else if (transform == 1)    // rotate by 90 degrees ccw
+               sxn = 3, syn = 0;  //   sx = (H-1) - dy, sy = dx
+       else if (transform == 2)    // rotate by 180 degrees
+               sxn = 1, syn = 3;  //   sx = (W-1) - dx, sy = (H-1) - dy
+       else if (transform == 3)    // rotate by 270 degrees ccw
+               sxn = 2, syn = 1;  //   sx = dy, sy = (W-1) - dx
+       else if (transform == 4)    // flip x
+               sxn = 1, syn = 2;  //   sx = (W-1) - dx, sy = dy
+       else if (transform == 5)    // flip x then rotate by 90 degrees ccw
+               sxn = 2, syn = 0;  //   sx = dy, sy = dx
+       else if (transform == 6)    // flip y
+               sxn = 0, syn = 3;  //   sx = dx, sy = (H-1) - dy
+       else if (transform == 7)    // flip y then rotate by 90 degrees ccw
+               sxn = 3, syn = 1;  //   sx = (H-1) - dy, sy = (W-1) - dx
+
+       for (u32 dy=0; dy<dstdim.Height; dy++)
+       for (u32 dx=0; dx<dstdim.Width; dx++)
+       {
+               u32 entries[4] = {dx, dstdim.Width-1-dx, dy, dstdim.Height-1-dy};
+               u32 sx = entries[sxn];
+               u32 sy = entries[syn];
+               video::SColor c = src->getPixel(sx,sy);
+               dst->setPixel(dx,dy,c);
+       }
+}
+
+video::ITexture* TextureSource::getNormalTexture(const std::string &name)
+{
+       u32 id;
+       if (isKnownSourceImage("override_normal.png"))
+               return getTexture("override_normal.png", &id);
+       std::string fname_base = name;
+       std::string normal_ext = "_normal.png";
+       size_t pos = fname_base.find(".");
+       std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
+       if (isKnownSourceImage(fname_normal)) {
+               // look for image extension and replace it
+               size_t i = 0;
+               while ((i = fname_base.find(".", i)) != std::string::npos) {
+                       fname_base.replace(i, 4, normal_ext);
+                       i += normal_ext.length();
+               }
+               return getTexture(fname_base, &id);
+               }
+       return NULL;
+}
diff --git a/src/client/tile.h b/src/client/tile.h
new file mode 100644 (file)
index 0000000..ea7a913
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef TILE_HEADER
+#define TILE_HEADER
+
+#include "irrlichttypes.h"
+#include "irr_v2d.h"
+#include "irr_v3d.h"
+#include <ITexture.h>
+#include <IrrlichtDevice.h>
+#include "threads.h"
+#include <string>
+#include <vector>
+
+class IGameDef;
+
+/*
+       tile.{h,cpp}: Texture handling stuff.
+*/
+
+/*
+       Find out the full path of an image by trying different filename
+       extensions.
+
+       If failed, return "".
+
+       TODO: Should probably be moved out from here, because things needing
+             this function do not need anything else from this header
+*/
+std::string getImagePath(std::string path);
+
+/*
+       Gets the path to a texture by first checking if the texture exists
+       in texture_path and if not, using the data path.
+
+       Checks all supported extensions by replacing the original extension.
+
+       If not found, returns "".
+
+       Utilizes a thread-safe cache.
+*/
+std::string getTexturePath(const std::string &filename);
+
+void clearTextureNameCache();
+
+/*
+       ITextureSource::generateTextureFromMesh parameters
+*/
+namespace irr {namespace scene {class IMesh;}}
+struct TextureFromMeshParams
+{
+       scene::IMesh *mesh;
+       core::dimension2d<u32> dim;
+       std::string rtt_texture_name;
+       bool delete_texture_on_shutdown;
+       v3f camera_position;
+       v3f camera_lookat;
+       core::CMatrix4<f32> camera_projection_matrix;
+       video::SColorf ambient_light;
+       v3f light_position;
+       video::SColorf light_color;
+       f32 light_radius;
+};
+
+/*
+       TextureSource creates and caches textures.
+*/
+
+class ISimpleTextureSource
+{
+public:
+       ISimpleTextureSource(){}
+       virtual ~ISimpleTextureSource(){}
+       virtual video::ITexture* getTexture(
+                       const std::string &name, u32 *id = NULL) = 0;
+};
+
+class ITextureSource : public ISimpleTextureSource
+{
+public:
+       ITextureSource(){}
+       virtual ~ITextureSource(){}
+       virtual u32 getTextureId(const std::string &name)=0;
+       virtual std::string getTextureName(u32 id)=0;
+       virtual video::ITexture* getTexture(u32 id)=0;
+       virtual video::ITexture* getTexture(
+                       const std::string &name, u32 *id = NULL)=0;
+       virtual IrrlichtDevice* getDevice()=0;
+       virtual bool isKnownSourceImage(const std::string &name)=0;
+       virtual video::ITexture* generateTextureFromMesh(
+                       const TextureFromMeshParams &params)=0;
+       virtual video::ITexture* getNormalTexture(const std::string &name)=0;
+};
+
+class IWritableTextureSource : public ITextureSource
+{
+public:
+       IWritableTextureSource(){}
+       virtual ~IWritableTextureSource(){}
+       virtual u32 getTextureId(const std::string &name)=0;
+       virtual std::string getTextureName(u32 id)=0;
+       virtual video::ITexture* getTexture(u32 id)=0;
+       virtual video::ITexture* getTexture(
+                       const std::string &name, u32 *id = NULL)=0;
+       virtual IrrlichtDevice* getDevice()=0;
+       virtual bool isKnownSourceImage(const std::string &name)=0;
+       virtual video::ITexture* generateTextureFromMesh(
+                       const TextureFromMeshParams &params)=0;
+
+       virtual void processQueue()=0;
+       virtual void insertSourceImage(const std::string &name, video::IImage *img)=0;
+       virtual void rebuildImagesAndTextures()=0;
+       virtual video::ITexture* getNormalTexture(const std::string &name)=0;
+};
+
+IWritableTextureSource* createTextureSource(IrrlichtDevice *device);
+
+#ifdef __ANDROID__
+/**
+ * @param size get next npot2 value
+ * @return npot2 value
+ */
+inline unsigned int npot2(unsigned int size)
+{
+       if (size == 0) return 0;
+       unsigned int npot = 1;
+
+       while ((size >>= 1) > 0) {
+               npot <<= 1;
+       }
+       return npot;
+}
+
+video::IImage * Align2Npot2(video::IImage * image, video::IVideoDriver* driver);
+#endif
+
+enum MaterialType{
+       TILE_MATERIAL_BASIC,
+       TILE_MATERIAL_ALPHA,
+       TILE_MATERIAL_LIQUID_TRANSPARENT,
+       TILE_MATERIAL_LIQUID_OPAQUE,
+       TILE_MATERIAL_WAVING_LEAVES,
+       TILE_MATERIAL_WAVING_PLANTS
+};
+
+// Material flags
+// Should backface culling be enabled?
+#define MATERIAL_FLAG_BACKFACE_CULLING 0x01
+// Should a crack be drawn?
+#define MATERIAL_FLAG_CRACK 0x02
+// Should the crack be drawn on transparent pixels (unset) or not (set)?
+// Ignored if MATERIAL_FLAG_CRACK is not set.
+#define MATERIAL_FLAG_CRACK_OVERLAY 0x04
+// Animation made up by splitting the texture to vertical frames, as
+// defined by extra parameters
+#define MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES 0x08
+#define MATERIAL_FLAG_HIGHLIGHTED 0x10
+
+/*
+       This fully defines the looks of a tile.
+       The SMaterial of a tile is constructed according to this.
+*/
+struct FrameSpec
+{
+       FrameSpec():
+               texture_id(0),
+               texture(NULL),
+               normal_texture(NULL)
+       {
+       }
+       u32 texture_id;
+       video::ITexture *texture;
+       video::ITexture *normal_texture;
+};
+
+struct TileSpec
+{
+       TileSpec():
+               texture_id(0),
+               texture(NULL),
+               normal_texture(NULL),
+               alpha(255),
+               material_type(TILE_MATERIAL_BASIC),
+               material_flags(
+                       //0 // <- DEBUG, Use the one below
+                       MATERIAL_FLAG_BACKFACE_CULLING
+               ),
+               shader_id(0),
+               animation_frame_count(1),
+               animation_frame_length_ms(0),
+               rotation(0)
+       {
+       }
+
+       bool operator==(const TileSpec &other) const
+       {
+               return (
+                       texture_id == other.texture_id &&
+                       /* texture == other.texture && */
+                       alpha == other.alpha &&
+                       material_type == other.material_type &&
+                       material_flags == other.material_flags &&
+                       rotation == other.rotation
+               );
+       }
+
+       bool operator!=(const TileSpec &other) const
+       {
+               return !(*this == other);
+       }
+       
+       // Sets everything else except the texture in the material
+       void applyMaterialOptions(video::SMaterial &material) const
+       {
+               switch (material_type) {
+               case TILE_MATERIAL_BASIC:
+                       material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+                       break;
+               case TILE_MATERIAL_ALPHA:
+                       material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+                       break;
+               case TILE_MATERIAL_LIQUID_TRANSPARENT:
+                       material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
+                       break;
+               case TILE_MATERIAL_LIQUID_OPAQUE:
+                       material.MaterialType = video::EMT_SOLID;
+                       break;
+               case TILE_MATERIAL_WAVING_LEAVES:
+                       material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+                       break;
+               case TILE_MATERIAL_WAVING_PLANTS:
+                       material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+                       break;
+               }
+               material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING)
+                       ? true : false;
+       }
+
+       void applyMaterialOptionsWithShaders(video::SMaterial &material) const
+       {
+               material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING)
+                       ? true : false;
+       }
+       
+       u32 texture_id;
+       video::ITexture *texture;
+       video::ITexture *normal_texture;
+       
+       // Vertex alpha (when MATERIAL_ALPHA_VERTEX is used)
+       u8 alpha;
+       // Material parameters
+       u8 material_type;
+       u8 material_flags;
+       u32 shader_id;
+       // Animation parameters
+       u8 animation_frame_count;
+       u16 animation_frame_length_ms;
+       std::vector<FrameSpec> frames;
+
+       u8 rotation;
+};
+
+#endif
index ddfa3b0320daf25525520543baff2cc001191718..5c3c7b54cb984a32cd1486eee1060afc442d9863 100644 (file)
@@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/numeric.h" // For IntervalLimiter
 #include "util/serialize.h"
 #include "util/mathconstants.h"
-#include "tile.h"
+#include "client/tile.h"
 #include "environment.h"
 #include "collision.h"
 #include "settings.h"
index 4779b20d1262fb7a218c35013a2a1934ee627d9e..c337472ffa93b0d406e0b8cf85a5a850f52630e0 100644 (file)
@@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "content_cso.h"
 #include <IBillboardSceneNode.h>
-#include "tile.h"
+#include "client/tile.h"
 #include "environment.h"
 #include "gamedef.h"
 #include "log.h"
index 54b9ac99f55213281b1823f14bc2173f1c6ba8d2..5bb787d019db95aa36a13a0c10051d56b558edd4 100644 (file)
@@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapblock_mesh.h" // For MapBlock_LightColor() and MeshCollector
 #include "settings.h"
 #include "nodedef.h"
-#include "tile.h"
+#include "client/tile.h"
 #include "mesh.h"
 #include <IMeshManipulator.h>
 #include "gamedef.h"
index 1fe1c4163f0965dd5c0e6325fcea2d9e0f8d684c..af76d2e02eb92e64f246e0de3acdff2ee791687e 100644 (file)
@@ -51,7 +51,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "nodemetadata.h"
 #include "main.h" // For g_settings
 #include "itemdef.h"
-#include "tile.h" // For TextureSource
+#include "client/tile.h" // For TextureSource
 #include "shader.h" // For ShaderSource
 #include "logoutputbuffer.h"
 #include "subgame.h"
index 8210e0bf42bc0ded0cce7e395e4d4e1cd910ef42..b52589044ab018f7f2e7bef8d706f346cd05d223 100644 (file)
@@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "main.h"  // for g_settings
 #include "porting.h"
-#include "tile.h"
+#include "client/tile.h"
 #include "fontengine.h"
 #include "log.h"
 #include "gettext.h"
index de6f44134ddb835faa3dde924de0e098c26fa794..b45011c2c4237638766e6dd7dd9e953e426bae39 100644 (file)
@@ -38,7 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "fontengine.h"
 
 #ifdef __ANDROID__
-#include "tile.h"
+#include "client/tile.h"
 #include <GLES/gl.h>
 #endif
 
index 0be8634ddd3cf31d1b7a8ff10446edf40fea1cc7..e57573220b107f8136756d389a3dda6429b9ded2 100644 (file)
@@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "modalMenu.h"
 #include "guiFormSpecMenu.h"
 #include "sound.h"
-#include "tile.h"
+#include "client/tile.h"
 
 /******************************************************************************/
 /* Typedefs and macros                                                        */
index a3cc0313108bf1986dbfd5f88ed30feece2f9a5b..581da894b4cb412e2c3e7328be4fb26115c849d5 100644 (file)
@@ -37,7 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <IGUITabControl.h>
 #include <IGUIComboBox.h>
 #include "log.h"
-#include "tile.h" // ITextureSource
+#include "client/tile.h" // ITextureSource
 #include "hud.h" // drawItemStack
 #include "filesys.h"
 #include "gettime.h"
index 05db228da3d7fe95a2f2ec40274aafd443dbac06..a7a53f581e2f61d9edb64ebc3eb8cebf10909948 100644 (file)
@@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <IGUIScrollBar.h>
 #include "debug.h"
 #include "log.h"
-#include "tile.h"
+#include "client/tile.h"
 #include "gettime.h"
 #include "util/string.h"
 #include "util/numeric.h"
index 29ebb810309501e67d5c73a15793143f998fcec8..aabaa066c597e308c4d7693efa8158be8b93d24c 100644 (file)
@@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "gamedef.h"
 #include "itemdef.h"
 #include "inventory.h"
-#include "tile.h"
+#include "client/tile.h"
 #include "localplayer.h"
 #include "camera.h"
 #include "porting.h"
index 3b4d2596ab0467be3e6b2dc0d4249b2c9435c30b..381773bb4b1e3c95a6a16d408556dd91238b7e2d 100644 (file)
@@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapblock_mesh.h"
 #include "mesh.h"
 #include "wieldmesh.h"
-#include "tile.h"
+#include "client/tile.h"
 #endif
 #include "log.h"
 #include "main.h" // g_settings
index 98af92c74c396e4019845182c2e2afed6bc96dff..b334ce46914c4fae321bdb9c5fa962574c63b7e6 100644 (file)
@@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define MAPBLOCK_MESH_HEADER
 
 #include "irrlichttypes_extrabloated.h"
-#include "tile.h"
+#include "client/tile.h"
 #include "voxel.h"
 #include <map>
 
index bcf51a072e1429e9f75d8c93b6e1e1786564fa6b..ac49b4f3cfad295342b9a31e48e52c6c07a70434 100644 (file)
@@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "main.h" // For g_settings
 #include "itemdef.h"
 #ifndef SERVER
-#include "tile.h"
+#include "client/tile.h"
 #include "mesh.h"
 #include <IMeshManipulator.h>
 #endif
index 7280bb8ea727af0e052f2b7569530cb3fee0db61..52ef29e50ec15e6f9e78c0f43c2a3d3277095ade 100644 (file)
@@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <list>
 #include "mapnode.h"
 #ifndef SERVER
-#include "tile.h"
+#include "client/tile.h"
 #include "shader.h"
 #endif
 #include "itemgroup.h"
index 64b8936bd7154e7d9c230007141302b12d8f09fc..a137aaaba54347f807e7bad3985163b707511c31 100644 (file)
@@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "debug.h"
 #include "main.h" // For g_profiler and g_settings
 #include "settings.h"
-#include "tile.h"
+#include "client/tile.h"
 #include "gamedef.h"
 #include "collision.h"
 #include <stdlib.h>
index d7f1147f16cf07b5a145f83ddee0ef7918d431b3..2bc2e7bfa813200943a85001c5ee375de58b9fca 100644 (file)
@@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include <iostream>
 #include "irrlichttypes_extrabloated.h"
-#include "tile.h"
+#include "client/tile.h"
 #include "localplayer.h"
 #include "environment.h"
 
index 1670458040f07da0052fa9e5fa7d6f544319a7f7..8c572ed49c4577e4a9c7ff2d188604151ab31e12 100644 (file)
@@ -36,7 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "gamedef.h"
 #include "strfnd.h" // trim()
-#include "tile.h"
+#include "client/tile.h"
 
 /*
        A cache from shader name to shader path
index 664ed694b397b6d894514118301422def46a328c..ac8e2cbf6d512a698ed5bfbfcd4011a33a2d3719 100644 (file)
@@ -3,7 +3,7 @@
 #include "ISceneManager.h"
 #include "ICameraSceneNode.h"
 #include "S3DVertex.h"
-#include "tile.h"
+#include "client/tile.h"
 #include "noise.h" // easeCurve
 #include "main.h" // g_profiler
 #include "profiler.h"
index fd2679eaea1e0be4c361d78de6f43ece33dcb7af..ccf477e8f4476b8089898306d3f55b34b5b93e3a 100644 (file)
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "strfnd.h"
 #ifndef SERVER
-#include "tile.h" // getImagePath
+#include "client/tile.h" // getImagePath
 #endif
 #include "util/string.h"
 
diff --git a/src/tile.cpp b/src/tile.cpp
deleted file mode 100644 (file)
index 81b362d..0000000
+++ /dev/null
@@ -1,1955 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "tile.h"
-
-#include <ICameraSceneNode.h>
-#include "util/string.h"
-#include "util/container.h"
-#include "util/thread.h"
-#include "util/numeric.h"
-#include "irrlichttypes_extrabloated.h"
-#include "debug.h"
-#include "main.h" // for g_settings
-#include "filesys.h"
-#include "settings.h"
-#include "mesh.h"
-#include "log.h"
-#include "gamedef.h"
-#include "strfnd.h"
-#include "util/string.h" // for parseColorString()
-
-#ifdef __ANDROID__
-#include <GLES/gl.h>
-#endif
-
-/*
-       A cache from texture name to texture path
-*/
-MutexedMap<std::string, std::string> g_texturename_to_path_cache;
-
-/*
-       Replaces the filename extension.
-       eg:
-               std::string image = "a/image.png"
-               replace_ext(image, "jpg")
-               -> image = "a/image.jpg"
-       Returns true on success.
-*/
-static bool replace_ext(std::string &path, const char *ext)
-{
-       if (ext == NULL)
-               return false;
-       // Find place of last dot, fail if \ or / found.
-       s32 last_dot_i = -1;
-       for (s32 i=path.size()-1; i>=0; i--)
-       {
-               if (path[i] == '.')
-               {
-                       last_dot_i = i;
-                       break;
-               }
-
-               if (path[i] == '\\' || path[i] == '/')
-                       break;
-       }
-       // If not found, return an empty string
-       if (last_dot_i == -1)
-               return false;
-       // Else make the new path
-       path = path.substr(0, last_dot_i+1) + ext;
-       return true;
-}
-
-/*
-       Find out the full path of an image by trying different filename
-       extensions.
-
-       If failed, return "".
-*/
-std::string getImagePath(std::string path)
-{
-       // A NULL-ended list of possible image extensions
-       const char *extensions[] = {
-               "png", "jpg", "bmp", "tga",
-               "pcx", "ppm", "psd", "wal", "rgb",
-               NULL
-       };
-       // If there is no extension, add one
-       if (removeStringEnd(path, extensions) == "")
-               path = path + ".png";
-       // Check paths until something is found to exist
-       const char **ext = extensions;
-       do{
-               bool r = replace_ext(path, *ext);
-               if (r == false)
-                       return "";
-               if (fs::PathExists(path))
-                       return path;
-       }
-       while((++ext) != NULL);
-
-       return "";
-}
-
-/*
-       Gets the path to a texture by first checking if the texture exists
-       in texture_path and if not, using the data path.
-
-       Checks all supported extensions by replacing the original extension.
-
-       If not found, returns "".
-
-       Utilizes a thread-safe cache.
-*/
-std::string getTexturePath(const std::string &filename)
-{
-       std::string fullpath = "";
-       /*
-               Check from cache
-       */
-       bool incache = g_texturename_to_path_cache.get(filename, &fullpath);
-       if (incache)
-               return fullpath;
-
-       /*
-               Check from texture_path
-       */
-       std::string texture_path = g_settings->get("texture_path");
-       if (texture_path != "")
-       {
-               std::string testpath = texture_path + DIR_DELIM + filename;
-               // Check all filename extensions. Returns "" if not found.
-               fullpath = getImagePath(testpath);
-       }
-
-       /*
-               Check from default data directory
-       */
-       if (fullpath == "")
-       {
-               std::string base_path = porting::path_share + DIR_DELIM + "textures"
-                               + DIR_DELIM + "base" + DIR_DELIM + "pack";
-               std::string testpath = base_path + DIR_DELIM + filename;
-               // Check all filename extensions. Returns "" if not found.
-               fullpath = getImagePath(testpath);
-       }
-
-       // Add to cache (also an empty result is cached)
-       g_texturename_to_path_cache.set(filename, fullpath);
-
-       // Finally return it
-       return fullpath;
-}
-
-void clearTextureNameCache()
-{
-       g_texturename_to_path_cache.clear();
-}
-
-/*
-       Stores internal information about a texture.
-*/
-
-struct TextureInfo
-{
-       std::string name;
-       video::ITexture *texture;
-
-       TextureInfo(
-                       const std::string &name_,
-                       video::ITexture *texture_=NULL
-               ):
-               name(name_),
-               texture(texture_)
-       {
-       }
-};
-
-/*
-       SourceImageCache: A cache used for storing source images.
-*/
-
-class SourceImageCache
-{
-public:
-       ~SourceImageCache() {
-               for (std::map<std::string, video::IImage*>::iterator iter = m_images.begin();
-                               iter != m_images.end(); iter++) {
-                       iter->second->drop();
-               }
-               m_images.clear();
-       }
-       void insert(const std::string &name, video::IImage *img,
-                       bool prefer_local, video::IVideoDriver *driver)
-       {
-               assert(img);
-               // Remove old image
-               std::map<std::string, video::IImage*>::iterator n;
-               n = m_images.find(name);
-               if (n != m_images.end()){
-                       if (n->second)
-                               n->second->drop();
-               }
-
-               video::IImage* toadd = img;
-               bool need_to_grab = true;
-
-               // Try to use local texture instead if asked to
-               if (prefer_local){
-                       std::string path = getTexturePath(name);
-                       if (path != ""){
-                               video::IImage *img2 = driver->createImageFromFile(path.c_str());
-                               if (img2){
-                                       toadd = img2;
-                                       need_to_grab = false;
-                               }
-                       }
-               }
-
-               if (need_to_grab)
-                       toadd->grab();
-               m_images[name] = toadd;
-       }
-       video::IImage* get(const std::string &name)
-       {
-               std::map<std::string, video::IImage*>::iterator n;
-               n = m_images.find(name);
-               if (n != m_images.end())
-                       return n->second;
-               return NULL;
-       }
-       // Primarily fetches from cache, secondarily tries to read from filesystem
-       video::IImage* getOrLoad(const std::string &name, IrrlichtDevice *device)
-       {
-               std::map<std::string, video::IImage*>::iterator n;
-               n = m_images.find(name);
-               if (n != m_images.end()){
-                       n->second->grab(); // Grab for caller
-                       return n->second;
-               }
-               video::IVideoDriver* driver = device->getVideoDriver();
-               std::string path = getTexturePath(name);
-               if (path == ""){
-                       infostream<<"SourceImageCache::getOrLoad(): No path found for \""
-                                       <<name<<"\""<<std::endl;
-                       return NULL;
-               }
-               infostream<<"SourceImageCache::getOrLoad(): Loading path \""<<path
-                               <<"\""<<std::endl;
-               video::IImage *img = driver->createImageFromFile(path.c_str());
-
-               if (img){
-                       m_images[name] = img;
-                       img->grab(); // Grab for caller
-               }
-               return img;
-       }
-private:
-       std::map<std::string, video::IImage*> m_images;
-};
-
-/*
-       TextureSource
-*/
-
-class TextureSource : public IWritableTextureSource
-{
-public:
-       TextureSource(IrrlichtDevice *device);
-       virtual ~TextureSource();
-
-       /*
-               Example case:
-               Now, assume a texture with the id 1 exists, and has the name
-               "stone.png^mineral1".
-               Then a random thread calls getTextureId for a texture called
-               "stone.png^mineral1^crack0".
-               ...Now, WTF should happen? Well:
-               - getTextureId strips off stuff recursively from the end until
-                 the remaining part is found, or nothing is left when
-                 something is stripped out
-
-               But it is slow to search for textures by names and modify them
-               like that?
-               - ContentFeatures is made to contain ids for the basic plain
-                 textures
-               - Crack textures can be slow by themselves, but the framework
-                 must be fast.
-
-               Example case #2:
-               - Assume a texture with the id 1 exists, and has the name
-                 "stone.png^mineral_coal.png".
-               - Now getNodeTile() stumbles upon a node which uses
-                 texture id 1, and determines that MATERIAL_FLAG_CRACK
-                 must be applied to the tile
-               - MapBlockMesh::animate() finds the MATERIAL_FLAG_CRACK and
-                 has received the current crack level 0 from the client. It
-                 finds out the name of the texture with getTextureName(1),
-                 appends "^crack0" to it and gets a new texture id with
-                 getTextureId("stone.png^mineral_coal.png^crack0").
-
-       */
-
-       /*
-               Gets a texture id from cache or
-               - if main thread, generates the texture, adds to cache and returns id.
-               - if other thread, adds to request queue and waits for main thread.
-
-               The id 0 points to a NULL texture. It is returned in case of error.
-       */
-       u32 getTextureId(const std::string &name);
-
-       // Finds out the name of a cached texture.
-       std::string getTextureName(u32 id);
-
-       /*
-               If texture specified by the name pointed by the id doesn't
-               exist, create it, then return the cached texture.
-
-               Can be called from any thread. If called from some other thread
-               and not found in cache, the call is queued to the main thread
-               for processing.
-       */
-       video::ITexture* getTexture(u32 id);
-
-       video::ITexture* getTexture(const std::string &name, u32 *id);
-
-       // Returns a pointer to the irrlicht device
-       virtual IrrlichtDevice* getDevice()
-       {
-               return m_device;
-       }
-
-       bool isKnownSourceImage(const std::string &name)
-       {
-               bool is_known = false;
-               bool cache_found = m_source_image_existence.get(name, &is_known);
-               if (cache_found)
-                       return is_known;
-               // Not found in cache; find out if a local file exists
-               is_known = (getTexturePath(name) != "");
-               m_source_image_existence.set(name, is_known);
-               return is_known;
-       }
-
-       // Processes queued texture requests from other threads.
-       // Shall be called from the main thread.
-       void processQueue();
-
-       // Insert an image into the cache without touching the filesystem.
-       // Shall be called from the main thread.
-       void insertSourceImage(const std::string &name, video::IImage *img);
-
-       // Rebuild images and textures from the current set of source images
-       // Shall be called from the main thread.
-       void rebuildImagesAndTextures();
-
-       // Render a mesh to a texture.
-       // Returns NULL if render-to-texture failed.
-       // Shall be called from the main thread.
-       video::ITexture* generateTextureFromMesh(
-                       const TextureFromMeshParams &params);
-
-       // Generates an image from a full string like
-       // "stone.png^mineral_coal.png^[crack:1:0".
-       // Shall be called from the main thread.
-       video::IImage* generateImage(const std::string &name);
-
-       video::ITexture* getNormalTexture(const std::string &name);
-private:
-
-       // The id of the thread that is allowed to use irrlicht directly
-       threadid_t m_main_thread;
-       // The irrlicht device
-       IrrlichtDevice *m_device;
-
-       // Cache of source images
-       // This should be only accessed from the main thread
-       SourceImageCache m_sourcecache;
-
-       // Generate a texture
-       u32 generateTexture(const std::string &name);
-
-       // Generate image based on a string like "stone.png" or "[crack:1:0".
-       // if baseimg is NULL, it is created. Otherwise stuff is made on it.
-       bool generateImagePart(std::string part_of_name, video::IImage *& baseimg);
-
-       // Thread-safe cache of what source images are known (true = known)
-       MutexedMap<std::string, bool> m_source_image_existence;
-
-       // A texture id is index in this array.
-       // The first position contains a NULL texture.
-       std::vector<TextureInfo> m_textureinfo_cache;
-       // Maps a texture name to an index in the former.
-       std::map<std::string, u32> m_name_to_id;
-       // The two former containers are behind this mutex
-       JMutex m_textureinfo_cache_mutex;
-
-       // Queued texture fetches (to be processed by the main thread)
-       RequestQueue<std::string, u32, u8, u8> m_get_texture_queue;
-
-       // Textures that have been overwritten with other ones
-       // but can't be deleted because the ITexture* might still be used
-       std::list<video::ITexture*> m_texture_trash;
-
-       // Cached settings needed for making textures from meshes
-       bool m_setting_trilinear_filter;
-       bool m_setting_bilinear_filter;
-       bool m_setting_anisotropic_filter;
-};
-
-IWritableTextureSource* createTextureSource(IrrlichtDevice *device)
-{
-       return new TextureSource(device);
-}
-
-TextureSource::TextureSource(IrrlichtDevice *device):
-               m_device(device)
-{
-       assert(m_device);
-
-       m_main_thread = get_current_thread_id();
-
-       // Add a NULL TextureInfo as the first index, named ""
-       m_textureinfo_cache.push_back(TextureInfo(""));
-       m_name_to_id[""] = 0;
-
-       // Cache some settings
-       // Note: Since this is only done once, the game must be restarted
-       // for these settings to take effect
-       m_setting_trilinear_filter = g_settings->getBool("trilinear_filter");
-       m_setting_bilinear_filter = g_settings->getBool("bilinear_filter");
-       m_setting_anisotropic_filter = g_settings->getBool("anisotropic_filter");
-}
-
-TextureSource::~TextureSource()
-{
-       video::IVideoDriver* driver = m_device->getVideoDriver();
-
-       unsigned int textures_before = driver->getTextureCount();
-
-       for (std::vector<TextureInfo>::iterator iter =
-                       m_textureinfo_cache.begin();
-                       iter != m_textureinfo_cache.end(); iter++)
-       {
-               //cleanup texture
-               if (iter->texture)
-                       driver->removeTexture(iter->texture);
-       }
-       m_textureinfo_cache.clear();
-
-       for (std::list<video::ITexture*>::iterator iter =
-                       m_texture_trash.begin(); iter != m_texture_trash.end();
-                       iter++)
-       {
-               video::ITexture *t = *iter;
-
-               //cleanup trashed texture
-               driver->removeTexture(t);
-       }
-
-       infostream << "~TextureSource() "<< textures_before << "/"
-                       << driver->getTextureCount() << std::endl;
-}
-
-u32 TextureSource::getTextureId(const std::string &name)
-{
-       //infostream<<"getTextureId(): \""<<name<<"\""<<std::endl;
-
-       {
-               /*
-                       See if texture already exists
-               */
-               JMutexAutoLock lock(m_textureinfo_cache_mutex);
-               std::map<std::string, u32>::iterator n;
-               n = m_name_to_id.find(name);
-               if (n != m_name_to_id.end())
-               {
-                       return n->second;
-               }
-       }
-
-       /*
-               Get texture
-       */
-       if (get_current_thread_id() == m_main_thread)
-       {
-               return generateTexture(name);
-       }
-       else
-       {
-               infostream<<"getTextureId(): Queued: name=\""<<name<<"\""<<std::endl;
-
-               // We're gonna ask the result to be put into here
-               static ResultQueue<std::string, u32, u8, u8> result_queue;
-
-               // Throw a request in
-               m_get_texture_queue.add(name, 0, 0, &result_queue);
-
-               /*infostream<<"Waiting for texture from main thread, name=\""
-                               <<name<<"\""<<std::endl;*/
-
-               try
-               {
-                       while(true) {
-                               // Wait result for a second
-                               GetResult<std::string, u32, u8, u8>
-                                       result = result_queue.pop_front(1000);
-
-                               if (result.key == name) {
-                                       return result.item;
-                               }
-                       }
-               }
-               catch(ItemNotFoundException &e)
-               {
-                       errorstream<<"Waiting for texture " << name << " timed out."<<std::endl;
-                       return 0;
-               }
-       }
-
-       infostream<<"getTextureId(): Failed"<<std::endl;
-
-       return 0;
-}
-
-// Draw an image on top of an another one, using the alpha channel of the
-// source image
-static void blit_with_alpha(video::IImage *src, video::IImage *dst,
-               v2s32 src_pos, v2s32 dst_pos, v2u32 size);
-
-// Like blit_with_alpha, but only modifies destination pixels that
-// are fully opaque
-static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
-               v2s32 src_pos, v2s32 dst_pos, v2u32 size);
-
-// Like blit_with_alpha overlay, but uses an int to calculate the ratio
-// and modifies any destination pixels that are not fully transparent
-static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst,
-               v2s32 src_pos, v2s32 dst_pos, v2u32 size, int ratio);
-
-// Apply a mask to an image
-static void apply_mask(video::IImage *mask, video::IImage *dst,
-               v2s32 mask_pos, v2s32 dst_pos, v2u32 size);
-
-// Draw or overlay a crack
-static void draw_crack(video::IImage *crack, video::IImage *dst,
-               bool use_overlay, s32 frame_count, s32 progression,
-               video::IVideoDriver *driver);
-
-// Brighten image
-void brighten(video::IImage *image);
-// Parse a transform name
-u32 parseImageTransform(const std::string& s);
-// Apply transform to image dimension
-core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim);
-// Apply transform to image data
-void imageTransform(u32 transform, video::IImage *src, video::IImage *dst);
-
-/*
-       This method generates all the textures
-*/
-u32 TextureSource::generateTexture(const std::string &name)
-{
-       //infostream << "generateTexture(): name=\"" << name << "\"" << std::endl;
-
-       // Empty name means texture 0
-       if (name == "") {
-               infostream<<"generateTexture(): name is empty"<<std::endl;
-               return 0;
-       }
-
-       {
-               /*
-                       See if texture already exists
-               */
-               JMutexAutoLock lock(m_textureinfo_cache_mutex);
-               std::map<std::string, u32>::iterator n;
-               n = m_name_to_id.find(name);
-               if (n != m_name_to_id.end()) {
-                       return n->second;
-               }
-       }
-
-       /*
-               Calling only allowed from main thread
-       */
-       if (get_current_thread_id() != m_main_thread) {
-               errorstream<<"TextureSource::generateTexture() "
-                               "called not from main thread"<<std::endl;
-               return 0;
-       }
-
-       video::IVideoDriver *driver = m_device->getVideoDriver();
-       assert(driver);
-
-       video::IImage *img = generateImage(name);
-
-       video::ITexture *tex = NULL;
-
-       if (img != NULL) {
-#ifdef __ANDROID__
-               img = Align2Npot2(img, driver);
-#endif
-               // Create texture from resulting image
-               tex = driver->addTexture(name.c_str(), img);
-               img->drop();
-       }
-
-       /*
-               Add texture to caches (add NULL textures too)
-       */
-
-       JMutexAutoLock lock(m_textureinfo_cache_mutex);
-
-       u32 id = m_textureinfo_cache.size();
-       TextureInfo ti(name, tex);
-       m_textureinfo_cache.push_back(ti);
-       m_name_to_id[name] = id;
-
-       return id;
-}
-
-std::string TextureSource::getTextureName(u32 id)
-{
-       JMutexAutoLock lock(m_textureinfo_cache_mutex);
-
-       if (id >= m_textureinfo_cache.size())
-       {
-               errorstream<<"TextureSource::getTextureName(): id="<<id
-                               <<" >= m_textureinfo_cache.size()="
-                               <<m_textureinfo_cache.size()<<std::endl;
-               return "";
-       }
-
-       return m_textureinfo_cache[id].name;
-}
-
-video::ITexture* TextureSource::getTexture(u32 id)
-{
-       JMutexAutoLock lock(m_textureinfo_cache_mutex);
-
-       if (id >= m_textureinfo_cache.size())
-               return NULL;
-
-       return m_textureinfo_cache[id].texture;
-}
-
-video::ITexture* TextureSource::getTexture(const std::string &name, u32 *id)
-{
-       u32 actual_id = getTextureId(name);
-       if (id){
-               *id = actual_id;
-       }
-       return getTexture(actual_id);
-}
-
-void TextureSource::processQueue()
-{
-       /*
-               Fetch textures
-       */
-       //NOTE this is only thread safe for ONE consumer thread!
-       if (!m_get_texture_queue.empty())
-       {
-               GetRequest<std::string, u32, u8, u8>
-                               request = m_get_texture_queue.pop();
-
-               /*infostream<<"TextureSource::processQueue(): "
-                               <<"got texture request with "
-                               <<"name=\""<<request.key<<"\""
-                               <<std::endl;*/
-
-               m_get_texture_queue.pushResult(request, generateTexture(request.key));
-       }
-}
-
-void TextureSource::insertSourceImage(const std::string &name, video::IImage *img)
-{
-       //infostream<<"TextureSource::insertSourceImage(): name="<<name<<std::endl;
-
-       assert(get_current_thread_id() == m_main_thread);
-
-       m_sourcecache.insert(name, img, true, m_device->getVideoDriver());
-       m_source_image_existence.set(name, true);
-}
-
-void TextureSource::rebuildImagesAndTextures()
-{
-       JMutexAutoLock lock(m_textureinfo_cache_mutex);
-
-       video::IVideoDriver* driver = m_device->getVideoDriver();
-       assert(driver != 0);
-
-       // Recreate textures
-       for (u32 i=0; i<m_textureinfo_cache.size(); i++){
-               TextureInfo *ti = &m_textureinfo_cache[i];
-               video::IImage *img = generateImage(ti->name);
-#ifdef __ANDROID__
-               img = Align2Npot2(img, driver);
-               assert(img->getDimension().Height == npot2(img->getDimension().Height));
-               assert(img->getDimension().Width == npot2(img->getDimension().Width));
-#endif
-               // Create texture from resulting image
-               video::ITexture *t = NULL;
-               if (img) {
-                       t = driver->addTexture(ti->name.c_str(), img);
-                       img->drop();
-               }
-               video::ITexture *t_old = ti->texture;
-               // Replace texture
-               ti->texture = t;
-
-               if (t_old)
-                       m_texture_trash.push_back(t_old);
-       }
-}
-
-video::ITexture* TextureSource::generateTextureFromMesh(
-               const TextureFromMeshParams &params)
-{
-       video::IVideoDriver *driver = m_device->getVideoDriver();
-       assert(driver);
-
-#ifdef __ANDROID__
-       const GLubyte* renderstr = glGetString(GL_RENDERER);
-       std::string renderer((char*) renderstr);
-
-       // use no render to texture hack
-       if (
-               (renderer.find("Adreno") != std::string::npos) ||
-               (renderer.find("Mali") != std::string::npos) ||
-               (renderer.find("Immersion") != std::string::npos) ||
-               (renderer.find("Tegra") != std::string::npos) ||
-               g_settings->getBool("inventory_image_hack")
-               ) {
-               // Get a scene manager
-               scene::ISceneManager *smgr_main = m_device->getSceneManager();
-               assert(smgr_main);
-               scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
-               assert(smgr);
-
-               const float scaling = 0.2;
-
-               scene::IMeshSceneNode* meshnode =
-                               smgr->addMeshSceneNode(params.mesh, NULL,
-                                               -1, v3f(0,0,0), v3f(0,0,0),
-                                               v3f(1.0 * scaling,1.0 * scaling,1.0 * scaling), true);
-               meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
-               meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
-               meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter);
-               meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter);
-               meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter);
-
-               scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
-                               params.camera_position, params.camera_lookat);
-               // second parameter of setProjectionMatrix (isOrthogonal) is ignored
-               camera->setProjectionMatrix(params.camera_projection_matrix, false);
-
-               smgr->setAmbientLight(params.ambient_light);
-               smgr->addLightSceneNode(0,
-                               params.light_position,
-                               params.light_color,
-                               params.light_radius*scaling);
-
-               core::dimension2d<u32> screen = driver->getScreenSize();
-
-               // Render scene
-               driver->beginScene(true, true, video::SColor(0,0,0,0));
-               driver->clearZBuffer();
-               smgr->drawAll();
-
-               core::dimension2d<u32> partsize(screen.Width * scaling,screen.Height * scaling);
-
-               irr::video::IImage* rawImage =
-                               driver->createImage(irr::video::ECF_A8R8G8B8, partsize);
-
-               u8* pixels = static_cast<u8*>(rawImage->lock());
-               if (!pixels)
-               {
-                       rawImage->drop();
-                       return NULL;
-               }
-
-               core::rect<s32> source(
-                               screen.Width /2 - (screen.Width  * (scaling / 2)),
-                               screen.Height/2 - (screen.Height * (scaling / 2)),
-                               screen.Width /2 + (screen.Width  * (scaling / 2)),
-                               screen.Height/2 + (screen.Height * (scaling / 2))
-                       );
-
-               glReadPixels(source.UpperLeftCorner.X, source.UpperLeftCorner.Y,
-                               partsize.Width, partsize.Height, GL_RGBA,
-                               GL_UNSIGNED_BYTE, pixels);
-
-               driver->endScene();
-
-               // Drop scene manager
-               smgr->drop();
-
-               unsigned int pixelcount = partsize.Width*partsize.Height;
-
-               u8* runptr = pixels;
-               for (unsigned int i=0; i < pixelcount; i++) {
-
-                       u8 B = *runptr;
-                       u8 G = *(runptr+1);
-                       u8 R = *(runptr+2);
-                       u8 A = *(runptr+3);
-
-                       //BGRA -> RGBA
-                       *runptr = R;
-                       runptr ++;
-                       *runptr = G;
-                       runptr ++;
-                       *runptr = B;
-                       runptr ++;
-                       *runptr = A;
-                       runptr ++;
-               }
-
-               video::IImage* inventory_image =
-                               driver->createImage(irr::video::ECF_A8R8G8B8, params.dim);
-
-               rawImage->copyToScaling(inventory_image);
-               rawImage->drop();
-
-               video::ITexture *rtt = driver->addTexture(params.rtt_texture_name.c_str(), inventory_image);
-               inventory_image->drop();
-
-               if (rtt == NULL) {
-                       errorstream << "TextureSource::generateTextureFromMesh(): failed to recreate texture from image: " << params.rtt_texture_name << std::endl;
-                       return NULL;
-               }
-
-               driver->makeColorKeyTexture(rtt, v2s32(0,0));
-
-               if (params.delete_texture_on_shutdown)
-                       m_texture_trash.push_back(rtt);
-
-               return rtt;
-       }
-#endif
-
-       if (driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
-       {
-               static bool warned = false;
-               if (!warned)
-               {
-                       errorstream<<"TextureSource::generateTextureFromMesh(): "
-                               <<"EVDF_RENDER_TO_TARGET not supported."<<std::endl;
-                       warned = true;
-               }
-               return NULL;
-       }
-
-       // Create render target texture
-       video::ITexture *rtt = driver->addRenderTargetTexture(
-                       params.dim, params.rtt_texture_name.c_str(),
-                       video::ECF_A8R8G8B8);
-       if (rtt == NULL)
-       {
-               errorstream<<"TextureSource::generateTextureFromMesh(): "
-                       <<"addRenderTargetTexture returned NULL."<<std::endl;
-               return NULL;
-       }
-
-       // Set render target
-       if (!driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0))) {
-               driver->removeTexture(rtt);
-               errorstream<<"TextureSource::generateTextureFromMesh(): "
-                       <<"failed to set render target"<<std::endl;
-               return NULL;
-       }
-
-       // Get a scene manager
-       scene::ISceneManager *smgr_main = m_device->getSceneManager();
-       assert(smgr_main);
-       scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
-       assert(smgr);
-
-       scene::IMeshSceneNode* meshnode =
-                       smgr->addMeshSceneNode(params.mesh, NULL,
-                                       -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true);
-       meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
-       meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
-       meshnode->setMaterialFlag(video::EMF_TRILINEAR_FILTER, m_setting_trilinear_filter);
-       meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, m_setting_bilinear_filter);
-       meshnode->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, m_setting_anisotropic_filter);
-
-       scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
-                       params.camera_position, params.camera_lookat);
-       // second parameter of setProjectionMatrix (isOrthogonal) is ignored
-       camera->setProjectionMatrix(params.camera_projection_matrix, false);
-
-       smgr->setAmbientLight(params.ambient_light);
-       smgr->addLightSceneNode(0,
-                       params.light_position,
-                       params.light_color,
-                       params.light_radius);
-
-       // Render scene
-       driver->beginScene(true, true, video::SColor(0,0,0,0));
-       smgr->drawAll();
-       driver->endScene();
-
-       // Drop scene manager
-       smgr->drop();
-
-       // Unset render target
-       driver->setRenderTarget(0, false, true, 0);
-
-       if (params.delete_texture_on_shutdown)
-               m_texture_trash.push_back(rtt);
-
-       return rtt;
-}
-
-video::IImage* TextureSource::generateImage(const std::string &name)
-{
-       /*
-               Get the base image
-       */
-
-       const char separator = '^';
-       const char paren_open = '(';
-       const char paren_close = ')';
-
-       // Find last separator in the name
-       s32 last_separator_pos = -1;
-       u8 paren_bal = 0;
-       for (s32 i = name.size() - 1; i >= 0; i--) {
-               switch(name[i]) {
-               case separator:
-                       if (paren_bal == 0) {
-                               last_separator_pos = i;
-                               i = -1; // break out of loop
-                       }
-                       break;
-               case paren_open:
-                       if (paren_bal == 0) {
-                               errorstream << "generateImage(): unbalanced parentheses"
-                                               << "(extranous '(') while generating texture \""
-                                               << name << "\"" << std::endl;
-                               return NULL;
-                       }
-                       paren_bal--;
-                       break;
-               case paren_close:
-                       paren_bal++;
-                       break;
-               default:
-                       break;
-               }
-       }
-       if (paren_bal > 0) {
-               errorstream << "generateImage(): unbalanced parentheses"
-                               << "(missing matching '(') while generating texture \""
-                               << name << "\"" << std::endl;
-               return NULL;
-       }
-
-
-       video::IImage *baseimg = NULL;
-
-       /*
-               If separator was found, make the base image
-               using a recursive call.
-       */
-       if (last_separator_pos != -1) {
-               baseimg = generateImage(name.substr(0, last_separator_pos));
-       }
-
-
-       video::IVideoDriver* driver = m_device->getVideoDriver();
-       assert(driver);
-
-       /*
-               Parse out the last part of the name of the image and act
-               according to it
-       */
-
-       std::string last_part_of_name = name.substr(last_separator_pos + 1);
-
-       /* 
-               If this name is enclosed in parentheses, generate it
-               and blit it onto the base image
-       */
-       if (last_part_of_name[0] == paren_open
-                       && last_part_of_name[last_part_of_name.size() - 1] == paren_close) {
-               std::string name2 = last_part_of_name.substr(1,
-                               last_part_of_name.size() - 2);
-               video::IImage *tmp = generateImage(name2);
-               if (!tmp) {
-                       errorstream << "generateImage(): "
-                               "Failed to generate \"" << name2 << "\""
-                               << std::endl;
-                       return NULL;
-               }
-               core::dimension2d<u32> dim = tmp->getDimension();
-               if (!baseimg)
-                       baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
-               blit_with_alpha(tmp, baseimg, v2s32(0, 0), v2s32(0, 0), dim);
-               tmp->drop();
-       } else if (!generateImagePart(last_part_of_name, baseimg)) {
-               // Generate image according to part of name
-               errorstream << "generateImage(): "
-                               "Failed to generate \"" << last_part_of_name << "\""
-                               << std::endl;
-       }
-
-       // If no resulting image, print a warning
-       if (baseimg == NULL) {
-               errorstream << "generateImage(): baseimg is NULL (attempted to"
-                               " create texture \"" << name << "\")" << std::endl;
-       }
-
-       return baseimg;
-}
-
-#ifdef __ANDROID__
-#include <GLES/gl.h>
-/**
- * Check and align image to npot2 if required by hardware
- * @param image image to check for npot2 alignment
- * @param driver driver to use for image operations
- * @return image or copy of image aligned to npot2
- */
-video::IImage * Align2Npot2(video::IImage * image,
-               video::IVideoDriver* driver)
-{
-       if (image == NULL) {
-               return image;
-       }
-
-       core::dimension2d<u32> dim = image->getDimension();
-
-       std::string extensions = (char*) glGetString(GL_EXTENSIONS);
-       if (extensions.find("GL_OES_texture_npot") != std::string::npos) {
-               return image;
-       }
-
-       unsigned int height = npot2(dim.Height);
-       unsigned int width  = npot2(dim.Width);
-
-       if ((dim.Height == height) &&
-                       (dim.Width == width)) {
-               return image;
-       }
-
-       if (dim.Height > height) {
-               height *= 2;
-       }
-
-       if (dim.Width > width) {
-               width *= 2;
-       }
-
-       video::IImage *targetimage =
-                       driver->createImage(video::ECF_A8R8G8B8,
-                                       core::dimension2d<u32>(width, height));
-
-       if (targetimage != NULL) {
-               image->copyToScaling(targetimage);
-       }
-       image->drop();
-       return targetimage;
-}
-
-#endif
-
-bool TextureSource::generateImagePart(std::string part_of_name,
-               video::IImage *& baseimg)
-{
-       video::IVideoDriver* driver = m_device->getVideoDriver();
-       assert(driver);
-
-       // Stuff starting with [ are special commands
-       if (part_of_name.size() == 0 || part_of_name[0] != '[')
-       {
-               video::IImage *image = m_sourcecache.getOrLoad(part_of_name, m_device);
-#ifdef __ANDROID__
-               image = Align2Npot2(image, driver);
-#endif
-               if (image == NULL) {
-                       if (part_of_name != "") {
-                               if (part_of_name.find("_normal.png") == std::string::npos){
-                                       errorstream<<"generateImage(): Could not load image \""
-                                               <<part_of_name<<"\""<<" while building texture"<<std::endl;
-                                       errorstream<<"generateImage(): Creating a dummy"
-                                               <<" image for \""<<part_of_name<<"\""<<std::endl;
-                               } else {
-                                       infostream<<"generateImage(): Could not load normal map \""
-                                               <<part_of_name<<"\""<<std::endl;
-                                       infostream<<"generateImage(): Creating a dummy"
-                                               <<" normal map for \""<<part_of_name<<"\""<<std::endl;
-                               }
-                       }
-
-                       // Just create a dummy image
-                       //core::dimension2d<u32> dim(2,2);
-                       core::dimension2d<u32> dim(1,1);
-                       image = driver->createImage(video::ECF_A8R8G8B8, dim);
-                       assert(image);
-                       /*image->setPixel(0,0, video::SColor(255,255,0,0));
-                       image->setPixel(1,0, video::SColor(255,0,255,0));
-                       image->setPixel(0,1, video::SColor(255,0,0,255));
-                       image->setPixel(1,1, video::SColor(255,255,0,255));*/
-                       image->setPixel(0,0, video::SColor(255,myrand()%256,
-                                       myrand()%256,myrand()%256));
-                       /*image->setPixel(1,0, video::SColor(255,myrand()%256,
-                                       myrand()%256,myrand()%256));
-                       image->setPixel(0,1, video::SColor(255,myrand()%256,
-                                       myrand()%256,myrand()%256));
-                       image->setPixel(1,1, video::SColor(255,myrand()%256,
-                                       myrand()%256,myrand()%256));*/
-               }
-
-               // If base image is NULL, load as base.
-               if (baseimg == NULL)
-               {
-                       //infostream<<"Setting "<<part_of_name<<" as base"<<std::endl;
-                       /*
-                               Copy it this way to get an alpha channel.
-                               Otherwise images with alpha cannot be blitted on
-                               images that don't have alpha in the original file.
-                       */
-                       core::dimension2d<u32> dim = image->getDimension();
-                       baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
-                       image->copyTo(baseimg);
-               }
-               // Else blit on base.
-               else
-               {
-                       //infostream<<"Blitting "<<part_of_name<<" on base"<<std::endl;
-                       // Size of the copied area
-                       core::dimension2d<u32> dim = image->getDimension();
-                       //core::dimension2d<u32> dim(16,16);
-                       // Position to copy the blitted to in the base image
-                       core::position2d<s32> pos_to(0,0);
-                       // Position to copy the blitted from in the blitted image
-                       core::position2d<s32> pos_from(0,0);
-                       // Blit
-                       /*image->copyToWithAlpha(baseimg, pos_to,
-                                       core::rect<s32>(pos_from, dim),
-                                       video::SColor(255,255,255,255),
-                                       NULL);*/
-                       blit_with_alpha(image, baseimg, pos_from, pos_to, dim);
-               }
-               //cleanup
-               image->drop();
-       }
-       else
-       {
-               // A special texture modification
-
-               /*infostream<<"generateImage(): generating special "
-                               <<"modification \""<<part_of_name<<"\""
-                               <<std::endl;*/
-
-               /*
-                       [crack:N:P
-                       [cracko:N:P
-                       Adds a cracking texture
-                       N = animation frame count, P = crack progression
-               */
-               if (part_of_name.substr(0,6) == "[crack")
-               {
-                       if (baseimg == NULL) {
-                               errorstream<<"generateImagePart(): baseimg == NULL "
-                                               <<"for part_of_name=\""<<part_of_name
-                                               <<"\", cancelling."<<std::endl;
-                               return false;
-                       }
-
-                       // Crack image number and overlay option
-                       bool use_overlay = (part_of_name[6] == 'o');
-                       Strfnd sf(part_of_name);
-                       sf.next(":");
-                       s32 frame_count = stoi(sf.next(":"));
-                       s32 progression = stoi(sf.next(":"));
-
-                       /*
-                               Load crack image.
-
-                               It is an image with a number of cracking stages
-                               horizontally tiled.
-                       */
-                       video::IImage *img_crack = m_sourcecache.getOrLoad(
-                                       "crack_anylength.png", m_device);
-
-                       if (img_crack && progression >= 0)
-                       {
-                               draw_crack(img_crack, baseimg,
-                                               use_overlay, frame_count,
-                                               progression, driver);
-                               img_crack->drop();
-                       }
-               }
-               /*
-                       [combine:WxH:X,Y=filename:X,Y=filename2
-                       Creates a bigger texture from an amount of smaller ones
-               */
-               else if (part_of_name.substr(0,8) == "[combine")
-               {
-                       Strfnd sf(part_of_name);
-                       sf.next(":");
-                       u32 w0 = stoi(sf.next("x"));
-                       u32 h0 = stoi(sf.next(":"));
-                       //infostream<<"combined w="<<w0<<" h="<<h0<<std::endl;
-                       core::dimension2d<u32> dim(w0,h0);
-                       if (baseimg == NULL) {
-                               baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
-                               baseimg->fill(video::SColor(0,0,0,0));
-                       }
-                       while (sf.atend() == false) {
-                               u32 x = stoi(sf.next(","));
-                               u32 y = stoi(sf.next("="));
-                               std::string filename = sf.next(":");
-                               infostream<<"Adding \""<<filename
-                                               <<"\" to combined ("<<x<<","<<y<<")"
-                                               <<std::endl;
-                               video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
-                               if (img) {
-                                       core::dimension2d<u32> dim = img->getDimension();
-                                       infostream<<"Size "<<dim.Width
-                                                       <<"x"<<dim.Height<<std::endl;
-                                       core::position2d<s32> pos_base(x, y);
-                                       video::IImage *img2 =
-                                                       driver->createImage(video::ECF_A8R8G8B8, dim);
-                                       img->copyTo(img2);
-                                       img->drop();
-                                       /*img2->copyToWithAlpha(baseimg, pos_base,
-                                                       core::rect<s32>(v2s32(0,0), dim),
-                                                       video::SColor(255,255,255,255),
-                                                       NULL);*/
-                                       blit_with_alpha(img2, baseimg, v2s32(0,0), pos_base, dim);
-                                       img2->drop();
-                               } else {
-                                       errorstream << "generateImagePart(): Failed to load image \""
-                                               << filename << "\" for [combine" << std::endl;
-                               }
-                       }
-               }
-               /*
-                       "[brighten"
-               */
-               else if (part_of_name.substr(0,9) == "[brighten")
-               {
-                       if (baseimg == NULL) {
-                               errorstream<<"generateImagePart(): baseimg==NULL "
-                                               <<"for part_of_name=\""<<part_of_name
-                                               <<"\", cancelling."<<std::endl;
-                               return false;
-                       }
-
-                       brighten(baseimg);
-               }
-               /*
-                       "[noalpha"
-                       Make image completely opaque.
-                       Used for the leaves texture when in old leaves mode, so
-                       that the transparent parts don't look completely black
-                       when simple alpha channel is used for rendering.
-               */
-               else if (part_of_name.substr(0,8) == "[noalpha")
-               {
-                       if (baseimg == NULL){
-                               errorstream<<"generateImagePart(): baseimg==NULL "
-                                               <<"for part_of_name=\""<<part_of_name
-                                               <<"\", cancelling."<<std::endl;
-                               return false;
-                       }
-
-                       core::dimension2d<u32> dim = baseimg->getDimension();
-
-                       // Set alpha to full
-                       for (u32 y=0; y<dim.Height; y++)
-                       for (u32 x=0; x<dim.Width; x++)
-                       {
-                               video::SColor c = baseimg->getPixel(x,y);
-                               c.setAlpha(255);
-                               baseimg->setPixel(x,y,c);
-                       }
-               }
-               /*
-                       "[makealpha:R,G,B"
-                       Convert one color to transparent.
-               */
-               else if (part_of_name.substr(0,11) == "[makealpha:")
-               {
-                       if (baseimg == NULL) {
-                               errorstream<<"generateImagePart(): baseimg == NULL "
-                                               <<"for part_of_name=\""<<part_of_name
-                                               <<"\", cancelling."<<std::endl;
-                               return false;
-                       }
-
-                       Strfnd sf(part_of_name.substr(11));
-                       u32 r1 = stoi(sf.next(","));
-                       u32 g1 = stoi(sf.next(","));
-                       u32 b1 = stoi(sf.next(""));
-                       std::string filename = sf.next("");
-
-                       core::dimension2d<u32> dim = baseimg->getDimension();
-
-                       /*video::IImage *oldbaseimg = baseimg;
-                       baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
-                       oldbaseimg->copyTo(baseimg);
-                       oldbaseimg->drop();*/
-
-                       // Set alpha to full
-                       for (u32 y=0; y<dim.Height; y++)
-                       for (u32 x=0; x<dim.Width; x++)
-                       {
-                               video::SColor c = baseimg->getPixel(x,y);
-                               u32 r = c.getRed();
-                               u32 g = c.getGreen();
-                               u32 b = c.getBlue();
-                               if (!(r == r1 && g == g1 && b == b1))
-                                       continue;
-                               c.setAlpha(0);
-                               baseimg->setPixel(x,y,c);
-                       }
-               }
-               /*
-                       "[transformN"
-                       Rotates and/or flips the image.
-
-                       N can be a number (between 0 and 7) or a transform name.
-                       Rotations are counter-clockwise.
-                       0  I      identity
-                       1  R90    rotate by 90 degrees
-                       2  R180   rotate by 180 degrees
-                       3  R270   rotate by 270 degrees
-                       4  FX     flip X
-                       5  FXR90  flip X then rotate by 90 degrees
-                       6  FY     flip Y
-                       7  FYR90  flip Y then rotate by 90 degrees
-
-                       Note: Transform names can be concatenated to produce
-                       their product (applies the first then the second).
-                       The resulting transform will be equivalent to one of the
-                       eight existing ones, though (see: dihedral group).
-               */
-               else if (part_of_name.substr(0,10) == "[transform")
-               {
-                       if (baseimg == NULL) {
-                               errorstream<<"generateImagePart(): baseimg == NULL "
-                                               <<"for part_of_name=\""<<part_of_name
-                                               <<"\", cancelling."<<std::endl;
-                               return false;
-                       }
-
-                       u32 transform = parseImageTransform(part_of_name.substr(10));
-                       core::dimension2d<u32> dim = imageTransformDimension(
-                                       transform, baseimg->getDimension());
-                       video::IImage *image = driver->createImage(
-                                       baseimg->getColorFormat(), dim);
-                       assert(image);
-                       imageTransform(transform, baseimg, image);
-                       baseimg->drop();
-                       baseimg = image;
-               }
-               /*
-                       [inventorycube{topimage{leftimage{rightimage
-                       In every subimage, replace ^ with &.
-                       Create an "inventory cube".
-                       NOTE: This should be used only on its own.
-                       Example (a grass block (not actually used in game):
-                       "[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
-               */
-               else if (part_of_name.substr(0,14) == "[inventorycube")
-               {
-                       if (baseimg != NULL){
-                               errorstream<<"generateImagePart(): baseimg != NULL "
-                                               <<"for part_of_name=\""<<part_of_name
-                                               <<"\", cancelling."<<std::endl;
-                               return false;
-                       }
-
-                       str_replace(part_of_name, '&', '^');
-                       Strfnd sf(part_of_name);
-                       sf.next("{");
-                       std::string imagename_top = sf.next("{");
-                       std::string imagename_left = sf.next("{");
-                       std::string imagename_right = sf.next("{");
-
-                       // Generate images for the faces of the cube
-                       video::IImage *img_top = generateImage(imagename_top);
-                       video::IImage *img_left = generateImage(imagename_left);
-                       video::IImage *img_right = generateImage(imagename_right);
-
-                       if (img_top == NULL || img_left == NULL || img_right == NULL) {
-                               errorstream << "generateImagePart(): Failed to create textures"
-                                               << " for inventorycube \"" << part_of_name << "\""
-                                               << std::endl;
-                               baseimg = generateImage(imagename_top);
-                               return true;
-                       }
-
-#ifdef __ANDROID__
-                       assert(img_top->getDimension().Height == npot2(img_top->getDimension().Height));
-                       assert(img_top->getDimension().Width == npot2(img_top->getDimension().Width));
-
-                       assert(img_left->getDimension().Height == npot2(img_left->getDimension().Height));
-                       assert(img_left->getDimension().Width == npot2(img_left->getDimension().Width));
-
-                       assert(img_right->getDimension().Height == npot2(img_right->getDimension().Height));
-                       assert(img_right->getDimension().Width == npot2(img_right->getDimension().Width));
-#endif
-
-                       // Create textures from images
-                       video::ITexture *texture_top = driver->addTexture(
-                                       (imagename_top + "__temp__").c_str(), img_top);
-                       video::ITexture *texture_left = driver->addTexture(
-                                       (imagename_left + "__temp__").c_str(), img_left);
-                       video::ITexture *texture_right = driver->addTexture(
-                                       (imagename_right + "__temp__").c_str(), img_right);
-                       assert(texture_top && texture_left && texture_right);
-
-                       // Drop images
-                       img_top->drop();
-                       img_left->drop();
-                       img_right->drop();
-
-                       /*
-                               Draw a cube mesh into a render target texture
-                       */
-                       scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1));
-                       setMeshColor(cube, video::SColor(255, 255, 255, 255));
-                       cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top);
-                       cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top);
-                       cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right);
-                       cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right);
-                       cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left);
-                       cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left);
-
-                       TextureFromMeshParams params;
-                       params.mesh = cube;
-                       params.dim.set(64, 64);
-                       params.rtt_texture_name = part_of_name + "_RTT";
-                       // We will delete the rtt texture ourselves
-                       params.delete_texture_on_shutdown = false;
-                       params.camera_position.set(0, 1.0, -1.5);
-                       params.camera_position.rotateXZBy(45);
-                       params.camera_lookat.set(0, 0, 0);
-                       // Set orthogonal projection
-                       params.camera_projection_matrix.buildProjectionMatrixOrthoLH(
-                                       1.65, 1.65, 0, 100);
-
-                       params.ambient_light.set(1.0, 0.2, 0.2, 0.2);
-                       params.light_position.set(10, 100, -50);
-                       params.light_color.set(1.0, 0.5, 0.5, 0.5);
-                       params.light_radius = 1000;
-
-                       video::ITexture *rtt = generateTextureFromMesh(params);
-
-                       // Drop mesh
-                       cube->drop();
-
-                       // Free textures
-                       driver->removeTexture(texture_top);
-                       driver->removeTexture(texture_left);
-                       driver->removeTexture(texture_right);
-
-                       if (rtt == NULL) {
-                               baseimg = generateImage(imagename_top);
-                               return true;
-                       }
-
-                       // Create image of render target
-                       video::IImage *image = driver->createImage(rtt, v2s32(0, 0), params.dim);
-                       assert(image);
-
-                       // Cleanup texture
-                       driver->removeTexture(rtt);
-
-                       baseimg = driver->createImage(video::ECF_A8R8G8B8, params.dim);
-
-                       if (image) {
-                               image->copyTo(baseimg);
-                               image->drop();
-                       }
-               }
-               /*
-                       [lowpart:percent:filename
-                       Adds the lower part of a texture
-               */
-               else if (part_of_name.substr(0,9) == "[lowpart:")
-               {
-                       Strfnd sf(part_of_name);
-                       sf.next(":");
-                       u32 percent = stoi(sf.next(":"));
-                       std::string filename = sf.next(":");
-                       //infostream<<"power part "<<percent<<"%% of "<<filename<<std::endl;
-
-                       if (baseimg == NULL)
-                               baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16));
-                       video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
-                       if (img)
-                       {
-                               core::dimension2d<u32> dim = img->getDimension();
-                               core::position2d<s32> pos_base(0, 0);
-                               video::IImage *img2 =
-                                               driver->createImage(video::ECF_A8R8G8B8, dim);
-                               img->copyTo(img2);
-                               img->drop();
-                               core::position2d<s32> clippos(0, 0);
-                               clippos.Y = dim.Height * (100-percent) / 100;
-                               core::dimension2d<u32> clipdim = dim;
-                               clipdim.Height = clipdim.Height * percent / 100 + 1;
-                               core::rect<s32> cliprect(clippos, clipdim);
-                               img2->copyToWithAlpha(baseimg, pos_base,
-                                               core::rect<s32>(v2s32(0,0), dim),
-                                               video::SColor(255,255,255,255),
-                                               &cliprect);
-                               img2->drop();
-                       }
-               }
-               /*
-                       [verticalframe:N:I
-                       Crops a frame of a vertical animation.
-                       N = frame count, I = frame index
-               */
-               else if (part_of_name.substr(0,15) == "[verticalframe:")
-               {
-                       Strfnd sf(part_of_name);
-                       sf.next(":");
-                       u32 frame_count = stoi(sf.next(":"));
-                       u32 frame_index = stoi(sf.next(":"));
-
-                       if (baseimg == NULL){
-                               errorstream<<"generateImagePart(): baseimg != NULL "
-                                               <<"for part_of_name=\""<<part_of_name
-                                               <<"\", cancelling."<<std::endl;
-                               return false;
-                       }
-
-                       v2u32 frame_size = baseimg->getDimension();
-                       frame_size.Y /= frame_count;
-
-                       video::IImage *img = driver->createImage(video::ECF_A8R8G8B8,
-                                       frame_size);
-                       if (!img){
-                               errorstream<<"generateImagePart(): Could not create image "
-                                               <<"for part_of_name=\""<<part_of_name
-                                               <<"\", cancelling."<<std::endl;
-                               return false;
-                       }
-
-                       // Fill target image with transparency
-                       img->fill(video::SColor(0,0,0,0));
-
-                       core::dimension2d<u32> dim = frame_size;
-                       core::position2d<s32> pos_dst(0, 0);
-                       core::position2d<s32> pos_src(0, frame_index * frame_size.Y);
-                       baseimg->copyToWithAlpha(img, pos_dst,
-                                       core::rect<s32>(pos_src, dim),
-                                       video::SColor(255,255,255,255),
-                                       NULL);
-                       // Replace baseimg
-                       baseimg->drop();
-                       baseimg = img;
-               }
-               /*
-                       [mask:filename
-                       Applies a mask to an image
-               */
-               else if (part_of_name.substr(0,6) == "[mask:")
-               {
-                       if (baseimg == NULL) {
-                               errorstream << "generateImage(): baseimg == NULL "
-                                               << "for part_of_name=\"" << part_of_name
-                                               << "\", cancelling." << std::endl;
-                               return false;
-                       }
-                       Strfnd sf(part_of_name);
-                       sf.next(":");
-                       std::string filename = sf.next(":");
-
-                       video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
-                       if (img) {
-                               apply_mask(img, baseimg, v2s32(0, 0), v2s32(0, 0),
-                                               img->getDimension());
-                       } else {
-                               errorstream << "generateImage(): Failed to load \""
-                                               << filename << "\".";
-                       }
-               }
-               /*
-                       [colorize:color
-                       Overlays image with given color
-                       color = color as ColorString
-               */
-               else if (part_of_name.substr(0,10) == "[colorize:") {
-                       Strfnd sf(part_of_name);
-                       sf.next(":");
-                       std::string color_str = sf.next(":");
-                       std::string ratio_str = sf.next(":");
-
-                       if (baseimg == NULL) {
-                               errorstream << "generateImagePart(): baseimg != NULL "
-                                               << "for part_of_name=\"" << part_of_name
-                                               << "\", cancelling." << std::endl;
-                               return false;
-                       }
-
-                       video::SColor color;
-                       int ratio = -1;
-
-                       if (!parseColorString(color_str, color, false))
-                               return false;
-
-                       if (is_number(ratio_str))
-                               ratio = mystoi(ratio_str, 0, 255);
-
-                       core::dimension2d<u32> dim = baseimg->getDimension();
-                       video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim);
-
-                       if (!img) {
-                               errorstream << "generateImagePart(): Could not create image "
-                                               << "for part_of_name=\"" << part_of_name
-                                               << "\", cancelling." << std::endl;
-                               return false;
-                       }
-
-                       img->fill(video::SColor(color));
-                       // Overlay the colored image
-                       blit_with_interpolate_overlay(img, baseimg, v2s32(0,0), v2s32(0,0), dim, ratio);
-                       img->drop();
-               }
-               else
-               {
-                       errorstream << "generateImagePart(): Invalid "
-                                       " modification: \"" << part_of_name << "\"" << std::endl;
-               }
-       }
-
-       return true;
-}
-
-/*
-       Draw an image on top of an another one, using the alpha channel of the
-       source image
-
-       This exists because IImage::copyToWithAlpha() doesn't seem to always
-       work.
-*/
-static void blit_with_alpha(video::IImage *src, video::IImage *dst,
-               v2s32 src_pos, v2s32 dst_pos, v2u32 size)
-{
-       for (u32 y0=0; y0<size.Y; y0++)
-       for (u32 x0=0; x0<size.X; x0++)
-       {
-               s32 src_x = src_pos.X + x0;
-               s32 src_y = src_pos.Y + y0;
-               s32 dst_x = dst_pos.X + x0;
-               s32 dst_y = dst_pos.Y + y0;
-               video::SColor src_c = src->getPixel(src_x, src_y);
-               video::SColor dst_c = dst->getPixel(dst_x, dst_y);
-               dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
-               dst->setPixel(dst_x, dst_y, dst_c);
-       }
-}
-
-/*
-       Draw an image on top of an another one, using the alpha channel of the
-       source image; only modify fully opaque pixels in destinaion
-*/
-static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
-               v2s32 src_pos, v2s32 dst_pos, v2u32 size)
-{
-       for (u32 y0=0; y0<size.Y; y0++)
-       for (u32 x0=0; x0<size.X; x0++)
-       {
-               s32 src_x = src_pos.X + x0;
-               s32 src_y = src_pos.Y + y0;
-               s32 dst_x = dst_pos.X + x0;
-               s32 dst_y = dst_pos.Y + y0;
-               video::SColor src_c = src->getPixel(src_x, src_y);
-               video::SColor dst_c = dst->getPixel(dst_x, dst_y);
-               if (dst_c.getAlpha() == 255 && src_c.getAlpha() != 0)
-               {
-                       dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
-                       dst->setPixel(dst_x, dst_y, dst_c);
-               }
-       }
-}
-
-/*
-       Draw an image on top of an another one, using the specified ratio
-       modify all partially-opaque pixels in the destination.
-*/
-static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst,
-               v2s32 src_pos, v2s32 dst_pos, v2u32 size, int ratio)
-{
-       for (u32 y0 = 0; y0 < size.Y; y0++)
-       for (u32 x0 = 0; x0 < size.X; x0++)
-       {
-               s32 src_x = src_pos.X + x0;
-               s32 src_y = src_pos.Y + y0;
-               s32 dst_x = dst_pos.X + x0;
-               s32 dst_y = dst_pos.Y + y0;
-               video::SColor src_c = src->getPixel(src_x, src_y);
-               video::SColor dst_c = dst->getPixel(dst_x, dst_y);
-               if (dst_c.getAlpha() > 0 && src_c.getAlpha() != 0)
-               {
-                       if (ratio == -1)
-                               dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
-                       else
-                               dst_c = src_c.getInterpolated(dst_c, (float)ratio/255.0f);
-                       dst->setPixel(dst_x, dst_y, dst_c);
-               }
-       }
-}
-
-/*
-       Apply mask to destination
-*/
-static void apply_mask(video::IImage *mask, video::IImage *dst,
-               v2s32 mask_pos, v2s32 dst_pos, v2u32 size)
-{
-       for (u32 y0 = 0; y0 < size.Y; y0++) {
-               for (u32 x0 = 0; x0 < size.X; x0++) {
-                       s32 mask_x = x0 + mask_pos.X;
-                       s32 mask_y = y0 + mask_pos.Y;
-                       s32 dst_x = x0 + dst_pos.X;
-                       s32 dst_y = y0 + dst_pos.Y;
-                       video::SColor mask_c = mask->getPixel(mask_x, mask_y);
-                       video::SColor dst_c = dst->getPixel(dst_x, dst_y);
-                       dst_c.color &= mask_c.color;
-                       dst->setPixel(dst_x, dst_y, dst_c);
-               }
-       }
-}
-
-static void draw_crack(video::IImage *crack, video::IImage *dst,
-               bool use_overlay, s32 frame_count, s32 progression,
-               video::IVideoDriver *driver)
-{
-       // Dimension of destination image
-       core::dimension2d<u32> dim_dst = dst->getDimension();
-       // Dimension of original image
-       core::dimension2d<u32> dim_crack = crack->getDimension();
-       // Count of crack stages
-       s32 crack_count = dim_crack.Height / dim_crack.Width;
-       // Limit frame_count
-       if (frame_count > (s32) dim_dst.Height)
-               frame_count = dim_dst.Height;
-       if (frame_count < 1)
-               frame_count = 1;
-       // Limit progression
-       if (progression > crack_count-1)
-               progression = crack_count-1;
-       // Dimension of a single crack stage
-       core::dimension2d<u32> dim_crack_cropped(
-               dim_crack.Width,
-               dim_crack.Width
-       );
-       // Dimension of the scaled crack stage,
-       // which is the same as the dimension of a single destination frame
-       core::dimension2d<u32> dim_crack_scaled(
-               dim_dst.Width,
-               dim_dst.Height / frame_count
-       );
-       // Create cropped and scaled crack images
-       video::IImage *crack_cropped = driver->createImage(
-                       video::ECF_A8R8G8B8, dim_crack_cropped);
-       video::IImage *crack_scaled = driver->createImage(
-                       video::ECF_A8R8G8B8, dim_crack_scaled);
-
-       if (crack_cropped && crack_scaled)
-       {
-               // Crop crack image
-               v2s32 pos_crack(0, progression*dim_crack.Width);
-               crack->copyTo(crack_cropped,
-                               v2s32(0,0),
-                               core::rect<s32>(pos_crack, dim_crack_cropped));
-               // Scale crack image by copying
-               crack_cropped->copyToScaling(crack_scaled);
-               // Copy or overlay crack image onto each frame
-               for (s32 i = 0; i < frame_count; ++i)
-               {
-                       v2s32 dst_pos(0, dim_crack_scaled.Height * i);
-                       if (use_overlay)
-                       {
-                               blit_with_alpha_overlay(crack_scaled, dst,
-                                               v2s32(0,0), dst_pos,
-                                               dim_crack_scaled);
-                       }
-                       else
-                       {
-                               blit_with_alpha(crack_scaled, dst,
-                                               v2s32(0,0), dst_pos,
-                                               dim_crack_scaled);
-                       }
-               }
-       }
-
-       if (crack_scaled)
-               crack_scaled->drop();
-
-       if (crack_cropped)
-               crack_cropped->drop();
-}
-
-void brighten(video::IImage *image)
-{
-       if (image == NULL)
-               return;
-
-       core::dimension2d<u32> dim = image->getDimension();
-
-       for (u32 y=0; y<dim.Height; y++)
-       for (u32 x=0; x<dim.Width; x++)
-       {
-               video::SColor c = image->getPixel(x,y);
-               c.setRed(0.5 * 255 + 0.5 * (float)c.getRed());
-               c.setGreen(0.5 * 255 + 0.5 * (float)c.getGreen());
-               c.setBlue(0.5 * 255 + 0.5 * (float)c.getBlue());
-               image->setPixel(x,y,c);
-       }
-}
-
-u32 parseImageTransform(const std::string& s)
-{
-       int total_transform = 0;
-
-       std::string transform_names[8];
-       transform_names[0] = "i";
-       transform_names[1] = "r90";
-       transform_names[2] = "r180";
-       transform_names[3] = "r270";
-       transform_names[4] = "fx";
-       transform_names[6] = "fy";
-
-       std::size_t pos = 0;
-       while(pos < s.size())
-       {
-               int transform = -1;
-               for (int i = 0; i <= 7; ++i)
-               {
-                       const std::string &name_i = transform_names[i];
-
-                       if (s[pos] == ('0' + i))
-                       {
-                               transform = i;
-                               pos++;
-                               break;
-                       }
-                       else if (!(name_i.empty()) &&
-                               lowercase(s.substr(pos, name_i.size())) == name_i)
-                       {
-                               transform = i;
-                               pos += name_i.size();
-                               break;
-                       }
-               }
-               if (transform < 0)
-                       break;
-
-               // Multiply total_transform and transform in the group D4
-               int new_total = 0;
-               if (transform < 4)
-                       new_total = (transform + total_transform) % 4;
-               else
-                       new_total = (transform - total_transform + 8) % 4;
-               if ((transform >= 4) ^ (total_transform >= 4))
-                       new_total += 4;
-
-               total_transform = new_total;
-       }
-       return total_transform;
-}
-
-core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<u32> dim)
-{
-       if (transform % 2 == 0)
-               return dim;
-       else
-               return core::dimension2d<u32>(dim.Height, dim.Width);
-}
-
-void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
-{
-       if (src == NULL || dst == NULL)
-               return;
-
-       core::dimension2d<u32> srcdim = src->getDimension();
-       core::dimension2d<u32> dstdim = dst->getDimension();
-
-       assert(dstdim == imageTransformDimension(transform, srcdim));
-       assert(transform <= 7);
-
-       /*
-               Compute the transformation from source coordinates (sx,sy)
-               to destination coordinates (dx,dy).
-       */
-       int sxn = 0;
-       int syn = 2;
-       if (transform == 0)         // identity
-               sxn = 0, syn = 2;  //   sx = dx, sy = dy
-       else if (transform == 1)    // rotate by 90 degrees ccw
-               sxn = 3, syn = 0;  //   sx = (H-1) - dy, sy = dx
-       else if (transform == 2)    // rotate by 180 degrees
-               sxn = 1, syn = 3;  //   sx = (W-1) - dx, sy = (H-1) - dy
-       else if (transform == 3)    // rotate by 270 degrees ccw
-               sxn = 2, syn = 1;  //   sx = dy, sy = (W-1) - dx
-       else if (transform == 4)    // flip x
-               sxn = 1, syn = 2;  //   sx = (W-1) - dx, sy = dy
-       else if (transform == 5)    // flip x then rotate by 90 degrees ccw
-               sxn = 2, syn = 0;  //   sx = dy, sy = dx
-       else if (transform == 6)    // flip y
-               sxn = 0, syn = 3;  //   sx = dx, sy = (H-1) - dy
-       else if (transform == 7)    // flip y then rotate by 90 degrees ccw
-               sxn = 3, syn = 1;  //   sx = (H-1) - dy, sy = (W-1) - dx
-
-       for (u32 dy=0; dy<dstdim.Height; dy++)
-       for (u32 dx=0; dx<dstdim.Width; dx++)
-       {
-               u32 entries[4] = {dx, dstdim.Width-1-dx, dy, dstdim.Height-1-dy};
-               u32 sx = entries[sxn];
-               u32 sy = entries[syn];
-               video::SColor c = src->getPixel(sx,sy);
-               dst->setPixel(dx,dy,c);
-       }
-}
-
-video::ITexture* TextureSource::getNormalTexture(const std::string &name)
-{
-       u32 id;
-       if (isKnownSourceImage("override_normal.png"))
-               return getTexture("override_normal.png", &id);
-       std::string fname_base = name;
-       std::string normal_ext = "_normal.png";
-       size_t pos = fname_base.find(".");
-       std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
-       if (isKnownSourceImage(fname_normal)) {
-               // look for image extension and replace it
-               size_t i = 0;
-               while ((i = fname_base.find(".", i)) != std::string::npos) {
-                       fname_base.replace(i, 4, normal_ext);
-                       i += normal_ext.length();
-               }
-               return getTexture(fname_base, &id);
-               }
-       return NULL;
-}
diff --git a/src/tile.h b/src/tile.h
deleted file mode 100644 (file)
index ea7a913..0000000
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifndef TILE_HEADER
-#define TILE_HEADER
-
-#include "irrlichttypes.h"
-#include "irr_v2d.h"
-#include "irr_v3d.h"
-#include <ITexture.h>
-#include <IrrlichtDevice.h>
-#include "threads.h"
-#include <string>
-#include <vector>
-
-class IGameDef;
-
-/*
-       tile.{h,cpp}: Texture handling stuff.
-*/
-
-/*
-       Find out the full path of an image by trying different filename
-       extensions.
-
-       If failed, return "".
-
-       TODO: Should probably be moved out from here, because things needing
-             this function do not need anything else from this header
-*/
-std::string getImagePath(std::string path);
-
-/*
-       Gets the path to a texture by first checking if the texture exists
-       in texture_path and if not, using the data path.
-
-       Checks all supported extensions by replacing the original extension.
-
-       If not found, returns "".
-
-       Utilizes a thread-safe cache.
-*/
-std::string getTexturePath(const std::string &filename);
-
-void clearTextureNameCache();
-
-/*
-       ITextureSource::generateTextureFromMesh parameters
-*/
-namespace irr {namespace scene {class IMesh;}}
-struct TextureFromMeshParams
-{
-       scene::IMesh *mesh;
-       core::dimension2d<u32> dim;
-       std::string rtt_texture_name;
-       bool delete_texture_on_shutdown;
-       v3f camera_position;
-       v3f camera_lookat;
-       core::CMatrix4<f32> camera_projection_matrix;
-       video::SColorf ambient_light;
-       v3f light_position;
-       video::SColorf light_color;
-       f32 light_radius;
-};
-
-/*
-       TextureSource creates and caches textures.
-*/
-
-class ISimpleTextureSource
-{
-public:
-       ISimpleTextureSource(){}
-       virtual ~ISimpleTextureSource(){}
-       virtual video::ITexture* getTexture(
-                       const std::string &name, u32 *id = NULL) = 0;
-};
-
-class ITextureSource : public ISimpleTextureSource
-{
-public:
-       ITextureSource(){}
-       virtual ~ITextureSource(){}
-       virtual u32 getTextureId(const std::string &name)=0;
-       virtual std::string getTextureName(u32 id)=0;
-       virtual video::ITexture* getTexture(u32 id)=0;
-       virtual video::ITexture* getTexture(
-                       const std::string &name, u32 *id = NULL)=0;
-       virtual IrrlichtDevice* getDevice()=0;
-       virtual bool isKnownSourceImage(const std::string &name)=0;
-       virtual video::ITexture* generateTextureFromMesh(
-                       const TextureFromMeshParams &params)=0;
-       virtual video::ITexture* getNormalTexture(const std::string &name)=0;
-};
-
-class IWritableTextureSource : public ITextureSource
-{
-public:
-       IWritableTextureSource(){}
-       virtual ~IWritableTextureSource(){}
-       virtual u32 getTextureId(const std::string &name)=0;
-       virtual std::string getTextureName(u32 id)=0;
-       virtual video::ITexture* getTexture(u32 id)=0;
-       virtual video::ITexture* getTexture(
-                       const std::string &name, u32 *id = NULL)=0;
-       virtual IrrlichtDevice* getDevice()=0;
-       virtual bool isKnownSourceImage(const std::string &name)=0;
-       virtual video::ITexture* generateTextureFromMesh(
-                       const TextureFromMeshParams &params)=0;
-
-       virtual void processQueue()=0;
-       virtual void insertSourceImage(const std::string &name, video::IImage *img)=0;
-       virtual void rebuildImagesAndTextures()=0;
-       virtual video::ITexture* getNormalTexture(const std::string &name)=0;
-};
-
-IWritableTextureSource* createTextureSource(IrrlichtDevice *device);
-
-#ifdef __ANDROID__
-/**
- * @param size get next npot2 value
- * @return npot2 value
- */
-inline unsigned int npot2(unsigned int size)
-{
-       if (size == 0) return 0;
-       unsigned int npot = 1;
-
-       while ((size >>= 1) > 0) {
-               npot <<= 1;
-       }
-       return npot;
-}
-
-video::IImage * Align2Npot2(video::IImage * image, video::IVideoDriver* driver);
-#endif
-
-enum MaterialType{
-       TILE_MATERIAL_BASIC,
-       TILE_MATERIAL_ALPHA,
-       TILE_MATERIAL_LIQUID_TRANSPARENT,
-       TILE_MATERIAL_LIQUID_OPAQUE,
-       TILE_MATERIAL_WAVING_LEAVES,
-       TILE_MATERIAL_WAVING_PLANTS
-};
-
-// Material flags
-// Should backface culling be enabled?
-#define MATERIAL_FLAG_BACKFACE_CULLING 0x01
-// Should a crack be drawn?
-#define MATERIAL_FLAG_CRACK 0x02
-// Should the crack be drawn on transparent pixels (unset) or not (set)?
-// Ignored if MATERIAL_FLAG_CRACK is not set.
-#define MATERIAL_FLAG_CRACK_OVERLAY 0x04
-// Animation made up by splitting the texture to vertical frames, as
-// defined by extra parameters
-#define MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES 0x08
-#define MATERIAL_FLAG_HIGHLIGHTED 0x10
-
-/*
-       This fully defines the looks of a tile.
-       The SMaterial of a tile is constructed according to this.
-*/
-struct FrameSpec
-{
-       FrameSpec():
-               texture_id(0),
-               texture(NULL),
-               normal_texture(NULL)
-       {
-       }
-       u32 texture_id;
-       video::ITexture *texture;
-       video::ITexture *normal_texture;
-};
-
-struct TileSpec
-{
-       TileSpec():
-               texture_id(0),
-               texture(NULL),
-               normal_texture(NULL),
-               alpha(255),
-               material_type(TILE_MATERIAL_BASIC),
-               material_flags(
-                       //0 // <- DEBUG, Use the one below
-                       MATERIAL_FLAG_BACKFACE_CULLING
-               ),
-               shader_id(0),
-               animation_frame_count(1),
-               animation_frame_length_ms(0),
-               rotation(0)
-       {
-       }
-
-       bool operator==(const TileSpec &other) const
-       {
-               return (
-                       texture_id == other.texture_id &&
-                       /* texture == other.texture && */
-                       alpha == other.alpha &&
-                       material_type == other.material_type &&
-                       material_flags == other.material_flags &&
-                       rotation == other.rotation
-               );
-       }
-
-       bool operator!=(const TileSpec &other) const
-       {
-               return !(*this == other);
-       }
-       
-       // Sets everything else except the texture in the material
-       void applyMaterialOptions(video::SMaterial &material) const
-       {
-               switch (material_type) {
-               case TILE_MATERIAL_BASIC:
-                       material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       break;
-               case TILE_MATERIAL_ALPHA:
-                       material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
-                       break;
-               case TILE_MATERIAL_LIQUID_TRANSPARENT:
-                       material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
-                       break;
-               case TILE_MATERIAL_LIQUID_OPAQUE:
-                       material.MaterialType = video::EMT_SOLID;
-                       break;
-               case TILE_MATERIAL_WAVING_LEAVES:
-                       material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       break;
-               case TILE_MATERIAL_WAVING_PLANTS:
-                       material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       break;
-               }
-               material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING)
-                       ? true : false;
-       }
-
-       void applyMaterialOptionsWithShaders(video::SMaterial &material) const
-       {
-               material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING)
-                       ? true : false;
-       }
-       
-       u32 texture_id;
-       video::ITexture *texture;
-       video::ITexture *normal_texture;
-       
-       // Vertex alpha (when MATERIAL_ALPHA_VERTEX is used)
-       u8 alpha;
-       // Material parameters
-       u8 material_type;
-       u8 material_flags;
-       u32 shader_id;
-       // Animation parameters
-       u8 animation_frame_count;
-       u16 animation_frame_length_ms;
-       std::vector<FrameSpec> frames;
-
-       u8 rotation;
-};
-
-#endif
index 4fe731513d24931860ef5fad3f0d2ca39675141f..a00df5c47e1971d3602fe7c1c0b7e18bef28c883 100644 (file)
@@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <map>
 
 #include "game.h"
-#include "tile.h"
+#include "client/tile.h"
 
 using namespace irr;
 using namespace irr::core;
index 7c16bddcbdb282af08ac465ab970c5f931013288..a2be55544f945c720ec192a18076928709d04cbb 100644 (file)
@@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "nodedef.h"
 #include "mesh.h"
 #include "mapblock_mesh.h"
-#include "tile.h"
+#include "client/tile.h"
 #include "log.h"
 #include "util/numeric.h"
 #include <map>