]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/tile.cpp
Hide pause menu prior drawing "shutting down..." message
[dragonfireclient.git] / src / tile.cpp
index da286fda943df837a449200ff1135e45c992a3d5..310630b84a842a98b2e2802fe792ad8908eb1f76 100644 (file)
@@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "tile.h"
+#include "irrlichttypes_extrabloated.h"
 #include "debug.h"
 #include "main.h" // for g_settings
 #include "filesys.h"
@@ -57,7 +58,7 @@ static bool replace_ext(std::string &path, const char *ext)
                        last_dot_i = i;
                        break;
                }
-               
+
                if(path[i] == '\\' || path[i] == '/')
                        break;
        }
@@ -96,7 +97,7 @@ std::string getImagePath(std::string path)
                        return path;
        }
        while((++ext) != NULL);
-       
+
        return "";
 }
 
@@ -119,7 +120,7 @@ std::string getTexturePath(const std::string &filename)
        bool incache = g_texturename_to_path_cache.get(filename, &fullpath);
        if(incache)
                return fullpath;
-       
+
        /*
                Check from texture_path
        */
@@ -130,18 +131,6 @@ std::string getTexturePath(const std::string &filename)
                // Check all filename extensions. Returns "" if not found.
                fullpath = getImagePath(testpath);
        }
-       
-       /*
-               Check from $user/textures/all
-       */
-       if(fullpath == "")
-       {
-               std::string texture_path = porting::path_user + DIR_DELIM
-                               + "textures" + DIR_DELIM + "all";
-               std::string testpath = texture_path + DIR_DELIM + filename;
-               // Check all filename extensions. Returns "" if not found.
-               fullpath = getImagePath(testpath);
-       }
 
        /*
                Check from default data directory
@@ -154,14 +143,19 @@ std::string getTexturePath(const std::string &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.
 */
@@ -308,14 +302,14 @@ class TextureSource : public IWritableTextureSource
                  getTextureId("stone.png^mineral_coal.png^crack0").
 
        */
-       
+
        /*
                Gets a texture id from cache or
                - if main thread, from getTextureIdDirect
                - if other thread, adds to request queue and waits for main thread
        */
        u32 getTextureId(const std::string &name);
-       
+
        /*
                Example names:
                "stone.png"
@@ -369,22 +363,38 @@ class TextureSource : public IWritableTextureSource
        // 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* generateImageFromScratch(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.
+       // Shall be called from the main thread.
+       bool generateImage(std::string part_of_name, video::IImage *& baseimg);
+
 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;
@@ -399,13 +409,18 @@ class TextureSource : public IWritableTextureSource
        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)
@@ -417,14 +432,19 @@ TextureSource::TextureSource(IrrlichtDevice *device):
                m_device(device)
 {
        assert(m_device);
-       
-       m_textureinfo_cache_mutex.Init();
-       
+
        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()
@@ -477,7 +497,7 @@ u32 TextureSource::getTextureId(const std::string &name)
                        return n->second;
                }
        }
-       
+
        /*
                Get texture
        */
@@ -490,45 +510,53 @@ u32 TextureSource::getTextureId(const std::string &name)
                infostream<<"getTextureId(): Queued: name=\""<<name<<"\""<<std::endl;
 
                // We're gonna ask the result to be put into here
-               ResultQueue<std::string, u32, u8, u8> result_queue;
-               
+               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;
-               
+
+               /*infostream<<"Waiting for texture from main thread, name=\""
+                               <<name<<"\""<<std::endl;*/
+
                try
                {
-                       // Wait result for a second
-                       GetResult<std::string, u32, u8, u8>
+                       while(true) {
+                               // Wait result for a second
+                               GetResult<std::string, u32, u8, u8>
                                        result = result_queue.pop_front(1000);
-               
-                       // Check that at least something worked OK
-                       assert(result.key == name);
 
-                       return result.item;
+                               if (result.key == name) {
+                                       return result.item;
+                               }
+                       }
                }
                catch(ItemNotFoundException &e)
                {
-                       infostream<<"Waiting for texture timed out."<<std::endl;
+                       errorstream<<"Waiting for texture " << name << " timed out."<<std::endl;
                        return 0;
                }
        }
-       
+
        infostream<<"getTextureId(): Failed"<<std::endl;
 
        return 0;
 }
 
