]> git.lizzy.rs Git - minetest.git/blobdiff - src/tile.cpp
inventorycube: use all three specified textures; also moved mesh creation / modificat...
[minetest.git] / src / tile.cpp
index c8fffffa7ad44fca8a8d16669efd0d0b50e9667d..89f3451973e078dc8b47fa10916110df8e891e3e 100644 (file)
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "filesys.h"
 #include "utility.h"
 #include "settings.h"
+#include "mesh.h"
 #include <ICameraSceneNode.h>
 #include "log.h"
 #include "mapnode.h" // For texture atlas making
@@ -132,7 +133,7 @@ std::string getTexturePath(const std::string &filename)
        */
        if(fullpath == "")
        {
-               std::string rel_path = std::string("textures")+DIR_DELIM+filename;
+               std::string rel_path = std::string("clienttextures")+DIR_DELIM+filename;
                std::string testpath = porting::path_data + DIR_DELIM + rel_path;
                // Check all filename extensions. Returns "" if not found.
                fullpath = getImagePath(testpath);
@@ -145,6 +146,108 @@ std::string getTexturePath(const std::string &filename)
        return fullpath;
 }
 
+/*
+       An internal variant of AtlasPointer with more data.
+       (well, more like a wrapper)
+*/
+
+struct SourceAtlasPointer
+{
+       std::string name;
+       AtlasPointer a;
+       video::IImage *atlas_img; // The source image of the atlas
+       // Integer variants of position and size
+       v2s32 intpos;
+       v2u32 intsize;
+
+       SourceAtlasPointer(
+                       const std::string &name_,
+                       AtlasPointer a_=AtlasPointer(0, NULL),
+                       video::IImage *atlas_img_=NULL,
+                       v2s32 intpos_=v2s32(0,0),
+                       v2u32 intsize_=v2u32(0,0)
+               ):
+               name(name_),
+               a(a_),
+               atlas_img(atlas_img_),
+               intpos(intpos_),
+               intsize(intsize_)
+       {
+       }
+};
+
+/*
+       SourceImageCache: A cache used for storing source images.
+*/
+
+class SourceImageCache
+{
+public:
+       void insert(const std::string &name, video::IImage *img,
+                       bool prefer_local, video::IVideoDriver *driver)
+       {
+               assert(img);
+               // Remove old image
+               core::map<std::string, video::IImage*>::Node *n;
+               n = m_images.find(name);
+               if(n){
+                       video::IImage *oldimg = n->getValue();
+                       if(oldimg)
+                               oldimg->drop();
+               }
+               // Try to use local texture instead if asked to
+               if(prefer_local){
+                       std::string path = getTexturePath(name.c_str());
+                       if(path != ""){
+                               video::IImage *img2 = driver->createImageFromFile(path.c_str());
+                               if(img2){
+                                       m_images[name] = img2;
+                                       return;
+                               }
+                       }
+               }
+               img->grab();
+               m_images[name] = img;
+       }
+       video::IImage* get(const std::string &name)
+       {
+               core::map<std::string, video::IImage*>::Node *n;
+               n = m_images.find(name);
+               if(n)
+                       return n->getValue();
+               return NULL;
+       }
+       // Primarily fetches from cache, secondarily tries to read from filesystem
+       video::IImage* getOrLoad(const std::string &name, IrrlichtDevice *device)
+       {
+               core::map<std::string, video::IImage*>::Node *n;
+               n = m_images.find(name);
+               if(n){
+                       n->getValue()->grab(); // Grab for caller
+                       return n->getValue();
+               }
+               video::IVideoDriver* driver = device->getVideoDriver();
+               std::string path = getTexturePath(name.c_str());
+               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());
+               // Even if could not be loaded, put as NULL
+               //m_images[name] = img;
+               if(img){
+                       m_images[name] = img;
+                       img->grab(); // Grab for caller
+               }
+               return img;
+       }
+private:
+       core::map<std::string, video::IImage*> m_images;
+};
+
 /*
        TextureSource
 */
@@ -209,9 +312,7 @@ class TextureSource : public IWritableTextureSource
        */
        u32 getTextureIdDirect(const std::string &name);
 
