work-in-progress texture atlas optimization
authorPerttu Ahola <celeron55@gmail.com>
Thu, 10 Feb 2011 00:13:03 +0000 (02:13 +0200)
committerPerttu Ahola <celeron55@gmail.com>
Thu, 10 Feb 2011 00:13:03 +0000 (02:13 +0200)
21 files changed:
data/water.png
src/CMakeLists.txt
src/environment.cpp
src/iirrlichtwrapper.h
src/inventory.cpp
src/inventory.h
src/irrlichtwrapper.cpp
src/irrlichtwrapper.h
src/main.cpp
src/main.h
src/map.cpp
src/map.h
src/mapblock.cpp
src/mapnode.cpp
src/mapnode.h
src/mineral.cpp
src/mineral.h
src/server.cpp
src/texture.h
src/tile.cpp
src/tile.h

index a5347f61da8c44cb67af6a7d115de442cde9cff8..a304059e3f37b31c3fec8e8d6a57186842aba751 100644 (file)
Binary files a/data/water.png and b/data/water.png differ
index de8e93e04f1bcd79e69809f7ef22a26569b82284..071beeadc626016cdd0a8610af1e8f7152d9526f 100644 (file)
@@ -75,6 +75,7 @@ set(minetest_SRCS
        guiPauseMenu.cpp
        irrlichtwrapper.cpp
        client.cpp
+       tile.cpp
        main.cpp
 )
 
index e8a01a4a15397944cb81658b5bfe4dfaa3f3bbd2..ecf853b7c6a72703e3cfe236fb897e903e2b9616 100644 (file)
@@ -118,9 +118,9 @@ void Environment::step(float dtime)
                                /*
                                        Apply water resistance
                                */
-                               if(player->in_water_stable)
+                               if(player->in_water_stable || player->in_water)
                                {
-                                       f32 max_down = 1.5*BS;
+                                       f32 max_down = 2.0*BS;
                                        if(speed.Y < -max_down) speed.Y = -max_down;
 
                                        f32 max = 2.5*BS;
index 47ae21ee9fffe3ca929cb73cc94eda05aa3e3db7..66f8a55c65ab5135d828ea79753a26ea377a52cc 100644 (file)
@@ -21,6 +21,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define IIRRLICHTWRAPPER_HEADER
 
 #include "common_irrlicht.h"
+#include "texture.h"
+
+/*
+       NOTE: This is deprecated and should be removed completely
+*/
 
 /*
        IrrlichtWrapper prototype.
@@ -38,15 +43,18 @@ public:
        {
        }
        
+       // Should be called only from the main thread
+       virtual IrrlichtDevice* getDevice(){ return NULL; }
+       
        virtual u32 getTime()
        {
                return 0;
        }
        
-       virtual textureid_t getTextureId(const std::string &name){ return 0; }
+       /*virtual textureid_t getTextureId(const std::string &name){ return 0; }
        virtual std::string getTextureName(textureid_t id){ return ""; }
        virtual video::ITexture* getTexture(const std::string &name){ return NULL; }
-       virtual video::ITexture* getTexture(const TextureSpec &spec){ return NULL; }
+       virtual video::ITexture* getTexture(const TextureSpec &spec){ return NULL; }*/
        
 private:
 };
index 5030d67d1d54fb96b6e5a079552ff1d57aff5c0a..c29bb947082dddc354fc4f2e51d7cc14d622b396 100644 (file)
@@ -96,13 +96,17 @@ InventoryItem* InventoryItem::deSerialize(std::istream &is)
 #ifndef SERVER
 video::ITexture * MapBlockObjectItem::getImage()
 {
+       //TODO
+       
        if(m_inventorystring.substr(0,3) == "Rat")
                //return g_device->getVideoDriver()->getTexture(porting::getDataPath("rat.png").c_str());
-               return g_irrlicht->getTexture("rat.png");
+               //return g_irrlicht->getTexture("rat.png");
+               return NULL;
        
        if(m_inventorystring.substr(0,4) == "Sign")
                //return g_device->getVideoDriver()->getTexture(porting::getDataPath("sign.png").c_str());
-               return g_irrlicht->getTexture("sign.png");
+               //return g_irrlicht->getTexture("sign.png");
+               return NULL;
 
        return NULL;
 }
index e7c7adaee3b3d194d2b6572d52a7f7f1606f69e9..ca07bf46dcbbd71ae6b13ba9c721f16f1686d705 100644 (file)
@@ -122,12 +122,9 @@ public:
 #ifndef SERVER
        video::ITexture * getImage()
        {
-               /*if(m_content >= USEFUL_CONTENT_COUNT)
-                       return NULL;
-                       
-               return g_irrlicht->getTexture(g_content_inventory_texture_paths[m_content]);*/
-
-               return g_irrlicht->getTexture(content_features(m_content).inventory_texture);
+               //TODO
+               //return g_irrlicht->getTexture(content_features(m_content).inventory_texture);
+               return NULL;
        }
 #endif
        std::string getText()
@@ -264,7 +261,9 @@ public:
                        name = "cloud.png";
                
                // Get such a texture
-               return g_irrlicht->getTexture(name);
+               //return g_irrlicht->getTexture(name);
+               //TODO
+               return NULL;
        }
 #endif
        std::string getText()
@@ -351,10 +350,12 @@ public:
                std::ostringstream os;
                os<<"[progressbar"<<value_f;
 
-               TextureSpec spec;
+               /*TextureSpec spec;
                spec.addTid(g_irrlicht->getTextureId(basename));
                spec.addTid(g_irrlicht->getTextureId(os.str()));
-               return g_irrlicht->getTexture(spec);
+               return g_irrlicht->getTexture(spec);*/
+               //TODO
+               return NULL;
 
                /*// Make texture name for the new texture with a progress bar
                float value_f = (float)toolprogress / (float)maxprogress;
index 4ad647194e0f3413f7252d5b171d6a702a1d2d57..4a1f2c413226091cf57b9c8bf8e525733cb3bd81 100644 (file)
@@ -11,8 +11,22 @@ IrrlichtWrapper::IrrlichtWrapper(IrrlichtDevice *device)
        m_device = device;
 }
 
+IrrlichtWrapper::~IrrlichtWrapper()
+{
+#if 0
+       // Clear image cache
+       for(core::map<std::string, video::IImage*>::Iterator
+                       i = m_imagecache.getIterator();
+                       i.atEnd() == false; i++)
+       {
+               i.getNode()->getValue()->drop();
+       }
+#endif
+}
+
 void IrrlichtWrapper::Run()
 {
+#if 0
        /*
                Fetch textures
        */
@@ -34,6 +48,7 @@ void IrrlichtWrapper::Run()
 
                request.dest->push_back(result);
        }
+#endif
 }
 
 void IrrlichtWrapper::Shutdown(bool shutdown)
@@ -41,6 +56,18 @@ void IrrlichtWrapper::Shutdown(bool shutdown)
        m_running = !shutdown;
 }
 
+IrrlichtDevice* IrrlichtWrapper::getDevice()
+{
+       if(get_current_thread_id() != m_main_thread)
+       {
+               dstream<<"WARNING: IrrlichtWrapper::getDevice() called "
+                               "not from main thread"<<std::endl;
+               return NULL;
+       }
+       return m_device;
+}
+
+#if 0
 textureid_t IrrlichtWrapper::getTextureId(const std::string &name)
 {
        u32 id = m_namecache.getId(name);
@@ -55,9 +82,9 @@ std::string IrrlichtWrapper::getTextureName(textureid_t id)
        return name;
 }
 
-video::ITexture* IrrlichtWrapper::getTexture(const std::string &name)
+video::ITexture* IrrlichtWrapper::getTexture(const std::string &filename)
 {
-       TextureSpec spec(getTextureId(name));
+       TextureSpec spec(getTextureId(filename));
        return getTexture(spec);
 }
 
@@ -162,12 +189,36 @@ video::ITexture* IrrlichtWrapper::getTextureDirect(const TextureSpec &spec)
                texture_name += "[";
                texture_name += name;
                texture_name += "]";