-// Overlay image on top of another image (used for cracks)
-void overlay(video::IImage *image, video::IImage *overlay);
-
 // 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);
+
+// 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
@@ -538,20 +566,6 @@ core::dimension2d<u32> imageTransformDimension(u32 transform, core::dimension2d<
 // Apply transform to image data
 void imageTransform(u32 transform, video::IImage *src, video::IImage *dst);
 
-/*
-       Generate image based on a string like "stone.png" or "[crack0".
-       if baseimg is NULL, it is created. Otherwise stuff is made on it.
-*/
-bool generate_image(std::string part_of_name, video::IImage *& baseimg,
-               IrrlichtDevice *device, SourceImageCache *sourcecache);
-
-/*
-       Generates an image from a full string like
-       "stone.png^mineral_coal.png^[crack0".
-*/
-video::IImage* generate_image_from_scratch(std::string name,
-               IrrlichtDevice *device, SourceImageCache *sourcecache);
-
 /*
        This method generates all the textures
 */
@@ -565,7 +579,7 @@ u32 TextureSource::getTextureIdDirect(const std::string &name)
                infostream<<"getTextureIdDirect(): name is empty"<<std::endl;
                return 0;
        }
-       
+
        /*
                Calling only allowed from main thread
        */
@@ -594,7 +608,7 @@ u32 TextureSource::getTextureIdDirect(const std::string &name)
 
        /*infostream<<"getTextureIdDirect(): \""<<name
                        <<"\" NOT found in cache. Creating it."<<std::endl;*/
-       
+
        /*
                Get the base image
        */
@@ -607,7 +621,7 @@ u32 TextureSource::getTextureIdDirect(const std::string &name)
                is made.
        */
        u32 base_image_id = 0;
-       
+
        // Find last meta separator in name
        s32 last_separator_position = -1;
        for(s32 i=name.size()-1; i>=0; i--)
@@ -632,9 +646,9 @@ u32 TextureSource::getTextureIdDirect(const std::string &name)
                 <<base_image_name<<"\""<<std::endl;*/
                base_image_id = getTextureIdDirect(base_image_name);
        }
-       
+
        //infostream<<"base_image_id="<<base_image_id<<std::endl;
-       
+
        video::IVideoDriver* driver = m_device->getVideoDriver();
        assert(driver);
 
@@ -644,14 +658,14 @@ u32 TextureSource::getTextureIdDirect(const std::string &name)
                An image will be built from files and then converted into a texture.
        */
        video::IImage *baseimg = NULL;
-       
+
        // If a base image was found, copy it to baseimg
        if(base_image_id != 0)
        {
                JMutexAutoLock lock(m_textureinfo_cache_mutex);
 
                TextureInfo *ti = &m_textureinfo_cache[base_image_id];
-               
+
                if(ti->img == NULL)
                {
                        infostream<<"getTextureIdDirect(): WARNING: NULL image in "
@@ -675,7 +689,7 @@ u32 TextureSource::getTextureIdDirect(const std::string &name)
                                        <<std::endl;*/
                }
        }
-       
+
        /*
                Parse out the last part of the name of the image and act
                according to it
@@ -685,7 +699,7 @@ u32 TextureSource::getTextureIdDirect(const std::string &name)
        //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
 
        // Generate image according to part of name
-       if(!generate_image(last_part_of_name, baseimg, m_device, &m_sourcecache))
+       if(!generateImage(last_part_of_name, baseimg))
        {
                errorstream<<"getTextureIdDirect(): "
                                "failed to generate \""<<last_part_of_name<<"\""
@@ -698,27 +712,24 @@ u32 TextureSource::getTextureIdDirect(const std::string &name)
                errorstream<<"getTextureIdDirect(): baseimg is NULL (attempted to"
                                " create texture \""<<name<<"\""<<std::endl;
        }
-       
+
        if(baseimg != NULL)
        {
                // Create texture from resulting image
                t = driver->addTexture(name.c_str(), baseimg);
        }
-       
+
        /*
                Add texture to caches (add NULL textures too)
        */
 
        JMutexAutoLock lock(m_textureinfo_cache_mutex);
-       
+
        u32 id = m_textureinfo_cache.size();
        TextureInfo ti(name, t, baseimg);
        m_textureinfo_cache.push_back(ti);
        m_name_to_id[name] = id;
 