-       /*
-               Finds out the name of a cached texture.
-       */
+       // Finds out the name of a cached texture.
        std::string getTextureName(u32 id);
 
        /*
@@ -232,30 +333,29 @@ class TextureSource : public IWritableTextureSource
        // Gets a separate texture
        video::ITexture* getTextureRaw(const std::string &name)
        {
-               AtlasPointer ap = getTexture(name);
+               AtlasPointer ap = getTexture(name + "^[forcesingle");
                return ap.atlas;
        }
 
-       /*
-               Update new texture pointer and texture coordinates to an
-               AtlasPointer based on it's texture id
-       */
+       // Update new texture pointer and texture coordinates to an
+       // AtlasPointer based on it's texture id
        void updateAP(AtlasPointer &ap);
 
-       /*
-               Build the main texture atlas which contains most of the
-               textures.
-               
-               This is called by the constructor.
-       */
-       void buildMainAtlas(class IGameDef *gamedef);
+       // Processes queued texture requests from other threads.
+       // Shall be called from the main thread.
+       void processQueue();
        
-       /*
-               Processes queued texture requests from other threads.
+       // 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();
 
-               Shall be called from the main thread.
-       */
-       void processQueue();
+       // Build the main texture atlas which contains most of the
+       // textures.
+       void buildMainAtlas(class IGameDef *gamedef);
        
 private:
        
@@ -264,6 +364,10 @@ class TextureSource : public IWritableTextureSource
        // The irrlicht device
        IrrlichtDevice *m_device;
        
+       // Cache of source images
+       // This should be only accessed from the main thread
+       SourceImageCache m_sourcecache;
+
        // A texture id is index in this array.
        // The first position contains a NULL texture.
        core::array<SourceAtlasPointer> m_atlaspointer_cache;
@@ -305,31 +409,6 @@ TextureSource::~TextureSource()
 {
 }
 
-void TextureSource::processQueue()
-{
-       /*
-               Fetch textures
-       */
-       if(m_get_texture_queue.size() > 0)
-       {
-               GetRequest<std::string, u32, u8, u8>
-                               request = m_get_texture_queue.pop();
-
-               infostream<<"TextureSource::processQueue(): "
-                               <<"got texture request with "
-                               <<"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);
-       }
-}
-
 u32 TextureSource::getTextureId(const std::string &name)
 {
        //infostream<<"getTextureId(): \""<<name<<"\""<<std::endl;
@@ -392,13 +471,15 @@ u32 TextureSource::getTextureId(const std::string &name)
 
 // Draw a progress bar on the image
 void make_progressbar(float value, video::IImage *image);
+// Brighten image
+void brighten(video::IImage *image);
 
 /*
        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);
+               IrrlichtDevice *device, SourceImageCache *sourcecache);
 
 /*
        Generates an image from a full string like
@@ -407,7 +488,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
        This is used by buildMainAtlas().
 */
 video::IImage* generate_image_from_scratch(std::string name,
-               IrrlichtDevice *device);
+               IrrlichtDevice *device, SourceImageCache *sourcecache);
 
 /*
        This method generates all the textures
@@ -443,14 +524,14 @@ u32 TextureSource::getTextureIdDirect(const std::string &name)
                n = m_name_to_id.find(name);
                if(n != NULL)
                {
-                       infostream<<"getTextureIdDirect(): \""<<name
-                                       <<"\" found in cache"<<std::endl;
+                       /*infostream<<"getTextureIdDirect(): \""<<name
+                                       <<"\" found in cache"<<std::endl;*/
                        return n->getValue();
                }
        }
 
