Improve glass
[oweals/minetest.git] / src / tile.cpp
index eb3616f026531eea675ce5d5ffeb7a378777e19c..f57abfbb8e9cc4a646351c105175e14d3f1edeba 100644 (file)
@@ -145,6 +145,98 @@ 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 do_overwrite)
+       {
+               assert(img);
+               core::map<std::string, video::IImage*>::Node *n;
+               n = m_images.find(name);
+               if(n){
+                       if(!do_overwrite)
+                               return;
+                       video::IImage *oldimg = n->getValue();
+                       if(oldimg)
+                               oldimg->drop();
+               }
+               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 +301,7 @@ public:
        */
        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);
 
        /*
@@ -236,26 +326,25 @@ public:
                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 +353,10 @@ private:
        // 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 +398,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;
@@ -398,7 +466,7 @@ void make_progressbar(float value, video::IImage *image);
        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 +475,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
@@ -547,7 +615,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) == false)
+       if(!generate_image(last_part_of_name, baseimg, m_device, &m_sourcecache))
        {
                infostream<<"getTextureIdDirect(): "
                                "failed to generate \""<<last_part_of_name<<"\""
@@ -624,6 +692,116 @@ 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, false);
+
+#if 0
+       JMutexAutoLock lock(m_atlaspointer_cache_mutex);
+
+       video::IVideoDriver* driver = m_device->getVideoDriver();
+       assert(driver);
+
+       // Create texture
+       video::ITexture *t = driver->addTexture(name.c_str(), img);
+
+       bool reuse_old_id = false;
+       u32 id = m_atlaspointer_cache.size();
+       // Check old id without fetching a texture
+       core::map<std::string, u32>::Node *n;
+       n = m_name_to_id.find(name);
+       // If it exists, we will replace the old definition
+       if(n){
+               id = n->getValue();
+               reuse_old_id = true;
+       }
+       
+       // Create AtlasPointer
+       AtlasPointer ap(id);
+       ap.atlas = t;
+       ap.pos = v2f(0,0);
+       ap.size = v2f(1,1);
+       ap.tiled = 0;
+       core::dimension2d<u32> dim = img->getDimension();
+
+       // Create SourceAtlasPointer and add to containers
+       SourceAtlasPointer nap(name, ap, img, v2s32(0,0), dim);
+       if(reuse_old_id)
+               m_atlaspointer_cache[id] = nap;
+       else
+               m_atlaspointer_cache.push_back(nap);
+       m_name_to_id[name] = id;
+#endif
+}
+       
+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,20 +886,9 @@ 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;
@@ -807,9 +974,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 +1032,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 +1075,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,7 +1088,7 @@ 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(): "
                                "failed to generate \""<<last_part_of_name<<"\""
@@ -932,7 +1100,7 @@ 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);
@@ -940,18 +1108,12 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
        // Stuff starting with [ are special commands
        if(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;
+                    <<part_of_name<<"\""<<" while building texture"<<std::endl;
 
                        //return false;
 
@@ -1056,8 +1218,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 +1304,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();
@@ -1205,10 +1365,10 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
 
                        std::string path = getTexturePath(filename.c_str());
 
-                       infostream<<"generate_image(): Loading path \""<<path
+                       infostream<<"generate_image(): Loading file \""<<filename
                                        <<"\""<<std::endl;
                        
-                       video::IImage *image = driver->createImageFromFile(path.c_str());
+                       video::IImage *image = sourcecache->getOrLoad(filename, device);
                        
                        if(image == NULL)
                        {
@@ -1254,17 +1414,15 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        u32 b1 = stoi(sf.next(":"));
                        std::string filename = sf.next("");
 
-                       std::string path = getTexturePath(filename.c_str());
-
-                       infostream<<"generate_image(): Loading path \""<<path
+                       infostream<<"generate_image(): Loading file \""<<filename
                                        <<"\""<<std::endl;
                        
-                       video::IImage *image = driver->createImageFromFile(path.c_str());
+                       video::IImage *image = sourcecache->getOrLoad(filename, device);
                        
                        if(image == NULL)
                        {
-                               infostream<<"generate_image(): Loading path \""
-                                               <<path<<"\" failed"<<std::endl;
+                               infostream<<"generate_image(): Loading file \""
+                                               <<filename<<"\" failed"<<std::endl;
                        }
                        else
                        {
@@ -1313,17 +1471,15 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        u32 b2 = stoi(sf.next(":"));
                        std::string filename = sf.next("");
 
-                       std::string path = getTexturePath(filename.c_str());
-
-                       infostream<<"generate_image(): Loading path \""<<path
+                       infostream<<"generate_image(): Loading filename \""<<filename
                                        <<"\""<<std::endl;
                        
-                       video::IImage *image = driver->createImageFromFile(path.c_str());
+                       video::IImage *image = sourcecache->getOrLoad(filename, device);
                        
                        if(image == NULL)
                        {
-                               infostream<<"generate_image(): Loading path \""
-                                               <<path<<"\" failed"<<std::endl;
+                               infostream<<"generate_image(): Loading file \""
+                                               <<filename<<"\" failed"<<std::endl;
                        }
                        else
                        {
@@ -1376,14 +1532,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"
                                                " not supported. Creating fallback image"<<std::endl;
                                baseimg = generate_image_from_scratch(
-                                               imagename_top, device);
+                                               imagename_top, device, sourcecache);
                                return true;
                        }
                        
@@ -1394,14 +1550,15 @@ 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
+                       // TODO: Use them all
                        video::ITexture *texture_top = driver->addTexture(
                                        (imagename_top + "__temp__").c_str(), img_top);
                        assert(texture_top);
@@ -1472,7 +1629,8 @@ 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
+                       // TODO: When all are used, free them all
                        driver->removeTexture(texture_top);
                        
                        // Create image of render target