-       /*infostream<<"getTextureIdDirect(): "
-                       <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
-       
        return id;
 }
 
@@ -733,7 +744,7 @@ std::string TextureSource::getTextureName(u32 id)
                                <<m_textureinfo_cache.size()<<std::endl;
                return "";
        }
-       
+
        return m_textureinfo_cache[id].name;
 }
 
@@ -761,6 +772,7 @@ 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>
@@ -771,26 +783,20 @@ void TextureSource::processQueue()
                                <<"name=\""<<request.key<<"\""
                                <<std::endl;*/
 
-               GetResult<std::string, u32, u8, u8>
-                               result;
-               result.key = request.key;
-               result.callers = request.callers;
-               result.item = getTextureIdDirect(request.key);
-
-               request.dest->push_back(result);
+               m_get_texture_queue.pushResult(request,getTextureIdDirect(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);
@@ -800,8 +806,7 @@ void TextureSource::rebuildImagesAndTextures()
        // Recreate textures
        for(u32 i=0; i<m_textureinfo_cache.size(); i++){
                TextureInfo *ti = &m_textureinfo_cache[i];
-               video::IImage *img =
-                       generate_image_from_scratch(ti->name, m_device, &m_sourcecache);
+               video::IImage *img = generateImageFromScratch(ti->name);
                // Create texture from resulting image
                video::ITexture *t = NULL;
                if(img)
@@ -816,13 +821,90 @@ void TextureSource::rebuildImagesAndTextures()
        }
 }
 