-       infostream<<"getTextureIdDirect(): \""<<name
-                       <<"\" NOT found in cache. Creating it."<<std::endl;
+       /*infostream<<"getTextureIdDirect(): \""<<name
+                       <<"\" NOT found in cache. Creating it."<<std::endl;*/
        
        /*
                Get the base image
@@ -513,7 +594,7 @@ u32 TextureSource::getTextureIdDirect(const std::string &name)
                
                if(image == NULL)
                {
-                       infostream<<"getTextureIdDirect(): NULL image in "
+                       infostream<<"getTextureIdDirect(): WARNING: NULL image in "
                                        <<"cache: \""<<base_image_name<<"\""
                                        <<std::endl;
                }
@@ -547,9 +628,9 @@ 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) == false)
+       if(!generate_image(last_part_of_name, baseimg, m_device, &m_sourcecache))
        {
-               infostream<<"getTextureIdDirect(): "
+               errorstream<<"getTextureIdDirect(): "
                                "failed to generate \""<<last_part_of_name<<"\""
                                <<std::endl;
        }
@@ -557,7 +638,7 @@ u32 TextureSource::getTextureIdDirect(const std::string &name)
        // If no resulting image, print a warning
        if(baseimg == NULL)
        {
-               infostream<<"getTextureIdDirect(): baseimg is NULL (attempted to"
+               errorstream<<"getTextureIdDirect(): baseimg is NULL (attempted to"
                                " create texture \""<<name<<"\""<<std::endl;
        }
        
@@ -598,7 +679,7 @@ std::string TextureSource::getTextureName(u32 id)
 
        if(id >= m_atlaspointer_cache.size())
        {
-               infostream<<"TextureSource::getTextureName(): id="<<id
+               errorstream<<"TextureSource::getTextureName(): id="<<id
                                <<" >= m_atlaspointer_cache.size()="
                                <<m_atlaspointer_cache.size()<<std::endl;
                return "";
@@ -624,6 +705,79 @@ void TextureSource::updateAP(AtlasPointer &ap)
        ap = ap2;
 }
 
+void TextureSource::processQueue()
+{
+       /*
+               Fetch textures
+       */
+       if(m_get_texture_queue.size() > 0)
+       {
+               GetRequest<std::string, u32, u8, u8>
+                               request = m_get_texture_queue.pop();
+
+               /*infostream<<"TextureSource::processQueue(): "
+                               <<"got texture request with "
+                               <<"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);
+       }
+}
+
+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());
+}
+       
+void TextureSource::rebuildImagesAndTextures()
+{
+       JMutexAutoLock lock(m_atlaspointer_cache_mutex);
+
+       /*// Oh well... just clear everything, they'll load sometime.
+       m_atlaspointer_cache.clear();
+       m_name_to_id.clear();*/
+
+       video::IVideoDriver* driver = m_device->getVideoDriver();
+       
+       // Remove source images from textures to disable inheriting textures
+       // from existing textures
+       /*for(u32 i=0; i<m_atlaspointer_cache.size(); i++){
+               SourceAtlasPointer *sap = &m_atlaspointer_cache[i];
+               sap->atlas_img->drop();
+               sap->atlas_img = NULL;
+       }*/
+       
+       // Recreate textures
+       for(u32 i=0; i<m_atlaspointer_cache.size(); i++){
+               SourceAtlasPointer *sap = &m_atlaspointer_cache[i];
+               video::IImage *img =
+                       generate_image_from_scratch(sap->name, m_device, &m_sourcecache);
+               // Create texture from resulting image
+               video::ITexture *t = NULL;
+               if(img)
+                       t = driver->addTexture(sap->name.c_str(), img);
+               
+               // Replace texture
+               sap->a.atlas = t;
+               sap->a.pos = v2f(0,0);
+               sap->a.size = v2f(1,1);
+               sap->a.tiled = 0;
+               sap->atlas_img = img;
+               sap->intpos = v2s32(0,0);
+               sap->intsize = img->getDimension();
+       }
+}
+
 void TextureSource::buildMainAtlas(class IGameDef *gamedef) 
 {
        assert(gamedef->tsrc() == this);
@@ -708,23 +862,13 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
        {
                std::string name = i.getNode()->getKey();
 
-               /*video::IImage *img = driver->createImageFromFile(
-                               getTexturePath(name.c_str()).c_str());
-               if(img == NULL)
-                       continue;
-               
-               core::dimension2d<u32> dim = img->getDimension();
-               // Make a copy with the right color format
-               video::IImage *img2 =
-                               driver->createImage(video::ECF_A8R8G8B8, dim);
-               img->copyTo(img2);
-               img->drop();*/
-               
                // Generate image by name
-               video::IImage *img2 = generate_image_from_scratch(name, m_device);
+               video::IImage *img2 = generate_image_from_scratch(name, m_device,
+                               &m_sourcecache);
                if(img2 == NULL)
                {
-                       infostream<<"TextureSource::buildMainAtlas(): Couldn't generate texture atlas: Couldn't generate image \""<<name<<"\""<<std::endl;
+                       errorstream<<"TextureSource::buildMainAtlas(): "
+                                       <<"Couldn't generate image \""<<name<<"\""<<std::endl;
                        continue;
                }
 
@@ -753,8 +897,8 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
                        pos_in_atlas.X += column_width + column_padding;
                }
                
-        infostream<<"TextureSource::buildMainAtlas(): Adding \""<<name
-                <<"\" to texture atlas"<<std::endl;
+               /*infostream<<"TextureSource::buildMainAtlas(): Adding \""<<name
+                               <<"\" to texture atlas"<<std::endl;*/
 
                // Tile it a few times in the X direction
                u16 xwise_tiling = column_width / dim.Width;
@@ -763,10 +907,14 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
                for(u32 j=0; j<xwise_tiling; j++)
                {
                        // Copy the copy to the atlas
-                       img2->copyToWithAlpha(atlas_img,
+                       /*img2->copyToWithAlpha(atlas_img,
                                        pos_in_atlas + v2s32(j*dim.Width,0),
                                        core::rect<s32>(v2s32(0,0), dim),
                                        video::SColor(255,255,255,255),
+                                       NULL);*/
+                       img2->copyTo(atlas_img,
+                                       pos_in_atlas + v2s32(j*dim.Width,0),
+                                       core::rect<s32>(v2s32(0,0), dim),
                                        NULL);
                }
 
@@ -807,9 +955,9 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
                if(n){
                        id = n->getValue();
                        reuse_old_id = true;
+                       /*infostream<<"TextureSource::buildMainAtlas(): "
+                                       <<"Replacing old AtlasPointer"<<std::endl;*/
                }
-               infostream<<"TextureSource::buildMainAtlas(): "
-                               <<"Replacing old AtlasPointer"<<std::endl;
 
                // Create AtlasPointer
                AtlasPointer ap(id);
@@ -865,7 +1013,7 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef)
 }
 
 video::IImage* generate_image_from_scratch(std::string name,
-               IrrlichtDevice *device)
+               IrrlichtDevice *device, SourceImageCache *sourcecache)
 {
        /*infostream<<"generate_image_from_scratch(): "
                        "\""<<name<<"\""<<std::endl;*/
@@ -908,7 +1056,8 @@ video::IImage* generate_image_from_scratch(std::string name,
                /*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);
+               baseimg = generate_image_from_scratch(base_image_name, device,
+                               sourcecache);
        }
        
        /*
@@ -920,9 +1069,9 @@ video::IImage* generate_image_from_scratch(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, device) == false)
+       if(!generate_image(last_part_of_name, baseimg, device, sourcecache))
        {
-               infostream<<"generate_image_from_scratch(): "
+               errorstream<<"generate_image_from_scratch(): "
                                "failed to generate \""<<last_part_of_name<<"\""
                                <<std::endl;
                return NULL;
@@ -932,31 +1081,24 @@ video::IImage* generate_image_from_scratch(std::string name,
 }
 
 bool generate_image(std::string part_of_name, video::IImage *& baseimg,
-               IrrlichtDevice *device)
+               IrrlichtDevice *device, SourceImageCache *sourcecache)
 {
        video::IVideoDriver* driver = device->getVideoDriver();
        assert(driver);
 
        // Stuff starting with [ are special commands
-       if(part_of_name[0] != '[')
+       if(part_of_name.size() == 0 || part_of_name[0] != '[')
        {
-               // A normal texture; load it from a file
-               std::string path = getTexturePath(part_of_name.c_str());
-               /*infostream<<"generate_image(): Loading path \""<<path
-                               <<"\""<<std::endl;*/
-               
-               video::IImage *image = driver->createImageFromFile(path.c_str());
+               video::IImage *image = sourcecache->getOrLoad(part_of_name, device);
 
                if(image == NULL)
                {
-                       infostream<<"generate_image(): Could not load image \""
-                    <<part_of_name<<"\" from path \""<<path<<"\""
-                                       <<" while building texture"<<std::endl;
-
-                       //return false;
-
-                       infostream<<"generate_image(): Creating a dummy"
-                    <<" image for \""<<part_of_name<<"\""<<std::endl;
+                       if(part_of_name != ""){
+                               errorstream<<"generate_image(): Could not load image \""
+                                               <<part_of_name<<"\""<<" while building texture"<<std::endl;
+                               errorstream<<"generate_image(): Creating a dummy"
+                                               <<" image for \""<<part_of_name<<"\""<<std::endl;
+                       }
 
                        // Just create a dummy image
                        //core::dimension2d<u32> dim(2,2);
@@ -1015,9 +1157,9 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
        {
                // A special texture modification
 
-               infostream<<"generate_image(): generating special "
+               /*infostream<<"generate_image(): generating special "
                                <<"modification \""<<part_of_name<<"\""
-                               <<std::endl;
+                               <<std::endl;*/
                
                /*
                        This is the simplest of all; it just adds stuff to the
@@ -1029,6 +1171,15 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                */
                if(part_of_name == "[forcesingle")
                {
+                       // If base image is NULL, create a random color
+                       if(baseimg == NULL)
+                       {
+                               core::dimension2d<u32> dim(1,1);
+                               baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
+                               assert(baseimg);
+                               baseimg->setPixel(0,0, video::SColor(255,myrand()%256,
+                                               myrand()%256,myrand()%256));
+                       }
                }
                /*
                        [crackN
@@ -1038,7 +1189,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                {
                        if(baseimg == NULL)
                        {
-                               infostream<<"generate_image(): baseimg==NULL "
+                               errorstream<<"generate_image(): baseimg==NULL "
                                                <<"for part_of_name=\""<<part_of_name
                                                <<"\", cancelling."<<std::endl;
                                return false;
@@ -1056,8 +1207,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                                It is an image with a number of cracking stages
                                horizontally tiled.
                        */
-                       video::IImage *img_crack = driver->createImageFromFile(
-                                       getTexturePath("crack.png").c_str());
+                       video::IImage *img_crack = sourcecache->getOrLoad("crack.png", device);
                
                        if(img_crack)
                        {
@@ -1143,8 +1293,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                                infostream<<"Adding \""<<filename
                                                <<"\" to combined ("<<x<<","<<y<<")"
                                                <<std::endl;
-                               video::IImage *img = driver->createImageFromFile(
-                                               getTexturePath(filename.c_str()).c_str());
+                               video::IImage *img = sourcecache->getOrLoad(filename, device);
                                if(img)
                                {
                                        core::dimension2d<u32> dim = img->getDimension();
@@ -1175,7 +1324,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                {
                        if(baseimg == NULL)
                        {
-                               infostream<<"generate_image(): baseimg==NULL "
+                               errorstream<<"generate_image(): baseimg==NULL "
                                                <<"for part_of_name=\""<<part_of_name
                                                <<"\", cancelling."<<std::endl;
                                return false;
@@ -1185,169 +1334,87 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        make_progressbar(value, baseimg);
                }
                /*
-                       "[noalpha:filename.png"
-                       Use an image without it's alpha channel.
-                       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.
+                       "[brighten"
                */
-               else if(part_of_name.substr(0,8) == "[noalpha")
+               else if(part_of_name.substr(0,9) == "[brighten")
                {
-                       if(baseimg != NULL)
+                       if(baseimg == NULL)
                        {
-                               infostream<<"generate_image(): baseimg!=NULL "
+                               errorstream<<"generate_image(): baseimg==NULL "
                                                <<"for part_of_name=\""<<part_of_name
                                                <<"\", cancelling."<<std::endl;
                                return false;
                        }
 
-                       std::string filename = part_of_name.substr(9);
-
-                       std::string path = getTexturePath(filename.c_str());
-
-                       infostream<<"generate_image(): Loading path \""<<path
-                                       <<"\""<<std::endl;
-                       
-                       video::IImage *image = driver->createImageFromFile(path.c_str());
-                       
-                       if(image == NULL)
-                       {
-                               infostream<<"generate_image(): Loading path \""
-                                               <<path<<"\" failed"<<std::endl;
-                       }
-                       else
-                       {
-                               core::dimension2d<u32> dim = image->getDimension();
-                               baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
-                               
-                               // Set alpha to full
-                               for(u32 y=0; y<dim.Height; y++)
-                               for(u32 x=0; x<dim.Width; x++)
-                               {
-                                       video::SColor c = image->getPixel(x,y);
-                                       c.setAlpha(255);
-                                       image->setPixel(x,y,c);
-                               }
-                               // Blit
-                               image->copyTo(baseimg);
-
-                               image->drop();
-                       }
+                       brighten(baseimg);
                }
                /*
-                       "[makealpha:R,G,B:filename.png"
-                       Use an image with converting one color to transparent.
+                       "[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,11) == "[makealpha:")
+               else if(part_of_name.substr(0,8) == "[noalpha")
                {
-                       if(baseimg != NULL)
+                       if(baseimg == NULL)
                        {
-                               infostream<<"generate_image(): baseimg!=NULL "
+                               errorstream<<"generate_image(): 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("");
-
-                       std::string path = getTexturePath(filename.c_str());
-
-                       infostream<<"generate_image(): Loading path \""<<path
-                                       <<"\""<<std::endl;
+                       core::dimension2d<u32> dim = baseimg->getDimension();
                        
-                       video::IImage *image = driver->createImageFromFile(path.c_str());
-                       
-                       if(image == NULL)
-                       {
-                               infostream<<"generate_image(): Loading path \""
-                                               <<path<<"\" failed"<<std::endl;
-                       }
-                       else
+                       // Set alpha to full
+                       for(u32 y=0; y<dim.Height; y++)
+                       for(u32 x=0; x<dim.Width; x++)
                        {
-                               core::dimension2d<u32> dim = image->getDimension();
-                               baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
-                               
-                               // Blit
-                               image->copyTo(baseimg);
-
-                               image->drop();
-
-                               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);
-                               }
+                               video::SColor c = baseimg->getPixel(x,y);
+                               c.setAlpha(255);
+                               baseimg->setPixel(x,y,c);
                        }
                }
                /*
-                       "[makealpha2:R,G,B;R2,G2,B2:filename.png"
-                       Use an image with converting two colors to transparent.
+                       "[makealpha:R,G,B"
+                       Convert one color to transparent.
                */
-               else if(part_of_name.substr(0,12) == "[makealpha2:")
+               else if(part_of_name.substr(0,11) == "[makealpha:")
                {
-                       if(baseimg != NULL)
+                       if(baseimg == NULL)
                        {
-                               infostream<<"generate_image(): baseimg!=NULL "
+                               errorstream<<"generate_image(): baseimg==NULL "
                                                <<"for part_of_name=\""<<part_of_name
                                                <<"\", cancelling."<<std::endl;
                                return false;
                        }
 
-                       Strfnd sf(part_of_name.substr(12));
+                       Strfnd sf(part_of_name.substr(11));
                        u32 r1 = stoi(sf.next(","));
                        u32 g1 = stoi(sf.next(","));
-                       u32 b1 = stoi(sf.next(";"));
-                       u32 r2 = stoi(sf.next(","));
-                       u32 g2 = stoi(sf.next(","));
-                       u32 b2 = stoi(sf.next(":"));
+                       u32 b1 = stoi(sf.next(""));
                        std::string filename = sf.next("");
 
-                       std::string path = getTexturePath(filename.c_str());
-
-                       infostream<<"generate_image(): Loading path \""<<path
-                                       <<"\""<<std::endl;
-                       
-                       video::IImage *image = driver->createImageFromFile(path.c_str());
+                       core::dimension2d<u32> dim = baseimg->getDimension();
                        
-                       if(image == NULL)
-                       {
-                               infostream<<"generate_image(): Loading path \""
-                                               <<path<<"\" failed"<<std::endl;
-                       }
-                       else
-                       {
-                               core::dimension2d<u32> dim = image->getDimension();
-                               baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
-
-                               // Blit
-                               image->copyTo(baseimg);
+                       /*video::IImage *oldbaseimg = baseimg;
+                       baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
+                       oldbaseimg->copyTo(baseimg);
+                       oldbaseimg->drop();*/
 
-                               image->drop();
-                               
-                               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) &&
-                                          !(r == r2 && g == g2 && b == b2))
-                                               continue;
-                                       c.setAlpha(0);
-                                       baseimg->setPixel(x,y,c);
-                               }
+                       // 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);
                        }
                }
                /*
@@ -1362,7 +1429,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                {
                        if(baseimg != NULL)
                        {
-                               infostream<<"generate_image(): baseimg!=NULL "
+                               errorstream<<"generate_image(): baseimg!=NULL "
                                                <<"for part_of_name=\""<<part_of_name
                                                <<"\", cancelling."<<std::endl;
                                return false;
@@ -1376,14 +1443,14 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        std::string imagename_right = sf.next("{");
 
 #if 1
-                       //TODO
+                       // TODO: Create cube with different textures on different sides
 
                        if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false)
                        {
-                               infostream<<"generate_image(): EVDF_RENDER_TO_TARGET"
+                               errorstream<<"generate_image(): EVDF_RENDER_TO_TARGET"
                                                " not supported. Creating fallback image"<<std::endl;
                                baseimg = generate_image_from_scratch(
-                                               imagename_top, device);
+                                               imagename_top, device, sourcecache);
                                return true;
                        }
                        
@@ -1394,18 +1461,22 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        
                        // Generate images for the faces of the cube
                        video::IImage *img_top = generate_image_from_scratch(
-                                       imagename_top, device);
+                                       imagename_top, device, sourcecache);
                        video::IImage *img_left = generate_image_from_scratch(
-                                       imagename_left, device);
+                                       imagename_left, device, sourcecache);
                        video::IImage *img_right = generate_image_from_scratch(
-                                       imagename_right, device);
+                                       imagename_right, device, sourcecache);
                        assert(img_top && img_left && img_right);
 
-                       // TODO: Create textures from images
+                       // Create textures from images
                        video::ITexture *texture_top = driver->addTexture(
                                        (imagename_top + "__temp__").c_str(), img_top);
-                       assert(texture_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();
@@ -1432,17 +1503,24 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                                Create scene:
                                - An unit cube is centered at 0,0,0
                                - Camera looks at cube from Y+, Z- towards Y-, Z+
-                               NOTE: Cube has to be changed to something else because
-                               the textures cannot be set individually (or can they?)
                        */
 
-                       scene::ISceneNode* cube = smgr->addCubeSceneNode(1.0, NULL, -1,
-                                       v3f(0,0,0), v3f(0, 45, 0));
+                       scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1));
+                       setMeshColor(cube, video::SColor(255, 255, 255, 255));
+
+                       scene::IMeshSceneNode* cubenode = smgr->addMeshSceneNode(cube, NULL, -1, v3f(0,0,0), v3f(0,45,0), v3f(1,1,1), true);
+                       cube->drop();
+
                        // Set texture of cube
-                       cube->setMaterialTexture(0, texture_top);
-                       //cube->setMaterialFlag(video::EMF_LIGHTING, false);
-                       cube->setMaterialFlag(video::EMF_ANTI_ALIASING, false);
-                       cube->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
+                       cubenode->getMaterial(0).setTexture(0, texture_top);
+                       cubenode->getMaterial(1).setTexture(0, texture_top);
+                       cubenode->getMaterial(2).setTexture(0, texture_right);
+                       cubenode->getMaterial(3).setTexture(0, texture_right);
+                       cubenode->getMaterial(4).setTexture(0, texture_left);
+                       cubenode->getMaterial(5).setTexture(0, texture_left);
+                       cubenode->setMaterialFlag(video::EMF_LIGHTING, true);
+                       cubenode->setMaterialFlag(video::EMF_ANTI_ALIASING, true);
+                       cubenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, true);
 
                        scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0,
                                        v3f(0, 1.0, -1.5), v3f(0, 0, 0));