+               
+               /*
+                       Try to get image from image cache
+               */
+               {
+                       core::map<std::string, video::IImage*>::Node *n;
+                       n = m_imagecache.find(texture_name);
+                       if(n != NULL)
+                       {
+                               video::IImage *image = n->getValue();
+
+                               core::dimension2d<u32> dim = image->getDimension();
+                               baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
+                               image->copyTo(baseimg);
 
+                               dstream<<"INFO: getTextureDirect(): Loaded \""
+                                               <<texture_name<<"\" from image cache"
+                                               <<std::endl;
+                               
+                               // Do not process any further.
+                               continue;
+                       }
+               }
+
+               // Stuff starting with [ are special commands
                if(name[0] != '[')
                {
                        // A normal texture; load it from a file
                        std::string path = porting::getDataPath(name.c_str());
-                       dstream<<"getTextureDirect(): Loading path \""<<path
+                       dstream<<"INFO: getTextureDirect(): Loading path \""<<path
                                        <<"\""<<std::endl;
                        
                        // DEBUG
@@ -192,7 +243,7 @@ video::ITexture* IrrlichtWrapper::getTextureDirect(const TextureSpec &spec)
                        // If base image is NULL, load as base.
                        if(baseimg == NULL)
                        {
-                               dstream<<"Setting "<<name<<" as base"<<std::endl;
+                               dstream<<"INFO: Setting "<<name<<" as base"<<std::endl;
                                /*
                                        Copy it this way to get an alpha channel.
                                        Otherwise images with alpha cannot be blitted on 
@@ -209,7 +260,7 @@ video::ITexture* IrrlichtWrapper::getTextureDirect(const TextureSpec &spec)
                        // Else blit on base.
                        else
                        {
-                               dstream<<"Blitting "<<name<<" on base"<<std::endl;
+                               dstream<<"INFO: Blitting "<<name<<" on base"<<std::endl;
                                // Size of the copied area
                                core::dimension2d<u32> dim = image->getDimension();
                                //core::dimension2d<u32> dim(16,16);
@@ -229,27 +280,85 @@ video::ITexture* IrrlichtWrapper::getTextureDirect(const TextureSpec &spec)
                else
                {
                        // A special texture modification
-                       dstream<<"getTextureDirect(): generating \""<<name<<"\""
+                       dstream<<"INFO: getTextureDirect(): generating \""<<name<<"\""
                                        <<std::endl;
                        if(name.substr(0,6) == "[crack")
                        {
                                u16 progression = stoi(name.substr(6));
                                // Size of the base image
-                               core::dimension2d<u32> dim(16, 16);
+                               core::dimension2d<u32> dim_base = baseimg->getDimension();
+                               // Crack will be drawn at this size
+                               u32 cracksize = 16;
                                // Size of the crack image
-                               //core::dimension2d<u32> dim_crack(16, 16 * CRACK_ANIMATION_LENGTH);
-                               // Position to copy the crack to in the base image
-                               core::position2d<s32> pos_base(0, 0);
+                               core::dimension2d<u32> dim_crack(cracksize,cracksize);
                                // Position to copy the crack from in the crack image
                                core::position2d<s32> pos_other(0, 16 * progression);
 
                                video::IImage *crackimage = driver->createImageFromFile(
                                                porting::getDataPath("crack.png").c_str());
-                               crackimage->copyToWithAlpha(baseimg, v2s32(0,0),
-                                               core::rect<s32>(pos_other, dim),
-                                               video::SColor(255,255,255,255),
-                                               NULL);
-                               crackimage->drop();
+                       
+                               if(crackimage)
+                               {
+                                       /*crackimage->copyToWithAlpha(baseimg, v2s32(0,0),
+                                                       core::rect<s32>(pos_other, dim_base),
+                                                       video::SColor(255,255,255,255),
+                                                       NULL);*/
+
+                                       for(u32 y0=0; y0<dim_base.Height/dim_crack.Height; y0++)
+                                       for(u32 x0=0; x0<dim_base.Width/dim_crack.Width; x0++)
+                                       {
+                                               // Position to copy the crack to in the base image
+                                               core::position2d<s32> pos_base(x0*cracksize, y0*cracksize);
+                                               crackimage->copyToWithAlpha(baseimg, pos_base,
+                                                               core::rect<s32>(pos_other, dim_crack),
+                                                               video::SColor(255,255,255,255),
+                                                               NULL);
+                                       }
+
+                                       crackimage->drop();
+                               }
+                       }
+                       else if(name.substr(0,8) == "[combine")
+                       {
+                               // "[combine:16x128:0,0=stone.png:0,16=grass.png"
+                               Strfnd sf(name);
+                               sf.next(":");
+                               u32 w0 = stoi(sf.next("x"));
+                               u32 h0 = stoi(sf.next(":"));
+                               dstream<<"INFO: combined w="<<w0<<" h="<<h0<<std::endl;
+                               core::dimension2d<u32> dim(w0,h0);
+                               baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
+                               while(sf.atend() == false)
+                               {
+                                       u32 x = stoi(sf.next(","));
+                                       u32 y = stoi(sf.next("="));
+                                       std::string filename = sf.next(":");
+                                       dstream<<"INFO: Adding \""<<filename
+                                                       <<"\" to combined ("<<x<<","<<y<<")"
+                                                       <<std::endl;
+                                       video::IImage *img = driver->createImageFromFile(
+                                                       porting::getDataPath(filename.c_str()).c_str());
+                                       if(img)
+                                       {
+                                               core::dimension2d<u32> dim = img->getDimension();
+                                               dstream<<"INFO: Size "<<dim.Width
+                                                               <<"x"<<dim.Height<<std::endl;
+                                               core::position2d<s32> pos_base(x, y);
+                                               video::IImage *img2 =
+                                                               driver->createImage(video::ECF_A8R8G8B8, dim);
+                                               img->copyTo(img2);
+                                               img->drop();
+                                               img2->copyToWithAlpha(baseimg, pos_base,
+                                                               core::rect<s32>(v2s32(0,0), dim),
+                                                               video::SColor(255,255,255,255),
+                                                               NULL);
+                                               img2->drop();
+                                       }
+                                       else
+                                       {
+                                               dstream<<"WARNING: img==NULL"<<std::endl;
+                                       }
+                               }
                        }
                        else if(name.substr(0,12) == "[progressbar")
                        {
@@ -262,12 +371,31 @@ video::ITexture* IrrlichtWrapper::getTextureDirect(const TextureSpec &spec)
                                                " texture: \""<<name<<"\""<<std::endl;
                        }
                }
+               
+               /*
+                       Add to image cache
+               */
+               if(baseimg != NULL)
+               {
+                       core::map<std::string, video::IImage*>::Node *n;
+                       n = m_imagecache.find(texture_name);
+                       if(n != NULL)
+                       {
+                               video::IImage *img = n->getValue();
+                               if(img != baseimg)
+                               {
+                                       img->drop();
+                               }
+                       }
+                       
+                       m_imagecache[texture_name] = baseimg;
+               }
        }
 
        // If no resulting image, return NULL
        if(baseimg == NULL)
        {
-               dstream<<"getTextureDirect(): baseimg is NULL (attempted to"
+               dstream<<"WARNING: getTextureDirect(): baseimg is NULL (attempted to"
                                " create texture \""<<texture_name<<"\""<<std::endl;
                return NULL;
        }
@@ -281,9 +409,8 @@ video::ITexture* IrrlichtWrapper::getTextureDirect(const TextureSpec &spec)
 
        // Create texture from resulting image
        t = driver->addTexture(texture_name.c_str(), baseimg);
-       baseimg->drop();
 
-       dstream<<"getTextureDirect(): created texture \""<<texture_name
+       dstream<<"INFO: getTextureDirect(): created texture \""<<texture_name
                        <<"\""<<std::endl;
 
        return t;
@@ -321,5 +448,5 @@ void make_progressbar(float value, video::IImage *image)
                }
        }
 }
-
+#endif
 
index 965d012080a1aebc46104e6a571e9b8b0af562d6..55e021bdac03eea7f9c6a88568e1c5ce412ba30b 100644 (file)
@@ -31,6 +31,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <jmutexautolock.h>
 #include <string>
 
+/*
+       NOTE: This is deprecated and should be removed completely
+*/
+
 /*
        A thread-safe texture pointer cache.
        
@@ -38,45 +42,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
        threads, because texture pointers have to be handled in
        background threads.
 */
-#if 0
-class TextureCache
-{
-public:
-       TextureCache()
-       {
-               m_mutex.Init();
-               assert(m_mutex.IsInitialized());
-       }
-       
-       void set(std::string name, video::ITexture *texture)
-       {
-               if(texture == NULL)
-                       return;
-               
-               JMutexAutoLock lock(m_mutex);
-
-               m_textures[name] = texture;
-       }
-       
-       video::ITexture* get(const std::string &name)
-       {
-               JMutexAutoLock lock(m_mutex);
-
-               core::map<std::string, video::ITexture*>::Node *n;
-               n = m_textures.find(name);
-
-               if(n != NULL)
-                       return n->getValue();
-
-               return NULL;
-       }
-
-private:
-       core::map<std::string, video::ITexture*> m_textures;
-       JMutex m_mutex;
-};
-#endif
 
+#if 0
 /*
        A thread-safe texture pointer cache
 */
@@ -116,6 +83,7 @@ private:
        core::map<TextureSpec, video::ITexture*> m_textures;
        JMutex m_mutex;
 };
+#endif
 
 /*
        A thread-safe wrapper for irrlicht, to be accessed from
@@ -124,6 +92,8 @@ private:
        Queues tasks to be done in the main thread.
 
        Also caches texture specification strings to ids and textures.
+
+       TODO: Remove this and move all texture functionality to TextureSource
 */
 
 class IrrlichtWrapper : public IIrrlichtWrapper
@@ -134,6 +104,8 @@ public:
        */
 
        IrrlichtWrapper(IrrlichtDevice *device);
+
+       ~IrrlichtWrapper();
        
        // Run queued tasks
        void Run();
@@ -141,6 +113,8 @@ public:
        // Shutdown wrapper; this disables queued texture fetching
        void Shutdown(bool shutdown);
 
+       IrrlichtDevice* getDevice();
+       
        /*
                These are called from other threads
        */
@@ -151,7 +125,8 @@ public:
        {
                return m_device->getTimer()->getRealTime();
        }
-       
+
+#if 0
        /*
                Format of a texture name:
                        "stone.png" (filename in image data directory)
@@ -167,20 +142,18 @@ public:
        // The reverse of the above
        std::string getTextureName(textureid_t id);
        // Gets a texture based on a filename
-       video::ITexture* getTexture(const std::string &name);
+       video::ITexture* getTexture(const std::string &filename);
        // Gets a texture based on a TextureSpec (a textureid_t is fine too)
        video::ITexture* getTexture(const TextureSpec &spec);
+#endif
        
 private:
        /*
                Non-thread-safe variants of stuff, for internal use
        */
 
-       // DEPRECATED NO-OP
-       //video::ITexture* getTextureDirect(const std::string &spec);
-       
        // Constructs a texture according to spec
-       video::ITexture* getTextureDirect(const TextureSpec &spec);
+       //video::ITexture* getTextureDirect(const TextureSpec &spec);
        
        /*
                Members
@@ -195,14 +168,19 @@ private:
        JMutex m_device_mutex;
        IrrlichtDevice *m_device;
        
+#if 0
        // Queued texture fetches (to be processed by the main thread)
        RequestQueue<TextureSpec, video::ITexture*, u8, u8> m_get_texture_queue;
 
        // Cache of textures by spec
        TextureCache m_texturecache;
 
+       // Cached or generated source images by texture name
+       core::map<std::string, video::IImage*> m_imagecache;
+
        // A mapping from texture id to string spec
        MutexedIdGenerator<std::string> m_namecache;
+#endif
 };
 
 #endif
index ff24fbb87f9c791f41ab23d8f5ea7776c5924a8c..1e30219c59e5df78ad521b245be850f5d8b906d0 100644 (file)
@@ -97,6 +97,9 @@ SUGG: Meshes of blocks could be split into 6 meshes facing into
       different directions and then only those drawn that need to be\r
          - Also an 1-dimensional tile map would be nice probably\r
 \r
+SUGG: Calculate lighting per vertex to get a lighting effect like in\r
+      bartwe's game\r
+\r
 Gaming ideas:\r
 -------------\r
 \r
@@ -363,8 +366,13 @@ Doing now (most important at the top):
 #include "guiMainMenu.h"\r
 #include "mineral.h"\r
 #include "noise.h"\r
+#include "tile.h"\r
+\r
+// TODO: Remove this\r
+IrrlichtWrapper *g_irrlicht = NULL;\r
 \r
-IrrlichtWrapper *g_irrlicht;\r
+// This makes textures\r
+TextureSource *g_texturesource = NULL;\r
 \r
 MapDrawControl draw_control;\r
 \r
@@ -1643,6 +1651,7 @@ int main(int argc, char *argv[])
        \r
        g_device = device;\r
        g_irrlicht = new IrrlichtWrapper(device);\r
+       g_texturesource = new TextureSource(device);\r
 \r
        /*\r
                Speed tests (done after irrlicht is loaded to get timer)\r
@@ -1670,7 +1679,8 @@ int main(int argc, char *argv[])
        video::IVideoDriver* driver = device->getVideoDriver();\r
 \r
        /*\r
-               This changes the minimum allowed number of vertices in a VBO\r
+               This changes the minimum allowed number of vertices in a VBO.\r
+               Default is 500.\r
        */\r
        //driver->setMinHardwareBufferVertexCount(50);\r
 \r