-video::IImage* generate_image_from_scratch(std::string name,
-               IrrlichtDevice *device, SourceImageCache *sourcecache)
+video::ITexture* TextureSource::generateTextureFromMesh(
+               const TextureFromMeshParams &params)
 {
-       /*infostream<<"generate_image_from_scratch(): "
+       video::IVideoDriver *driver = m_device->getVideoDriver();
+       assert(driver);
+
+       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
+       driver->setRenderTarget(rtt, false, true, video::SColor(0,0,0,0));
+
+       // 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();
+
+       // NOTE: The scene nodes should not be dropped, otherwise
+       //       smgr->drop() segfaults
+       /*cube->drop();
+       camera->drop();
+       light->drop();*/
+       // 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::generateImageFromScratch(std::string name)
+{
+       /*infostream<<"generateImageFromScratch(): "
                        "\""<<name<<"\""<<std::endl;*/
-       
-       video::IVideoDriver* driver = device->getVideoDriver();
+
+       video::IVideoDriver *driver = m_device->getVideoDriver();
        assert(driver);
 
        /*
@@ -835,12 +917,6 @@ video::IImage* generate_image_from_scratch(std::string name,
 
        // Find last meta separator in name
        s32 last_separator_position = name.find_last_of(separator);
-       //if(last_separator_position == std::npos)
-       //      last_separator_position = -1;
-
-       /*infostream<<"generate_image_from_scratch(): "
-                       <<"last_separator_position="<<last_separator_position
-                       <<std::endl;*/
 
        /*
                If separator was found, construct the base name and make the
@@ -851,51 +927,65 @@ video::IImage* generate_image_from_scratch(std::string name,
        {
                // Construct base name
                base_image_name = name.substr(0, last_separator_position);
-               /*infostream<<"generate_image_from_scratch(): Calling itself recursively"
-                               " to get base image of \""<<name<<"\" = \""
-                <<base_image_name<<"\""<<std::endl;*/
-               baseimg = generate_image_from_scratch(base_image_name, device,
-                               sourcecache);
+               baseimg = generateImageFromScratch(base_image_name);
        }
-       
+
        /*
                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_position+1);
-       //infostream<<"last_part_of_name=\""<<last_part_of_name<<"\""<<std::endl;
-       
+
        // Generate image according to part of name
-       if(!generate_image(last_part_of_name, baseimg, device, sourcecache))
+       if(!generateImage(last_part_of_name, baseimg))
        {
-               errorstream<<"generate_image_from_scratch(): "
+               errorstream<<"generateImageFromScratch(): "
                                "failed to generate \""<<last_part_of_name<<"\""
                                <<std::endl;
                return NULL;
        }
-       
+
        return baseimg;
 }
 
-bool generate_image(std::string part_of_name, video::IImage *& baseimg,
-               IrrlichtDevice *device, SourceImageCache *sourcecache)
+bool TextureSource::generateImage(std::string part_of_name, video::IImage *& baseimg)
 {
-       video::IVideoDriver* driver = device->getVideoDriver();
+       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 = sourcecache->getOrLoad(part_of_name, device);
+               video::IImage *image = m_sourcecache.getOrLoad(part_of_name, m_device);
 
-               if(image == NULL)
-               {
-                       if(part_of_name != ""){
-                               errorstream<<"generate_image(): Could not load image \""
+               if (image != NULL) {
+                       if (!driver->queryFeature(irr::video::EVDF_TEXTURE_NPOT)) {
+                               core::dimension2d<u32> dim = image->getDimension();
+
+
+                               if ((dim.Height %2 != 0) ||
+                                               (dim.Width %2 != 0)) {
+                                       infostream << "TextureSource::generateImage "
+                                                       << part_of_name << " size npot2 x=" << dim.Width
+                                                       << " y=" << dim.Height << std::endl;
+                               }
+                       }
+               }
+
+               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<<"generate_image(): Creating a dummy"
+                                       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
@@ -923,7 +1013,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        //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 
+                               Otherwise images with alpha cannot be blitted on
                                images that don't have alpha in the original file.
                        */
                        core::dimension2d<u32> dim = image->getDimension();
@@ -955,103 +1045,47 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
        {
                // A special texture modification
 
-               /*infostream<<"generate_image(): generating special "
+               /*infostream<<"generateImage(): generating special "
                                <<"modification \""<<part_of_name<<"\""
                                <<std::endl;*/
-               
+
                /*
-                       [crackN
+                       [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<<"generate_image(): baseimg==NULL "
+                               errorstream<<"generateImage(): baseimg==NULL "
                                                <<"for part_of_name=\""<<part_of_name
                                                <<"\", cancelling."<<std::endl;
                                return false;
                        }
-                       
+
                        // Crack image number and overlay option
-                       s32 progression = 0;
-                       bool use_overlay = false;
-                       if(part_of_name.substr(6,1) == "o")
-                       {
-                               progression = stoi(part_of_name.substr(7));
-                               use_overlay = true;
-                       }
-                       else
-                       {
-                               progression = stoi(part_of_name.substr(6));
-                               use_overlay = false;
-                       }
+                       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(":"));
 
-                       // Size of the base image
-                       core::dimension2d<u32> dim_base = baseimg->getDimension();
-                       
                        /*
                                Load crack image.
 
                                It is an image with a number of cracking stages
                                horizontally tiled.
                        */
-                       video::IImage *img_crack = sourcecache->getOrLoad(
-                                       "crack_anylength.png", device);
-               
+                       video::IImage *img_crack = m_sourcecache.getOrLoad(
+                                       "crack_anylength.png", m_device);
+
                        if(img_crack && progression >= 0)
                        {
-                               // Dimension of original image
-                               core::dimension2d<u32> dim_crack
-                                               = img_crack->getDimension();
-                               // Count of crack stages
-                               s32 crack_count = dim_crack.Height / dim_crack.Width;
-                               // 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
-                               );
-                               // Create cropped and scaled crack images
-                               video::IImage *img_crack_cropped = driver->createImage(
-                                               video::ECF_A8R8G8B8, dim_crack_cropped);
-                               video::IImage *img_crack_scaled = driver->createImage(
-                                               video::ECF_A8R8G8B8, dim_base);
-
-                               if(img_crack_cropped && img_crack_scaled)
-                               {
-                                       // Crop crack image
-                                       v2s32 pos_crack(0, progression*dim_crack.Width);
-                                       img_crack->copyTo(img_crack_cropped,
-                                                       v2s32(0,0),
-                                                       core::rect<s32>(pos_crack, dim_crack_cropped));
-                                       // Scale crack image by copying
-                                       img_crack_cropped->copyToScaling(img_crack_scaled);
-                                       // Copy or overlay crack image
-                                       if(use_overlay)
-                                       {
-                                               overlay(baseimg, img_crack_scaled);
-                                       }
-                                       else
-                                       {
-                                               /*img_crack_scaled->copyToWithAlpha(
-                                                               baseimg,
-                                                               v2s32(0,0),
-                                                               core::rect<s32>(v2s32(0,0), dim_base),
-                                                               video::SColor(255,255,255,255));*/
-                                               blit_with_alpha(img_crack_scaled, baseimg,
-                                                               v2s32(0,0), v2s32(0,0), dim_base);
-                                       }
-                               }
-
-                               if(img_crack_scaled)
-                                       img_crack_scaled->drop();
-
-                               if(img_crack_cropped)
-                                       img_crack_cropped->drop();
-                               
+                               draw_crack(img_crack, baseimg,
+                                               use_overlay, frame_count,
+                                               progression, driver);
                                img_crack->drop();
                        }
                }