@@ -1452,7 +1530,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        camera->setProjectionMatrix(pm, true);
 
                        /*scene::ILightSceneNode *light =*/ smgr->addLightSceneNode(0,
-                                       v3f(-50, 100, 0), video::SColorf(0.5,0.5,0.5), 1000);
+                                       v3f(-50, 100, -75), video::SColorf(0.5,0.5,0.5), 1000);
 
                        smgr->setAmbientLight(video::SColorf(0.2,0.2,0.2));
 
@@ -1472,8 +1550,10 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        // Unset render target
                        driver->setRenderTarget(0, true, true, 0);
 
-                       //TODO: Free textures of images
+                       // Free textures of images
                        driver->removeTexture(texture_top);
+                       driver->removeTexture(texture_left);
+                       driver->removeTexture(texture_right);
                        
                        // Create image of render target
                        video::IImage *image = driver->createImage(rtt, v2s32(0,0), dim);
@@ -1491,7 +1571,7 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                }
                else
                {
-                       infostream<<"generate_image(): Invalid "
+                       errorstream<<"generate_image(): Invalid "
                                        " modification: \""<<part_of_name<<"\""<<std::endl;
                }
        }
@@ -1531,3 +1611,21 @@ void make_progressbar(float value, video::IImage *image)
        }
 }
 
+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);
+       }
+}
+