@@ -2061,6 +2071,11 @@ int main(int argc, char *argv[])
                */\r
                g_irrlicht->Run();\r
 \r
+               /*\r
+                       Process TextureSource's queue\r
+               */\r
+               g_texturesource->processQueue();\r
+\r
                /*\r
                        Random calculations\r
                */\r
index e6bb1caacea2511a96473f91dc7ea5e9bf499bf9..9978c537a39281023f158686ca403c17fada5b31 100644 (file)
@@ -20,15 +20,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #ifndef MAIN_HEADER
 #define MAIN_HEADER
 
-#include "irrlichtwrapper.h"
-
 // Settings
+#include "utility.h"
 extern Settings g_settings;
 
 // A thread safe wrapper to irrlicht
 // On a server build, this is always NULL.
+// TODO: Remove this
+#include "irrlichtwrapper.h"
 extern IrrlichtWrapper *g_irrlicht;
 
+// This makes and maps textures
+#include "tile.h"
+extern TextureSource *g_texturesource;
+
 // Debug streams
 
 #include <fstream>
index 696019f3ae702378b690a030de39c93ba5e9727f..e4992618d7ecda391476c5583ea48fa76bfcfa3c 100644 (file)
@@ -1974,7 +1974,7 @@ double tree_amount_2d(u64 seed, v2s16 p)
 {
        double noise = noise2d_perlin(
                        0.5+(float)p.X/250, 0.5+(float)p.Y/250,
-                       seed+2, 5, 0.6);
+                       seed+2, 5, 0.66);
        double zeroval = -0.3;
        if(noise < zeroval)
                return 0;
@@ -2021,9 +2021,9 @@ double base_rock_level_2d(u64 seed, v2s16 p)
                base = base2;*/
 #if 1
        // Higher ground level
-       double higher = (double)WATER_LEVEL + 13. + 50. * noise2d_perlin(
-                       0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
-                       seed+85039, 6, 0.69);
+       double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
+                       0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
+                       seed+85039, 5, 0.69);
        //higher = 30; // For debugging
 
        // Limit higher to at least base
@@ -2042,7 +2042,7 @@ double base_rock_level_2d(u64 seed, v2s16 p)
        //double b = 20;
 
        // Offset to more low
-       double a_off = -0.3;
+       double a_off = -0.15;
        // High/low selector
        /*double a = 0.5 + b * (a_off + noise2d_perlin(
                        0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
@@ -2414,12 +2414,12 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
        /*
                Make dungeons
        */
-       //u32 dungeons_count = relative_volume / 600000;
-       /*u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
+       u32 dungeons_count = relative_volume / 600000;
+       u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
        if(stone_surface_max_y < WATER_LEVEL)
-               bruises_count = 0;*/
-       u32 dungeons_count = 0;
-       u32 bruises_count = 0;
+               bruises_count = 0;
+       /*u32 dungeons_count = 0;
+       u32 bruises_count = 0;*/
        for(u32 jj=0; jj<dungeons_count+bruises_count; jj++)
        {
                s16 min_tunnel_diameter = 2;
@@ -5116,6 +5116,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                        timecheck_counter++;
                        if(timecheck_counter > 50)
                        {
+                               timecheck_counter = 0;
                                int time2 = time(0);
                                if(time2 > time1 + 4)
                                {
@@ -5167,71 +5168,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                                continue;
                        }
 
-#if 0                  
-                       v3s16 blockpos_nodes = block->getPosRelative();
-                       
-                       // Block center position
-                       v3f blockpos(
-                                       ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
-                                       ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
-                                       ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
-                       );
-
-                       // Block position relative to camera
-                       v3f blockpos_relative = blockpos - camera_position;
-
-                       // Distance in camera direction (+=front, -=back)
-                       f32 dforward = blockpos_relative.dotProduct(camera_direction);
-
-                       // Total distance
-                       f32 d = blockpos_relative.getLength();
-                       
-                       if(m_control.range_all == false)
-                       {
-                               // If block is far away, don't draw it
-                               if(d > m_control.wanted_range * BS)
-                                       continue;
-                       }
-                       
-                       // Maximum radius of a block
-                       f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
-                       
-                       // If block is (nearly) touching the camera, don't
-                       // bother validating further (that is, render it anyway)
-                       if(d > block_max_radius * 1.5)
-                       {
-                               // Cosine of the angle between the camera direction
-                               // and the block direction (camera_direction is an unit vector)
-                               f32 cosangle = dforward / d;
-                               
-                               // Compensate for the size of the block
-                               // (as the block has to be shown even if it's a bit off FOV)
-                               // This is an estimate.
-                               cosangle += block_max_radius / dforward;
-
-                               // If block is not in the field of view, skip it
-                               //if(cosangle < cos(FOV_ANGLE/2))
-                               if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
-                                       continue;
-                       }
-#endif                 
-#if 0
-                       v3s16 blockpos_nodes = block->getPosRelative();
-                       
-                       // Block center position
-                       v3f blockpos(
-                                       ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
-                                       ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
-                                       ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
-                       );
-
-                       // Block position relative to camera
-                       v3f blockpos_relative = blockpos - camera_position;
-
-                       // Total distance
-                       f32 d = blockpos_relative.getLength();
-#endif
-                       
 #if 1
                        /*
                                Update expired mesh
@@ -5324,6 +5260,11 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                                        // Render transparent on transparent pass and likewise.
                                        if(transparent == is_transparent_pass)
                                        {
+                                               /*
+                                                       This *shouldn't* hurt too much because Irrlicht
+                                                       doesn't change opengl textures if the old
+                                                       material is set again.
+                                               */
                                                driver->setMaterial(buf->getMaterial());
                                                driver->drawMeshBuffer(buf);
                                                vertex_count += buf->getVertexCount();
index b3ef47282be07617b4a431c7ec0df8ee950f92cd..d18278834469385795bd09839f80c1c1990cc98a 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -513,6 +513,8 @@ public:
        // For debug printing
        virtual void PrintInfo(std::ostream &out);
 
+       bool isSavingEnabled(){ return m_map_saving_enabled; }
+
 private:
        // Seed used for all kinds of randomness
        u64 m_seed;
index e3e90a9a1bd072c5080963529bfbab96bb1e6217..2ec63dbde6e21bdf4f142bc78d273eb8980f2e7b 100644 (file)
@@ -273,13 +273,32 @@ void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
        video::SColor c = video::SColor(alpha,li,li,li);
 
        face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
-                       core::vector2d<f32>(0,1));
-       face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
                        core::vector2d<f32>(abs_scale,1));
+       face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
+                       core::vector2d<f32>(0,1));
        face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
+                       core::vector2d<f32>(0,0));
+       face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
                        core::vector2d<f32>(abs_scale,0));
+       
+       /*float x0 = (float)tile.tx/256.0;
+       float y0 = (float)tile.ty/256.0;
+       float w = ((float)tile.tw + 1.0)/256.0;
+       float h = ((float)tile.th + 1.0)/256.0;*/
+
+       float x0 = tile.texture.pos.X;
+       float y0 = tile.texture.pos.Y;
+       float w = tile.texture.size.X;
+       float h = tile.texture.size.Y;
+
+       face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
+                       core::vector2d<f32>(x0+w*abs_scale, y0+h));
+       face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
+                       core::vector2d<f32>(x0, y0+h));
+       face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
+                       core::vector2d<f32>(x0, y0));
        face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
-                       core::vector2d<f32>(0,0));
+                       core::vector2d<f32>(x0+w*abs_scale, y0));
 
        face.tile = tile;
        //DEBUG
@@ -318,11 +337,26 @@ TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
                }
                if(mod.type == NODEMOD_CRACK)
                {
+                       /*
+                               Get texture id, translate it to name, append stuff to
+                               name, get texture id
+                       */
+                       // Get original texture name
+                       u32 orig_id = spec.texture.id;
+                       std::string orig_name = g_texturesource->getTextureName(orig_id);
+                       // Create new texture name
                        std::ostringstream os;
-                       os<<"[crack"<<mod.param;
-
-                       textureid_t tid = g_irrlicht->getTextureId(os.str());
-                       spec.spec.addTid(tid);
+                       os<<orig_name<<"^[crack"<<mod.param;
+                       //os<<orig_name<<"^[progressbar0.5";
+                       //os<<"mese.png";
+                       // Get new texture
+                       u32 new_id = g_texturesource->getTextureId(os.str());
+                       
+                       dstream<<"MapBlock::getNodeTile(): Switching from "
+                                       <<orig_name<<" to "<<os.str()<<" ("
+                                       <<orig_id<<" to "<<new_id<<")"<<std::endl;
+                       
+                       spec.texture = g_texturesource->getTexture(new_id);
                }
        }
        