@@ -1080,7 +1114,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                                infostream<<"Adding \""<<filename
                                                <<"\" to combined ("<<x<<","<<y<<")"
                                                <<std::endl;
-                               video::IImage *img = sourcecache->getOrLoad(filename, device);
+                               video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
                                if(img)
                                {
                                        core::dimension2d<u32> dim = img->getDimension();
@@ -1111,7 +1145,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                {
                        if(baseimg == NULL)
                        {
-                               errorstream<<"generate_image(): baseimg==NULL "
+                               errorstream<<"generateImage(): baseimg==NULL "
                                                <<"for part_of_name=\""<<part_of_name
                                                <<"\", cancelling."<<std::endl;
                                return false;
@@ -1123,21 +1157,21 @@ bool generate_image(std::string part_of_name, video::IImage *& 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 
+                       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<<"generate_image(): baseimg==NULL "
+                               errorstream<<"generateImage(): 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++)
@@ -1155,7 +1189,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                {
                        if(baseimg == NULL)
                        {
-                               errorstream<<"generate_image(): baseimg==NULL "
+                               errorstream<<"generateImage(): baseimg==NULL "
                                                <<"for part_of_name=\""<<part_of_name
                                                <<"\", cancelling."<<std::endl;
                                return false;
@@ -1168,7 +1202,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        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);
@@ -1212,7 +1246,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                {
                        if(baseimg == NULL)
                        {
-                               errorstream<<"generate_image(): baseimg==NULL "
+                               errorstream<<"generateImage(): baseimg==NULL "
                                                <<"for part_of_name=\""<<part_of_name
                                                <<"\", cancelling."<<std::endl;
                                return false;
@@ -1240,7 +1274,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                {
                        if(baseimg != NULL)
                        {
-                               errorstream<<"generate_image(): baseimg!=NULL "
+                               errorstream<<"generateImage(): baseimg!=NULL "
                                                <<"for part_of_name=\""<<part_of_name
                                                <<"\", cancelling."<<std::endl;
                                return false;
@@ -1254,12 +1288,12 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        std::string imagename_right = sf.next("{");
 
                        // Generate images for the faces of the cube
-                       video::IImage *img_top = generate_image_from_scratch(
-                                       imagename_top, device, sourcecache);
-                       video::IImage *img_left = generate_image_from_scratch(
-                                       imagename_left, device, sourcecache);
-                       video::IImage *img_right = generate_image_from_scratch(
-                                       imagename_right, device, sourcecache);
+                       video::IImage *img_top =
+                               generateImageFromScratch(imagename_top);
+                       video::IImage *img_left =
+                               generateImageFromScratch(imagename_left);
+                       video::IImage *img_right =
+                               generateImageFromScratch(imagename_right);
                        assert(img_top && img_left && img_right);
 
                        // Create textures from images
@@ -1275,7 +1309,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        img_top->drop();
                        img_left->drop();
                        img_right->drop();
-                       
+
                        /*
                                Draw a cube mesh into a render target texture
                        */
@@ -1288,32 +1322,26 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left);
                        cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left);
 
-                       core::dimension2d<u32> dim(64,64);
-                       std::string rtt_texture_name = part_of_name + "_RTT";
-
-                       v3f camera_position(0, 1.0, -1.5);
-                       camera_position.rotateXZBy(45);
-                       v3f camera_lookat(0, 0, 0);
-                       core::CMatrix4<f32> camera_projection_matrix;
+                       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
-                       camera_projection_matrix.buildProjectionMatrixOrthoLH(
+                       params.camera_projection_matrix.buildProjectionMatrixOrthoLH(
                                        1.65, 1.65, 0, 100);
 
-                       video::SColorf ambient_light(0.2,0.2,0.2);
-                       v3f light_position(10, 100, -50);
-                       video::SColorf light_color(0.5,0.5,0.5);
-                       f32 light_radius = 1000;
-
-                       video::ITexture *rtt = generateTextureFromMesh(
-                                       cube, device, dim, rtt_texture_name,
-                                       camera_position,
-                                       camera_lookat,
-                                       camera_projection_matrix,
-                                       ambient_light,
-                                       light_position,
-                                       light_color,
-                                       light_radius);
-                       
+                       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();
 
@@ -1321,22 +1349,21 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        driver->removeTexture(texture_top);
                        driver->removeTexture(texture_left);
                        driver->removeTexture(texture_right);
-                       
+
                        if(rtt == NULL)
                        {
-                               baseimg = generate_image_from_scratch(
-                                               imagename_top, device, sourcecache);
+                               baseimg = generateImageFromScratch(imagename_top);
                                return true;
                        }
 
                        // Create image of render target
-                       video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
+                       video::IImage *image = driver->createImage(rtt, v2s32(0,0), params.dim);
                        assert(image);
 
-                       //cleanup texture
+                       // Cleanup texture
                        driver->removeTexture(rtt);
 
-                       baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
+                       baseimg = driver->createImage(video::ECF_A8R8G8B8, params.dim);
 
                        if(image)
                        {
@@ -1358,7 +1385,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
 
                        if(baseimg == NULL)
                                baseimg = driver->createImage(video::ECF_A8R8G8B8, v2u32(16,16));
-                       video::IImage *img = sourcecache->getOrLoad(filename, device);
+                       video::IImage *img = m_sourcecache.getOrLoad(filename, m_device);
                        if(img)
                        {
                                core::dimension2d<u32> dim = img->getDimension();
@@ -1392,19 +1419,19 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        u32 frame_index = stoi(sf.next(":"));
 
                        if(baseimg == NULL){
-                               errorstream<<"generate_image(): baseimg!=NULL "
+                               errorstream<<"generateImage(): 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<<"generate_image(): Could not create image "
+                               errorstream<<"generateImage(): Could not create image "
                                                <<"for part_of_name=\""<<part_of_name
                                                <<"\", cancelling."<<std::endl;
                                return false;
@@ -1426,7 +1453,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                }
                else
                {
-                       errorstream<<"generate_image(): Invalid "
+                       errorstream<<"generateImage(): Invalid "
                                        " modification: \""<<part_of_name<<"\""<<std::endl;
                }
        }
@@ -1434,37 +1461,6 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
        return true;
 }
 
-void overlay(video::IImage *image, video::IImage *overlay)
-{
-       /*
-               Copy overlay to image, taking alpha into account.
-               Where image is transparent, don't copy from overlay.
-               Images sizes must be identical.
-       */
-       if(image == NULL || overlay == NULL)
-               return;
-       
-       core::dimension2d<u32> dim = image->getDimension();
-       core::dimension2d<u32> dim_overlay = overlay->getDimension();
-       assert(dim == dim_overlay);
-
-       for(u32 y=0; y<dim.Height; y++)
-       for(u32 x=0; x<dim.Width; x++)
-       {
-               video::SColor c1 = image->getPixel(x,y);
-               video::SColor c2 = overlay->getPixel(x,y);
-               u32 a1 = c1.getAlpha();
-               u32 a2 = c2.getAlpha();
-               if(a1 == 255 && a2 != 0)
-               {
-                       c1.setRed((c1.getRed()*(255-a2) + c2.getRed()*a2)/255);
-                       c1.setGreen((c1.getGreen()*(255-a2) + c2.getGreen()*a2)/255);
-                       c1.setBlue((c1.getBlue()*(255-a2) + c2.getBlue()*a2)/255);
-               }
-               image->setPixel(x,y,c1);
-       }
-}
-
 /*
        Draw an image on top of an another one, using the alpha channel of the
        source image
@@ -1489,11 +1485,105 @@ static void blit_with_alpha(video::IImage *src, video::IImage *dst,
        }
 }
 
+/*
+       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);
+               }
+       }
+}
+
+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++)
@@ -1570,7 +1660,7 @@ 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();