@@ -406,7 +440,9 @@ void MapBlock::updateFastFaceRow(
                TileSpec tile0_next;
                TileSpec tile1_next;
                u8 light_next = 0;
-
+               
+               // If at last position, there is nothing to compare to and
+               // the face must be drawn anyway
                if(j != length - 1)
                {
                        p_next = p + translate_dir;
@@ -426,7 +462,31 @@ void MapBlock::updateFastFaceRow(
 
                continuous_tiles_count++;
                
-               if(next_is_different)
+               // This is set to true if the texture doesn't allow more tiling
+               bool end_of_texture = false;
+               /*
+                       If there is no texture, it can be tiled infinitely.
+                       If tiled==0, it means the texture can be tiled infinitely.
+                       Otherwise check tiled agains continuous_tiles_count.
+
+                       This check has to be made for both tiles, because this is
+                       a bit hackish and we know which one we're using only when
+                       the decision to make the faces is made.
+               */
+               if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
+               {
+                       if(tile0.texture.tiled <= continuous_tiles_count)
+                               end_of_texture = true;
+               }
+               if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
+               {
+                       if(tile1.texture.tiled <= continuous_tiles_count)
+                               end_of_texture = true;
+               }
+               
+               //end_of_texture = true; //DEBUG
+               
+               if(next_is_different || end_of_texture)
                {
                        /*
                                Create a face if there should be one
@@ -684,10 +744,6 @@ void MapBlock::updateMesh(u32 daynight_ratio)
                Convert FastFaces to SMesh
        */
 
-       scene::SMesh *mesh_new = NULL;
-       
-       mesh_new = new scene::SMesh();
-       
        MeshCollector collector;
 
        if(fastfaces_new.size() > 0)
@@ -708,8 +764,9 @@ void MapBlock::updateMesh(u32 daynight_ratio)
                        FastFace &f = fastfaces_new[i];
 
                        const u16 indices[] = {0,1,2,2,3,0};
-
-                       video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
+                       
+                       //video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
+                       video::ITexture *texture = f.tile.texture.atlas;
                        if(texture == NULL)
                                continue;
 
@@ -717,11 +774,6 @@ void MapBlock::updateMesh(u32 daynight_ratio)
                        
                        f.tile.applyMaterialOptions(material);
                        
-                       /*if(f.tile.alpha != 255)
-                               material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
-                       else
-                               material.MaterialType = video::EMT_SOLID;*/
-                       
                        collector.append(material, f.vertices, 4, indices, 6);
                }
        }
@@ -742,7 +794,11 @@ void MapBlock::updateMesh(u32 daynight_ratio)
        material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
        material_water1.setFlag(video::EMF_FOG_ENABLE, true);
        material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
-       material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
+       //TODO
+       //material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
+       AtlasPointer pa_water1 = g_texturesource->getTexture(
+                       g_texturesource->getTextureId("water.png"));
+       material_water1.setTexture(0, pa_water1.atlas);
 
        // New-style leaves material
        video::SMaterial material_leaves1;
@@ -751,7 +807,11 @@ void MapBlock::updateMesh(u32 daynight_ratio)
        material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
        material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
        material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-       material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
+       //TODO
+       //material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
+       AtlasPointer pa_leaves1 = g_texturesource->getTexture(
+                       g_texturesource->getTextureId("leaves.png"));
+       material_leaves1.setTexture(0, pa_leaves1.atlas);
 
        for(s16 z=0; z<MAP_BLOCKSIZE; z++)
        for(s16 y=0; y<MAP_BLOCKSIZE; y++)
@@ -804,7 +864,8 @@ void MapBlock::updateMesh(u32 daynight_ratio)
                        //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
                        material.MaterialType
                                        = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       if(dir == v3s16(0,-1,0))
+                       //TODO
+                       /*if(dir == v3s16(0,-1,0))
                                material.setTexture(0,
                                                g_irrlicht->getTexture("torch_on_floor.png"));
                        else if(dir == v3s16(0,1,0))
@@ -816,7 +877,7 @@ void MapBlock::updateMesh(u32 daynight_ratio)
                                                g_irrlicht->getTexture("torch_on_floor.png"));
                        else
                                material.setTexture(0, 
-                                               g_irrlicht->getTexture("torch.png"));
+                                               g_irrlicht->getTexture("torch.png"));*/
 
                        u16 indices[] = {0,1,2,2,3,0};
                        // Add to mesh collector
@@ -974,14 +1035,18 @@ void MapBlock::updateMesh(u32 daynight_ratio)
                                
                                video::S3DVertex vertices[4] =
                                {
-                                       /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
-                                       video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
-                                       video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
-                                       video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
-                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
+                                       /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
                                        video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
                                        video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
-                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
+                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
+                                       video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
+                                                       pa_water1.x0(), pa_water1.y1()),
+                                       video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
+                                                       pa_water1.x1(), pa_water1.y1()),
+                                       video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
+                                                       pa_water1.x1(), pa_water1.y0()),
+                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
+                                                       pa_water1.x0(), pa_water1.y0()),
                                };
                                
                                /*
@@ -1048,10 +1113,18 @@ void MapBlock::updateMesh(u32 daynight_ratio)
                        {
                                video::S3DVertex vertices[4] =
                                {
-                                       video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
+                                       /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
                                        video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
                                        video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
-                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
+                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
+                                       video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
+                                                       pa_water1.x0(), pa_water1.y1()),
+                                       video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
+                                                       pa_water1.x1(), pa_water1.y1()),
+                                       video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
+                                                       pa_water1.x1(), pa_water1.y0()),
+                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
+                                                       pa_water1.x0(), pa_water1.y0()),
                                };
 
                                for(s32 i=0; i<4; i++)
@@ -1092,10 +1165,18 @@ void MapBlock::updateMesh(u32 daynight_ratio)
                        
                        video::S3DVertex vertices[4] =
                        {
-                               video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
+                               /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
                                video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
                                video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
-                               video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
+                               video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
+                               video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
+                                               pa_water1.x0(), pa_water1.y1()),
+                               video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
+                                               pa_water1.x1(), pa_water1.y1()),
+                               video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
+                                               pa_water1.x1(), pa_water1.y0()),
+                               video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
+                                               pa_water1.x0(), pa_water1.y0()),
                        };
 
                        for(s32 i=0; i<4; i++)
@@ -1120,10 +1201,18 @@ void MapBlock::updateMesh(u32 daynight_ratio)
                        {
                                video::S3DVertex vertices[4] =
                                {
-                                       video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
+                                       /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
                                        video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
                                        video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
-                                       video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),
+                                       video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
+                                       video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
+                                               pa_leaves1.x0(), pa_leaves1.y1()),
+                                       video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
+                                               pa_leaves1.x1(), pa_leaves1.y1()),
+                                       video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
+                                               pa_leaves1.x1(), pa_leaves1.y0()),
+                                       video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
+                                               pa_leaves1.x0(), pa_leaves1.y0()),
                                };
 
                                if(j == 0)
@@ -1173,6 +1262,9 @@ void MapBlock::updateMesh(u32 daynight_ratio)
                Add stuff from collector to mesh
        */
        
+       scene::SMesh *mesh_new = NULL;
+       mesh_new = new scene::SMesh();
+       
        collector.fillMesh(mesh_new);
 
        /*
@@ -1191,13 +1283,25 @@ void MapBlock::updateMesh(u32 daynight_ratio)
                mesh_new = NULL;
        }
 
-       // Use VBO for mesh (this just would set this for ever buffer)
-       // This will lead to infinite memory usage because or irrlicht.
-       //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
-       
-       /*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces "
-                       <<"and uses "<<mesh_new->getMeshBufferCount()
-                       <<" materials (meshbuffers)"<<std::endl;*/
+       if(mesh_new)
+       {
+#if 0
+               // Usually 1-700 faces and 1-7 materials
+               std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
+                               <<"and uses "<<mesh_new->getMeshBufferCount()
+                               <<" materials (meshbuffers)"<<std::endl;
+#endif
+
+               // Use VBO for mesh (this just would set this for ever buffer)
+               // This will lead to infinite memory usage because or irrlicht.
+               //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
+
+               /*
+                       NOTE: If that is enabled, some kind of a queue to the main
+                       thread should be made which would call irrlicht to delete
+                       the hardware buffer and then delete the mesh
+               */
+       }
        
        /*
                Replace the mesh
index e9a6ba904f7f879328560c2550e146925dbf5e81..653195ad47e16c8deb7609c973ce4995044aabca 100644 (file)
@@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
+#include "common_irrlicht.h"
 #include "mapnode.h"
 #include "tile.h"
 #include "porting.h"
@@ -31,6 +32,20 @@ ContentFeatures::~ContentFeatures()
                delete translate_to;
 }
 
+void ContentFeatures::setTexture(u16 i, std::string name, u8 alpha)
+{
+       if(g_texturesource)
+       {
+               tiles[i].texture = g_texturesource->getTexture(name);
+       }
+       
+       if(alpha != 255)
+       {
+               tiles[i].alpha = alpha;
+               tiles[i].material_type = MATERIAL_ALPHA_VERTEX;
+       }
+}
+
 struct ContentFeatures g_content_features[256];
 
 ContentFeatures & content_features(u8 i)
@@ -40,56 +55,75 @@ ContentFeatures & content_features(u8 i)
 
 void init_mapnode(IIrrlichtWrapper *irrlicht)
 {
+       // Read some settings
        bool new_style_water = g_settings.getBool("new_style_water");
        bool new_style_leaves = g_settings.getBool("new_style_leaves");
+
+       /*
+               Initialize content feature table
+       */
+       
+       /*
+               Set initial material type to same in all tiles, so that the
+               same material can be used in more stuff.
+               This is set according to the leaves because they are the only
+               differing material to which all materials can be changed to
+               get this optimization.
+       */
+       u8 initial_material_type = MATERIAL_ALPHA_SIMPLE;
+       /*if(new_style_leaves)
+               initial_material_type = MATERIAL_ALPHA_SIMPLE;
+       else
+               initial_material_type = MATERIAL_ALPHA_NONE;*/
+       for(u16 i=0; i<256; i++)
+       {
+               ContentFeatures *f = &g_content_features[i];
+               for(u16 j=0; j<6; j++)
+                       f->tiles[j].material_type = initial_material_type;
+       }
        
        u8 i;
        ContentFeatures *f = NULL;
 
        i = CONTENT_STONE;
        f = &g_content_features[i];
-       f->setAllTextures(irrlicht->getTextureId("stone.png"));
+       f->setAllTextures("stone.png");
        f->param_type = CPT_MINERAL;
        f->is_ground_content = true;
        
        i = CONTENT_GRASS;
        f = &g_content_features[i];
-       f->setAllTextures(TextureSpec(irrlicht->getTextureId("mud.png"),
-                       irrlicht->getTextureId("grass_side.png")));
-       f->setTexture(0, irrlicht->getTextureId("grass.png"));
-       f->setTexture(1, irrlicht->getTextureId("mud.png"));
-       f->setInventoryTexture(irrlicht->getTextureId("grass.png"));
+       f->setAllTextures("mud.png^grass_side.png");
+       f->setTexture(0, "grass.png");
+       f->setTexture(1, "mud.png");
+       //f->setInventoryTexture(irrlicht->getTextureId("grass.png"));
        f->param_type = CPT_MINERAL;
        f->is_ground_content = true;
        
        i = CONTENT_GRASS_FOOTSTEPS;
        f = &g_content_features[i];
-       f->setAllTextures(TextureSpec(irrlicht->getTextureId("mud.png"),
-                       irrlicht->getTextureId("grass_side.png")));
-       f->setTexture(0, irrlicht->getTextureId("grass_footsteps.png"));
-       f->setTexture(1, irrlicht->getTextureId("mud.png"));
-       f->setInventoryTexture(irrlicht->getTextureId("grass_footsteps.png"));
+       //f->setInventoryTexture(irrlicht->getTextureId("grass_footsteps.png"));
        f->param_type = CPT_MINERAL;
        f->is_ground_content = true;
        
        i = CONTENT_MUD;
        f = &g_content_features[i];
-       f->setAllTextures(irrlicht->getTextureId("mud.png"));
+       f->setAllTextures("mud.png");
        f->param_type = CPT_MINERAL;
        f->is_ground_content = true;
        
        i = CONTENT_SAND;
        f = &g_content_features[i];
-       f->setAllTextures(irrlicht->getTextureId("sand.png"));
+       f->setAllTextures("sand.png");
        f->param_type = CPT_MINERAL;
        f->is_ground_content = true;
        
        i = CONTENT_TREE;
        f = &g_content_features[i];
-       f->setAllTextures(irrlicht->getTextureId("tree.png"));
-       f->setTexture(0, irrlicht->getTextureId("tree_top.png"));
-       f->setTexture(1, irrlicht->getTextureId("tree_top.png"));
-       f->setInventoryTexture(irrlicht->getTextureId("tree_top.png"));
+       f->setAllTextures("tree.png");
+       f->setTexture(0, "tree_top.png");
+       f->setTexture(1, "tree_top.png");
+       //f->setInventoryTexture(irrlicht->getTextureId("tree_top.png"));
        f->param_type = CPT_MINERAL;
        f->is_ground_content = true;
        
@@ -105,36 +139,33 @@ void init_mapnode(IIrrlichtWrapper *irrlicht)
        }
        else
        {
-               f->setAllTextures(irrlicht->getTextureId("leaves.png"));
+               f->setAllTextures("[noalpha:leaves.png");
        }
-       /*{
-               TileSpec t;
-               t.spec = TextureSpec(irrlicht->getTextureId("leaves.png"));
-               //t.material_type = MATERIAL_ALPHA_SIMPLE;
-               //t.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
-               f->setAllTiles(t);
-       }*/
        
        i = CONTENT_COALSTONE;
        f = &g_content_features[i];
        //f->translate_to = new MapNode(CONTENT_STONE, MINERAL_COAL);
-       f->setAllTextures(TextureSpec(irrlicht->getTextureId("coal.png"),
-                       irrlicht->getTextureId("mineral_coal.png")));
+       /*f->setAllTextures(TextureSpec(irrlicht->getTextureId("coal.png"),
+                       irrlicht->getTextureId("mineral_coal.png")));*/
+       f->setAllTextures("stone.png^mineral_coal.png");
        f->is_ground_content = true;
        
        i = CONTENT_WOOD;
        f = &g_content_features[i];
-       f->setAllTextures(irrlicht->getTextureId("wood.png"));
+       //f->setAllTextures(irrlicht->getTextureId("wood.png"));
+       f->setAllTextures("wood.png");
        f->is_ground_content = true;
        
        i = CONTENT_MESE;
        f = &g_content_features[i];
-       f->setAllTextures(irrlicht->getTextureId("mese.png"));
+       //f->setAllTextures(irrlicht->getTextureId("mese.png"));
+       f->setAllTextures("mese.png");
        f->is_ground_content = true;
        
        i = CONTENT_CLOUD;
        f = &g_content_features[i];
-       f->setAllTextures(irrlicht->getTextureId("cloud.png"));
+       //f->setAllTextures(irrlicht->getTextureId("cloud.png"));
+       f->setAllTextures("cloud.png");
        f->is_ground_content = true;
        
        i = CONTENT_AIR;
@@ -150,7 +181,7 @@ void init_mapnode(IIrrlichtWrapper *irrlicht)
        
        i = CONTENT_WATER;
        f = &g_content_features[i];
-       f->setInventoryTexture(irrlicht->getTextureId("water.png"));
+       //f->setInventoryTexture(irrlicht->getTextureId("water.png"));
        f->param_type = CPT_LIGHT;
        f->light_propagates = true;
        f->solidness = 0; // Drawn separately, makes no faces
@@ -162,21 +193,23 @@ void init_mapnode(IIrrlichtWrapper *irrlicht)
        
        i = CONTENT_WATERSOURCE;
        f = &g_content_features[i];
-       f->setInventoryTexture(irrlicht->getTextureId("water.png"));
+       //f->setInventoryTexture(irrlicht->getTextureId("water.png"));
        if(new_style_water)
        {
                f->solidness = 0; // drawn separately, makes no faces
        }
        else // old style
        {
-               f->setAllTextures(irrlicht->getTextureId("water.png"), WATER_ALPHA);
+               f->solidness = 1;
+
                TileSpec t;
-               t.spec = TextureSpec(irrlicht->getTextureId("water.png"));
+               if(g_texturesource)
+                       t.texture = g_texturesource->getTexture("water.png");
+               
                t.alpha = WATER_ALPHA;
                t.material_type = MATERIAL_ALPHA_VERTEX;
                t.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
                f->setAllTiles(t);
-               f->solidness = 1;
        }
        f->param_type = CPT_LIGHT;
        f->light_propagates = true;
@@ -188,7 +221,6 @@ void init_mapnode(IIrrlichtWrapper *irrlicht)
        
        i = CONTENT_TORCH;
        f = &g_content_features[i];
-       f->setInventoryTexture(irrlicht->getTextureId("torch_on_floor.png"));
        f->param_type = CPT_LIGHT;
        f->light_propagates = true;
        f->solidness = 0; // drawn separately, makes no faces
@@ -224,13 +256,23 @@ TileSpec MapNode::getTile(v3s16 dir)
        else 
                spec = content_features(d).tiles[dir_i];
        
-       if(content_features(d).param_type == CPT_MINERAL)
+       /*
+               If it contains some mineral, change texture id
+       */
+       if(content_features(d).param_type == CPT_MINERAL && g_texturesource)
        {
                u8 mineral = param & 0x1f;
-               // Add mineral block texture
-               textureid_t tid = mineral_block_texture(mineral);
-               if(tid != 0)
-                       spec.spec.addTid(tid);
+               std::string mineral_texture_name = mineral_block_texture(mineral);
+               if(mineral_texture_name != "")
+               {
+                       u32 orig_id = spec.texture.id;
+                       std::string texture_name = g_texturesource->getTextureName(orig_id);
+                       //texture_name += "^blit:";
+                       texture_name += "^";
+                       texture_name += mineral_texture_name;
+                       u32 new_id = g_texturesource->getTextureId(texture_name);
+                       spec.texture = g_texturesource->getTexture(new_id);
+               }
        }
 
        return spec;
index 0aaa4dc780f8f25767cdffa0e8ae94fb625a59d6..3de1705985dd1081ad57279262ee3621dc38c684 100644 (file)
@@ -132,9 +132,11 @@ struct ContentFeatures
                5: front
        */
        TileSpec tiles[6];
-
+       
+       // TODO: Somehow specify inventory image
        //std::string inventory_image_path;
-       TextureSpec inventory_texture;
+       //TextureSpec inventory_texture;
+       //u32 inventory_texture_id;
 
        bool is_ground_content; //TODO: Remove, use walkable instead
        bool light_propagates;
@@ -167,27 +169,36 @@ struct ContentFeatures
 
        ~ContentFeatures();
        
-       // Quickhands for simple materials
-       void setTexture(u16 i, const TextureSpec &spec, u8 alpha=255)
+       /*
+               Quickhands for simple materials
+       */
+       
+       void setTexture(u16 i, std::string name, u8 alpha=255);
+
+       void setAllTextures(std::string name, u8 alpha=255)
        {
-               tiles[i].spec = spec;
+               for(u16 i=0; i<6; i++)
+               {
+                       setTexture(i, name, alpha);
+               }
+       }
+
+       /*void setTexture(u16 i, AtlasPointer p, u8 alpha=255)
+       {
+               tiles[i].texture = p;
                if(alpha != 255)
                {
                        tiles[i].alpha = alpha;
                        tiles[i].material_type = MATERIAL_ALPHA_VERTEX;
                }
        }
-       void setAllTextures(const TextureSpec &spec, u8 alpha=255)
+       void setAllTextures(AtlasPointer p, u8 alpha=255)
        {
                for(u16 i=0; i<6; i++)
                {
-                       setTexture(i, spec, alpha);
+                       setTexture(i, p, alpha);
                }
-               
-               // Set this too so it can be left as is most times
-               if(inventory_texture.empty())
-                       inventory_texture = spec;
-       }
+       }*/
 
        void setTile(u16 i, const TileSpec &tile)
        {
@@ -201,10 +212,10 @@ struct ContentFeatures
                }
        }
 
-       void setInventoryTexture(const TextureSpec &spec)
+       /*void setInventoryTexture(const TextureSpec &spec)
        {
                inventory_texture = spec;
-       }
+       }*/
 
        /*void setInventoryImage(std::string imgname)
        {
index c333ae6f5d988c93addc249d1d2019b5faa5b223..905f6497cc1465c22f80b1d197f441f257125c30 100644 (file)
@@ -27,7 +27,8 @@ const char *mineral_filenames[MINERAL_COUNT] =
        "mineral_iron.png"
 };
 
-textureid_t mineral_textures[MINERAL_COUNT] = {0};
+//textureid_t mineral_textures[MINERAL_COUNT] = {0};
+std::string mineral_textures[MINERAL_COUNT];
 
 void init_mineral(IIrrlichtWrapper *irrlicht)
 {
@@ -35,14 +36,17 @@ void init_mineral(IIrrlichtWrapper *irrlicht)
        {
                if(mineral_filenames[i] == NULL)
                        continue;
-               mineral_textures[i] = irrlicht->getTextureId(mineral_filenames[i]);
+               //mineral_textures[i] = irrlicht->getTextureId(mineral_filenames[i]);
+               //mineral_textures[i] = 0;
+               mineral_textures[i] = mineral_filenames[i];
        }
 }
 
-textureid_t mineral_block_texture(u8 mineral)
+//textureid_t mineral_block_texture(u8 mineral)
+std::string mineral_block_texture(u8 mineral)
 {
        if(mineral >= MINERAL_COUNT)
-               return 0;
+               return "";
        
        return mineral_textures[mineral];
 }
index c663d92757e63cd5f6114871eaa055feb47038e7..a181b89de8255cdf00f21bfa82c05eec6700a443 100644 (file)
@@ -40,7 +40,8 @@ void init_mineral(IIrrlichtWrapper *irrlicht);
 
 #define MINERAL_COUNT 3
 
-textureid_t mineral_block_texture(u8 mineral);
+//textureid_t mineral_block_texture(u8 mineral);
+std::string mineral_block_texture(u8 mineral);
 
 inline CraftItem * getDiggedMineralItem(u8 mineral)
 {
index 59d9acee4d033ba8f791b9ba5e3d3be73ee29961..f2078b9d06269190ae81c8877a094a38dbdbcbd8 100644 (file)
@@ -1642,6 +1642,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                os<<name<<L",";
                        }
                        os<<L"}";
+                       if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
+                               os<<" WARNING: Map saving is disabled."<<std::endl;
                        // Send message
                        SendChatMessage(peer_id, os.str());
                }
index f14efae11f50b901bbc377b8e5df68220cfb85c6..d8310789d2bd735e7c8024844113ef7da88f4c8d 100644 (file)
@@ -20,6 +20,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #ifndef TEXTURE_HEADER
 #define TEXTURE_HEADER
 
+// This file now contains all that was here
+#include "tile.h"
+
+// TODO: Remove this
+typedef u16 textureid_t;
+
+#if 0
+
 #include "common_irrlicht.h"
 //#include "utility.h"
 #include "debug.h"
@@ -122,3 +130,5 @@ struct TextureSpec
 };
 
 #endif
+
+#endif
index 60e9873c0cd73f8a93fb99d4cc4a2d6d4e60a206..f31f830762fb8096f3c0fcc3b061345343a02fd7 100644 (file)
@@ -18,10 +18,770 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "tile.h"
-//#include "porting.h"
-// For IrrlichtWrapper
-//#include "main.h"
-//#include <string>
+#include "debug.h"
 
-// Nothing here
+TextureSource::TextureSource(IrrlichtDevice *device):
+               m_device(device),
+               m_main_atlas_image(NULL),
+               m_main_atlas_texture(NULL)
+{
+       assert(m_device);
+       
+       m_atlaspointer_cache_mutex.Init();
+       
+       m_main_thread = get_current_thread_id();
+       
+       // Add a NULL AtlasPointer as the first index, named ""
+       m_atlaspointer_cache.push_back(SourceAtlasPointer(""));
+       m_name_to_id[""] = 0;
+
+       // Build main texture atlas
+       buildMainAtlas();
+}
+
+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();
+
+               dstream<<"INFO: 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)
+{
+       //dstream<<"INFO: getTextureId(): name="<<name<<std::endl;
+
+       {
+               /*
+                       See if texture already exists
+               */
+               JMutexAutoLock lock(m_atlaspointer_cache_mutex);
+               core::map<std::string, u32>::Node *n;
+               n = m_name_to_id.find(name);
+               if(n != NULL)
+               {
+                       return n->getValue();
+               }
+       }
+       
+       /*
+               Get texture
+       */
+       if(get_current_thread_id() == m_main_thread)
+       {
+               return getTextureIdDirect(name);
+       }
+       else
+       {
+               dstream<<"INFO: 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;
+               
+               // Throw a request in
+               m_get_texture_queue.add(name, 0, 0, &result_queue);
+               
+               dstream<<"INFO: Waiting for texture from main thread, name="
+                               <<name<<std::endl;
+               
+               try
+               {
+                       // 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;
+               }
+               catch(ItemNotFoundException &e)
+               {
+                       dstream<<"WARNING: Waiting for texture timed out."<<std::endl;
+                       return 0;
+               }
+       }
+       
+       dstream<<"WARNING: getTextureId(): Failed"<<std::endl;
+
+       return 0;
+}
+
+// Draw a progress bar on the image
+void make_progressbar(float value, 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,
+               video::IVideoDriver* driver);
+
+/*
+       Generates an image from a full string like
+       "stone.png^mineral_coal.png^[crack0".
+
+       This is used by buildMainAtlas().
+*/
+video::IImage* generate_image_from_scratch(std::string name,
+               video::IVideoDriver* driver);
+
+/*
+       This method generates all the textures
+*/
+u32 TextureSource::getTextureIdDirect(const std::string &name)
+{
+       dstream<<"INFO: getTextureIdDirect(): name="<<name<<std::endl;
+
+       // Empty name means texture 0
+       if(name == "")
+       {
+               dstream<<"INFO: getTextureIdDirect(): name is empty"<<std::endl;
+               return 0;
+       }
+       
+       /*
+               Calling only allowed from main thread
+       */
+       if(get_current_thread_id() != m_main_thread)
+       {
+               dstream<<"ERROR: TextureSource::getTextureIdDirect() "
+                               "called not from main thread"<<std::endl;
+               return 0;
+       }
+
+       /*
+               See if texture already exists
+       */
+       {
+               JMutexAutoLock lock(m_atlaspointer_cache_mutex);
+
+               core::map<std::string, u32>::Node *n;
+               n = m_name_to_id.find(name);
+               if(n != NULL)
+               {
+                       dstream<<"INFO: getTextureIdDirect(): name="<<name
+                                       <<" found in cache"<<std::endl;
+                       return n->getValue();
+               }
+       }
+
+       dstream<<"INFO: getTextureIdDirect(): name="<<name
+                       <<" NOT found in cache. Creating it."<<std::endl;
+       
+       /*
+               Get the base image
+       */
+
+       char separator = '^';
+
+       /*
+               This is set to the id of the base image.
+               If left 0, there is no base image and a completely new image
+               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--)
+       {
+               if(name[i] == separator)
+               {
+                       last_separator_position = i;
+                       break;
+               }
+       }
+       /*
+               If separator was found, construct the base name and make the
+               base image using a recursive call
+       */
+       std::string base_image_name;
+       if(last_separator_position != -1)
+       {
+               // Construct base name
+               base_image_name = name.substr(0, last_separator_position);
+               dstream<<"INFO: getTextureIdDirect(): Calling itself recursively"
+                               " to get base image, name="<<base_image_name<<std::endl;
+               base_image_id = getTextureIdDirect(base_image_name);
+       }
+       
+       dstream<<"base_image_id="<<base_image_id<<std::endl;
+       
+       video::IVideoDriver* driver = m_device->getVideoDriver();
+       assert(driver);
+
+       video::ITexture *t = NULL;
+
+       /*
+               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_atlaspointer_cache_mutex);
+
+               SourceAtlasPointer ap = m_atlaspointer_cache[base_image_id];
+
+               video::IImage *image = ap.atlas_img;
+
+               core::dimension2d<u32> dim = ap.intsize;
+
+               baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
+
+               core::position2d<s32> pos_to(0,0);
+               core::position2d<s32> pos_from = ap.intpos;
+               
+               image->copyTo(
+                               baseimg, // target
+                               v2s32(0,0), // position in target
+                               core::rect<s32>(pos_from, dim) // from
+               );
+
+               dstream<<"INFO: getTextureIdDirect(): Loaded \""
+                               <<base_image_name<<"\" from image cache"
+                               <<std::endl;
+       }
+       
+       /*
+               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);
+       dstream<<"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, driver) == false)
+       {
+               dstream<<"INFO: getTextureIdDirect(): "
+                               "failed to generate \""<<last_part_of_name<<"\""
+                               <<std::endl;
+               return 0;
+       }
+
+       // If no resulting image, return NULL
+       if(baseimg == NULL)
+       {
+               dstream<<"WARNING: getTextureIdDirect(): baseimg is NULL (attempted to"
+                               " create texture \""<<name<<"\""<<std::endl;
+               return 0;
+       }
+
+       // Create texture from resulting image
+       t = driver->addTexture(name.c_str(), baseimg);
+       
+       // If no texture
+       if(t == NULL)
+               return 0;
+
+       /*
+               Add texture to caches
+       */
+
+       JMutexAutoLock lock(m_atlaspointer_cache_mutex);
+       
+       u32 id = m_atlaspointer_cache.size();
+       AtlasPointer ap(id);
+       ap.atlas = t;
+       ap.pos = v2f(0,0);
+       ap.size = v2f(1,1);
+       ap.tiled = 0;
+       SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg->getDimension());
+       m_atlaspointer_cache.push_back(nap);
+       m_name_to_id.insert(name, id);
+
+       dstream<<"INFO: getTextureIdDirect(): name="<<name
+                       <<": succesfully returning id="<<id<<std::endl;
+       
+       return id;
+}
+
+std::string TextureSource::getTextureName(u32 id)
+{
+       JMutexAutoLock lock(m_atlaspointer_cache_mutex);
+
+       if(id >= m_atlaspointer_cache.size())
+       {
+               dstream<<"WARNING: TextureSource::getTextureName(): id="<<id
+                               <<" >= m_atlaspointer_cache.size()="
+                               <<m_atlaspointer_cache.size()<<std::endl;
+               return "";
+       }
+       
+       return m_atlaspointer_cache[id].name;
+}
+
+
+AtlasPointer TextureSource::getTexture(u32 id)
+{
+       JMutexAutoLock lock(m_atlaspointer_cache_mutex);
+
+       if(id >= m_atlaspointer_cache.size())
+               return AtlasPointer(0, NULL);
+       
+       return m_atlaspointer_cache[id].a;
+}
+
+void TextureSource::buildMainAtlas() 
+{
+       dstream<<"TextureSource::buildMainAtlas()"<<std::endl;
+
+       //return; // Disable (for testing)
+       
+       video::IVideoDriver* driver = m_device->getVideoDriver();
+       assert(driver);
+
+       JMutexAutoLock lock(m_atlaspointer_cache_mutex);
+
+       // Create an image of the right size
+       core::dimension2d<u32> atlas_dim(1024,1024);
+       video::IImage *atlas_img =
+                       driver->createImage(video::ECF_A8R8G8B8, atlas_dim);
+
+       /*
+               A list of stuff to add. This should contain as much of the
+               stuff shown in game as possible, to minimize texture changes.
+       */
+
+       core::array<std::string> sourcelist;
+
+       sourcelist.push_back("stone.png");
+       sourcelist.push_back("mud.png");
+       sourcelist.push_back("sand.png");
+       sourcelist.push_back("grass.png");
+       sourcelist.push_back("mud.png");
+       sourcelist.push_back("tree.png");
+       sourcelist.push_back("tree_top.png");
+       sourcelist.push_back("water.png");
+       sourcelist.push_back("leaves.png");
+       sourcelist.push_back("mud.png^grass_side.png");
+       
+       sourcelist.push_back("stone.png^mineral_coal.png");
+       sourcelist.push_back("stone.png^mineral_iron.png");
+       sourcelist.push_back("mud.png^mineral_coal.png");
+       sourcelist.push_back("mud.png^mineral_iron.png");
+       sourcelist.push_back("sand.png^mineral_coal.png");
+       sourcelist.push_back("sand.png^mineral_iron.png");
+       
+       /*
+               First pass: generate almost everything
+       */
+       core::position2d<s32> pos_in_atlas(0,0);
+       for(u32 i=0; i<sourcelist.size(); i++)
+       {
+               std::string name = sourcelist[i];
+
+               /*video::IImage *img = driver->createImageFromFile(
+                               porting::getDataPath(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, driver);
+               core::dimension2d<u32> dim = img2->getDimension();
+               
+               // Tile it a few times in the X direction
+               u16 xwise_tiling = 16;
+               for(u32 j=0; j<xwise_tiling; j++)
+               {
+                       // Copy the copy to the atlas
+                       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->drop();
+
+               /*
+                       Add texture to caches
+               */
+               
+               // Get next id
+               u32 id = m_atlaspointer_cache.size();
+
+               // Create AtlasPointer
+               AtlasPointer ap(id);
+               ap.atlas = NULL; // Set on the second pass
+               ap.pos = v2f((float)pos_in_atlas.X/(float)atlas_dim.Width,
+                               (float)pos_in_atlas.Y/(float)atlas_dim.Height);
+               ap.size = v2f((float)dim.Width/(float)atlas_dim.Width,
+                               (float)dim.Width/(float)atlas_dim.Height);
+               ap.tiled = xwise_tiling;
+
+               // Create SourceAtlasPointer and add to containers
+               SourceAtlasPointer nap(name, ap, atlas_img, pos_in_atlas, dim);
+               m_atlaspointer_cache.push_back(nap);
+               m_name_to_id.insert(name, id);
+                       
+               // Increment position
+               pos_in_atlas.Y += dim.Height;
+       }
+
+       /*
+               Make texture
+       */
+       video::ITexture *t = driver->addTexture("__main_atlas__", atlas_img);
+       assert(t);
+
+       /*
+               Second pass: set texture pointer in generated AtlasPointers
+       */
+       for(u32 i=0; i<sourcelist.size(); i++)
+       {
+               std::string name = sourcelist[i];
+               u32 id = m_name_to_id[name];
+               m_atlaspointer_cache[id].a.atlas = t;
+       }
+
+       /*
+               Write image to file so that it can be inspected
+       */
+       driver->writeImageToFile(atlas_img, 
+                       porting::getDataPath("main_atlas.png").c_str());
+}
+
+video::IImage* generate_image_from_scratch(std::string name,
+               video::IVideoDriver* driver)
+{
+       dstream<<"INFO: generate_image_from_scratch(): "
+                       "name="<<name<<std::endl;
+       
+       /*
+               Get the base image
+       */
+
+       video::IImage *baseimg = NULL;
+
+       char separator = '^';
+
+       // Find last meta separator in name
+       s32 last_separator_position = -1;
+       for(s32 i=name.size()-1; i>=0; i--)
+       {
+               if(name[i] == separator)
+               {
+                       last_separator_position = i;
+                       break;
+               }
+       }
+
+       /*dstream<<"INFO: generate_image_from_scratch(): "
+                       <<"last_separator_position="<<last_separator_position
+                       <<std::endl;*/
+
+       /*
+               If separator was found, construct the base name and make the
+               base image using a recursive call
+       */
+       std::string base_image_name;
+       if(last_separator_position != -1)
+       {
+               // Construct base name
+               base_image_name = name.substr(0, last_separator_position);
+               dstream<<"INFO: generate_image_from_scratch(): Calling itself recursively"
+                               " to get base image, name="<<base_image_name<<std::endl;
+               baseimg = generate_image_from_scratch(base_image_name, driver);
+       }
+       
+       /*
+               Parse out the last part of the name of the image and act
+               according to it
+       */
+
+       std::string last_part_of_name = name.substr(last_separator_position+1);
+       dstream<<"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, driver) == false)
+       {
+               dstream<<"INFO: generate_image_from_scratch(): "
+                               "failed to generate \""<<last_part_of_name<<"\""
+                               <<std::endl;
+               return NULL;
+       }
+       
+       return baseimg;
+}
+
+bool generate_image(std::string part_of_name, video::IImage *& baseimg,
+               video::IVideoDriver* driver)
+{
+       // Stuff starting with [ are special commands
+       if(part_of_name[0] != '[')
+       {
+               // A normal texture; load it from a file
+               std::string path = porting::getDataPath(part_of_name.c_str());
+               dstream<<"INFO: getTextureIdDirect(): Loading path \""<<path
+                               <<"\""<<std::endl;
+               
+               video::IImage *image = driver->createImageFromFile(path.c_str());
+
+               if(image == NULL)
+               {
+                       dstream<<"WARNING: Could not load image \""<<part_of_name
+                                       <<"\" from path \""<<path<<"\""
+                                       <<" while building texture"<<std::endl;
+                       return false;
+               }
+
+               // If base image is NULL, load as base.
+               if(baseimg == NULL)
+               {
+                       dstream<<"INFO: Setting "<<part_of_name<<" as base"<<std::endl;
+                       /*
+                               Copy it this way to get an alpha channel.
+                               Otherwise images with alpha cannot be blitted on 
+                               images that don't have alpha in the original file.
+                       */
+                       core::dimension2d<u32> dim = image->getDimension();
+                       baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
+                       image->copyTo(baseimg);
+                       image->drop();
+               }
+               // Else blit on base.
+               else
+               {
+                       dstream<<"INFO: Blitting "<<part_of_name<<" on base"<<std::endl;
+                       // Size of the copied area
+                       core::dimension2d<u32> dim = image->getDimension();
+                       //core::dimension2d<u32> dim(16,16);
+                       // Position to copy the blitted to in the base image
+                       core::position2d<s32> pos_to(0,0);
+                       // Position to copy the blitted from in the blitted image
+                       core::position2d<s32> pos_from(0,0);
+                       // Blit
+                       image->copyToWithAlpha(baseimg, pos_to,
+                                       core::rect<s32>(pos_from, dim),
+                                       video::SColor(255,255,255,255),
+                                       NULL);
+                       // Drop image
+                       image->drop();
+               }
+       }
+       else
+       {
+               // A special texture modification
+
+               dstream<<"INFO: getTextureIdDirect(): generating special "
+                               <<"modification \""<<part_of_name<<"\""
+                               <<std::endl;
+
+               if(part_of_name.substr(0,6) == "[crack")
+               {
+                       if(baseimg == NULL)
+                       {
+                               dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
+                                               <<"for part_of_name="<<part_of_name
+                                               <<", cancelling."<<std::endl;
+                               return false;
+                       }
+
+                       u16 progression = stoi(part_of_name.substr(6));
+                       // Size of the base image
+                       core::dimension2d<u32> dim_base = baseimg->getDimension();
+                       // Crack will be drawn at this size
+                       u32 cracksize = 16;
+                       // Size of the crack image
+                       core::dimension2d<u32> dim_crack(cracksize,cracksize);
+                       // Position to copy the crack from in the crack image
+                       core::position2d<s32> pos_other(0, 16 * progression);
+
+                       video::IImage *crackimage = driver->createImageFromFile(
+                                       porting::getDataPath("crack.png").c_str());
+               
+                       if(crackimage)
+                       {
+                               /*crackimage->copyToWithAlpha(baseimg, v2s32(0,0),
+                                               core::rect<s32>(pos_other, dim_base),
+                                               video::SColor(255,255,255,255),
+                                               NULL);*/
+
+                               for(u32 y0=0; y0<dim_base.Height/dim_crack.Height; y0++)
+                               for(u32 x0=0; x0<dim_base.Width/dim_crack.Width; x0++)
+                               {
+                                       // Position to copy the crack to in the base image
+                                       core::position2d<s32> pos_base(x0*cracksize, y0*cracksize);
+                                       crackimage->copyToWithAlpha(baseimg, pos_base,
+                                                       core::rect<s32>(pos_other, dim_crack),
+                                                       video::SColor(255,255,255,255),
+                                                       NULL);
+                               }
+
+                               crackimage->drop();
+                       }
+               }
+               else if(part_of_name.substr(0,8) == "[combine")
+               {
+                       // "[combine:16x128:0,0=stone.png:0,16=grass.png"
+                       Strfnd sf(part_of_name);
+                       sf.next(":");
+                       u32 w0 = stoi(sf.next("x"));
+                       u32 h0 = stoi(sf.next(":"));
+                       dstream<<"INFO: combined w="<<w0<<" h="<<h0<<std::endl;
+                       core::dimension2d<u32> dim(w0,h0);
+                       baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
+                       while(sf.atend() == false)
+                       {
+                               u32 x = stoi(sf.next(","));
+                               u32 y = stoi(sf.next("="));
+                               std::string filename = sf.next(":");
+                               dstream<<"INFO: Adding \""<<filename
+                                               <<"\" to combined ("<<x<<","<<y<<")"
+                                               <<std::endl;
+                               video::IImage *img = driver->createImageFromFile(
+                                               porting::getDataPath(filename.c_str()).c_str());
+                               if(img)
+                               {
+                                       core::dimension2d<u32> dim = img->getDimension();
+                                       dstream<<"INFO: Size "<<dim.Width
+                                                       <<"x"<<dim.Height<<std::endl;
+                                       core::position2d<s32> pos_base(x, y);
+                                       video::IImage *img2 =
+                                                       driver->createImage(video::ECF_A8R8G8B8, dim);
+                                       img->copyTo(img2);
+                                       img->drop();
+                                       img2->copyToWithAlpha(baseimg, pos_base,
+                                                       core::rect<s32>(v2s32(0,0), dim),
+                                                       video::SColor(255,255,255,255),
+                                                       NULL);
+                                       img2->drop();
+                               }
+                               else
+                               {
+                                       dstream<<"WARNING: img==NULL"<<std::endl;
+                               }
+                       }
+               }
+               else if(part_of_name.substr(0,12) == "[progressbar")
+               {
+                       if(baseimg == NULL)
+                       {
+                               dstream<<"WARNING: getTextureIdDirect(): baseimg==NULL "
+                                               <<"for part_of_name="<<part_of_name
+                                               <<", cancelling."<<std::endl;
+                               return false;
+                       }
+
+                       float value = stof(part_of_name.substr(12));
+                       make_progressbar(value, baseimg);
+               }
+               // "[noalpha:filename.png"
+               // Use an image without it's alpha channel
+               else if(part_of_name.substr(0,8) == "[noalpha")
+               {
+                       if(baseimg != NULL)
+                       {
+                               dstream<<"WARNING: getTextureIdDirect(): 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 = porting::getDataPath(filename.c_str());
+
+                       dstream<<"INFO: getTextureIdDirect(): Loading path \""<<path
+                                       <<"\""<<std::endl;
+                       
+                       video::IImage *image = driver->createImageFromFile(path.c_str());
+                       
+                       if(image == NULL)
+                       {
+                               dstream<<"WARNING: getTextureIdDirect(): 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();
+                       }
+               }
+               else
+               {
+                       dstream<<"WARNING: getTextureIdDirect(): Invalid "
+                                       " modification: \""<<part_of_name<<"\""<<std::endl;
+               }
+       }
+
+       return true;
+}
+
+void make_progressbar(float value, video::IImage *image)
+{
+       if(image == NULL)
+               return;
+       
+       core::dimension2d<u32> size = image->getDimension();
+
+       u32 barheight = 1;
+       u32 barpad_x = 1;
+       u32 barpad_y = 1;
+       u32 barwidth = size.Width - barpad_x*2;
+       v2u32 barpos(barpad_x, size.Height - barheight - barpad_y);
+
+       u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5);
+
+       video::SColor active(255,255,0,0);
+       video::SColor inactive(255,0,0,0);
+       for(u32 x0=0; x0<barwidth; x0++)
+       {
+               video::SColor *c;
+               if(x0 < barvalue_i)
+                       c = &active;
+               else
+                       c = &inactive;
+               u32 x = x0 + barpos.X;
+               for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
+               {
+                       image->setPixel(x,y, *c);
+               }
+       }
+}
 
index a68731f55cedc1b6b0817e8b95ad6455b9a36585..b846b467b775c73d5c551b0aa7bb777a1e42abec 100644 (file)
@@ -21,10 +21,208 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define TILE_HEADER
 
 #include "common_irrlicht.h"
-//#include "utility.h"
-#include "texture.h"
+#include "threads.h"
+#include "utility.h"
 #include <string>
 
+/*
+       Specifies a texture in an atlas.
+
+       This is used to specify single textures also.
+
+       This has been designed to be small enough to be thrown around a lot.
+*/
+struct AtlasPointer
+{
+       u32 id; // Texture id
+       video::ITexture *atlas; // Atlas in where the texture is
+       v2f pos; // Position in atlas
+       v2f size; // Size in atlas
+       u16 tiled; // X-wise tiling count. If 0, width of atlas is width of image.
+
+       AtlasPointer(
+                       u16 id_,
+                       video::ITexture *atlas_=NULL,
+                       v2f pos_=v2f(0,0),
+                       v2f size_=v2f(1,1),
+                       u16 tiled_=1
+               ):
+               id(id_),
+               atlas(atlas_),
+               pos(pos_),
+               size(size_),
+               tiled(tiled_)
+       {
+       }
+
+       bool operator==(const AtlasPointer &other)
+       {
+               return (
+                       id == other.id
+               );
+               /*return (
+                       id == other.id &&
+                       atlas == other.atlas &&
+                       pos == other.pos &&
+                       size == other.size &&
+                       tiled == other.tiled
+               );*/
+       }
+
+       float x0(){ return pos.X; }
+       float x1(){ return pos.X + size.X; }
+       float y0(){ return pos.Y; }
+       float y1(){ return pos.Y + size.Y; }
+};
+
+/*
+       An internal variant of the former with more data.
+*/
+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_)
+       {
+       }
+};
+
+/*
+       Creates and caches textures.
+*/
+class TextureSource
+{
+public:
+       TextureSource(IrrlichtDevice *device);
+       ~TextureSource();
+
+       /*
+               Processes queued texture requests from other threads.
+
+               Shall be called from the main thread.
+       */
+       void processQueue();
+       
+       /*
+               Example case:
+               Now, assume a texture with the id 1 exists, and has the name
+               "stone.png^mineral1".
+               Then a random thread calls getTextureId for a texture called
+               "stone.png^mineral1^crack0".
+               ...Now, WTF should happen? Well:
+               - getTextureId strips off stuff recursively from the end until
+                 the remaining part is found, or nothing is left when
+                 something is stripped out
+
+               But it is slow to search for textures by names and modify them
+               like that?
+               - ContentFeatures is made to contain ids for the basic plain
+                 textures
+               - Crack textures can be slow by themselves, but the framework
+                 must be fast.
+
+               Example case #2:
+               - Assume a texture with the id 1 exists, and has the name
+                 "stone.png^mineral1" and is specified as a part of some atlas.
+               - Now MapBlock::getNodeTile() stumbles upon a node which uses
+                 texture id 1, and finds out that NODEMOD_CRACK must be applied
+                 with progression=0
+               - It finds out the name of the texture with getTextureName(1),
+                 appends "^crack0" to it and gets a new texture id with
+                 getTextureId("stone.png^mineral1^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"
+               "stone.png^crack2"
+               "stone.png^blit:mineral_coal.png"
+               "stone.png^blit:mineral_coal.png^crack1"
+
+               - If texture specified by name is found from cache, return the
+                 cached id.
+               - Otherwise generate the texture, add to cache and return id.
+                 Recursion is used to find out the largest found part of the
+                 texture and continue based on it.
+
+               The id 0 points to a NULL texture. It is returned in case of error.
+       */
+       u32 getTextureIdDirect(const std::string &name);
+
+       /*
+               Finds out the name of a cached texture.
+       */
+       std::string getTextureName(u32 id);
+
+       /*
+               If texture specified by the name pointed by the id doesn't
+               exist, create it, then return the cached texture.
+
+               Can be called from any thread. If called from some other thread
+               and not found in cache, the call is queued to the main thread
+               for processing.
+       */
+       AtlasPointer getTexture(u32 id);
+       
+       AtlasPointer getTexture(const std::string &name)
+       {
+               return getTexture(getTextureId(name));
+       }
+
+private:
+       /*
+               Build the main texture atlas which contains most of the
+               textures.
+               
+               This is called by the constructor.
+       */
+       void buildMainAtlas();
+       
+       // The id of the thread that is allowed to use irrlicht directly
+       threadid_t m_main_thread;
+       // The irrlicht device
+       IrrlichtDevice *m_device;
+       
+       // A texture id is index in this array.
+       // The first position contains a NULL texture.
+       core::array<SourceAtlasPointer> m_atlaspointer_cache;
+       // Maps a texture name to an index in the former.
+       core::map<std::string, u32> m_name_to_id;
+       // The two former containers are behind this mutex
+       JMutex m_atlaspointer_cache_mutex;
+       
+       // Main texture atlas. This is filled at startup and is then not touched.
+       video::IImage *m_main_atlas_image;
+       video::ITexture *m_main_atlas_texture;
+
+       // Queued texture fetches (to be processed by the main thread)
+       RequestQueue<std::string, u32, u8, u8> m_get_texture_queue;
+};
+
 enum MaterialType{
        MATERIAL_ALPHA_NONE,
        MATERIAL_ALPHA_VERTEX,
@@ -38,12 +236,17 @@ enum MaterialType{
 /*
        This fully defines the looks of a tile.
        The SMaterial of a tile is constructed according to this.
+
+       TODO: Change this to use an AtlasPointer
 */
 struct TileSpec
 {
        TileSpec():
+               texture(0),
                alpha(255),
                material_type(MATERIAL_ALPHA_NONE),
+               // Use this so that leaves don't need a separate material
+               //material_type(MATERIAL_ALPHA_SIMPLE),
                material_flags(
                        MATERIAL_FLAG_BACKFACE_CULLING
                )
@@ -53,7 +256,7 @@ struct TileSpec
        bool operator==(TileSpec &other)
        {
                return (
-                       spec == other.spec &&
+                       texture == other.texture &&
                        alpha == other.alpha &&
                        material_type == other.material_type &&
                        material_flags == other.material_flags
@@ -80,8 +283,14 @@ struct TileSpec
                material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING) ? true : false;
        }
        
-       // Specification of texture
-       TextureSpec spec;
+       // NOTE: Deprecated, i guess?
+       void setTexturePos(u8 tx_, u8 ty_, u8 tw_, u8 th_)
+       {
+               texture.pos = v2f((float)tx_/256.0, (float)ty_/256.0);
+               texture.size = v2f(((float)tw_ + 1.0)/256.0, ((float)th_ + 1.0)/256.0);
+       }
+       
+       AtlasPointer texture;
        // Vertex alpha
        u8 alpha;
        // Material type