]> git.lizzy.rs Git - minetest.git/commitdiff
MapBlockMesh, mesh animation system, urgent mesh updates, athmospheric light, removed...
authorKahrl <kahrl@gmx.net>
Tue, 13 Mar 2012 17:56:12 +0000 (18:56 +0100)
committerPerttu Ahola <celeron55@gmail.com>
Thu, 15 Mar 2012 19:45:44 +0000 (21:45 +0200)
22 files changed:
src/client.cpp
src/client.h
src/content_mapblock.cpp
src/content_mapblock.h
src/environment.cpp
src/environment.h
src/game.cpp
src/itemdef.cpp
src/map.cpp
src/map.h
src/mapblock.cpp
src/mapblock.h
src/mapblock_mesh.cpp
src/mapblock_mesh.h
src/mapblock_nodemod.h [deleted file]
src/mapnode.cpp
src/mapnode.h
src/nodedef.cpp
src/nodedef.h
src/tile.cpp
src/tile.h
src/utility.h

index 14f93a1a18145524fd8c463a91af9f81aba89864..203a905bc7635229e7707504e40a70a1fcecc002 100644 (file)
@@ -82,8 +82,9 @@ MeshUpdateQueue::~MeshUpdateQueue()
 {
        JMutexAutoLock lock(m_mutex);
 
-       core::list<QueuedMeshUpdate*>::Iterator i;
-       for(i=m_queue.begin(); i!=m_queue.end(); i++)
+       for(std::vector<QueuedMeshUpdate*>::iterator
+                       i = m_queue.begin();
+                       i != m_queue.end(); i++)
        {
                QueuedMeshUpdate *q = *i;
                delete q;
@@ -93,7 +94,7 @@ MeshUpdateQueue::~MeshUpdateQueue()
 /*
        peer_id=0 adds with nobody to send to
 */
-void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server)
+void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
 {
        DSTACK(__FUNCTION_NAME);
 
@@ -101,12 +102,16 @@ void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_se
 
        JMutexAutoLock lock(m_mutex);
 
+       if(urgent)
+               m_urgents.insert(p);
+
        /*
                Find if block is already in queue.
                If it is, update the data and quit.
        */
-       core::list<QueuedMeshUpdate*>::Iterator i;
-       for(i=m_queue.begin(); i!=m_queue.end(); i++)
+       for(std::vector<QueuedMeshUpdate*>::iterator
+                       i = m_queue.begin();
+                       i != m_queue.end(); i++)
        {
                QueuedMeshUpdate *q = *i;
                if(q->p == p)
@@ -136,12 +141,19 @@ QueuedMeshUpdate * MeshUpdateQueue::pop()
 {
        JMutexAutoLock lock(m_mutex);
 
-       core::list<QueuedMeshUpdate*>::Iterator i = m_queue.begin();
-       if(i == m_queue.end())
-               return NULL;
-       QueuedMeshUpdate *q = *i;
-       m_queue.erase(i);
-       return q;
+       bool must_be_urgent = !m_urgents.empty();
+       for(std::vector<QueuedMeshUpdate*>::iterator
+                       i = m_queue.begin();
+                       i != m_queue.end(); i++)
+       {
+               QueuedMeshUpdate *q = *i;
+               if(must_be_urgent && m_urgents.count(q->p) == 0)
+                       continue;
+               m_queue.erase(i);
+               m_urgents.erase(q->p);
+               return q;
+       }
+       return NULL;
 }
 
 /*
@@ -178,8 +190,12 @@ void * MeshUpdateThread::Thread()
 
                ScopeProfiler sp(g_profiler, "Client: Mesh making");
 
-               scene::SMesh *mesh_new = NULL;
-               mesh_new = makeMapBlockMesh(q->data, m_gamedef);
+               MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
+               if(mesh_new->getMesh()->getMeshBufferCount() == 0)
+               {
+                       delete mesh_new;
+                       mesh_new = NULL;
+               }
 
                MeshUpdateResult r;
                r.p = q->p;
@@ -227,6 +243,9 @@ Client::Client(
        m_inventory_updated(false),
        m_inventory_from_server(NULL),
        m_inventory_from_server_age(0.0),
+       m_animation_time(0),
+       m_crack_level(-1),
+       m_crack_pos(0,0,0),
        m_time_of_day(0),
        m_map_seed(0),
        m_password(password),
@@ -309,6 +328,10 @@ void Client::step(float dtime)
        else
                m_ignore_damage_timer = 0.0;
        
+       m_animation_time += dtime;
+       if(m_animation_time > 60.0)
+               m_animation_time -= 60.0;
+
        //infostream<<"Client steps "<<dtime<<std::endl;
 
        {
@@ -635,7 +658,18 @@ void Client::step(float dtime)
                        MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
                        if(block)
                        {
-                               block->replaceMesh(r.mesh);
+                               //JMutexAutoLock lock(block->mesh_mutex);
+
+                               // Delete the old mesh
+                               if(block->mesh != NULL)
+                               {
+                                       // TODO: Remove hardware buffers of meshbuffers of block->mesh
+                                       delete block->mesh;
+                                       block->mesh = NULL;
+                               }
+
+                               // Replace with the new mesh
+                               block->mesh = r.mesh;
                        }
                        if(r.ack_block_to_server)
                        {
@@ -868,9 +902,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                
                //TimeTaker t1("TOCLIENT_REMOVENODE");
                
-               // This will clear the cracking animation after digging
-               ((ClientMap&)m_env.getMap()).clearTempMod(p);
-
                removeNode(p);
        }
        else if(command == TOCLIENT_ADDNODE)
@@ -960,13 +991,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                m_con.Send(PEER_ID_SERVER, 1, reply, true);
 #endif
 
-               /*
-                       Update Mesh of this block and blocks at x-, y- and z-.
-                       Environment should not be locked as it interlocks with the
-                       main thread, from which is will want to retrieve textures.
-               */
-
-               //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
                /*
                        Add it to mesh update queue and set it to be acknowledged after update.
                */
@@ -1837,12 +1861,14 @@ void Client::removeNode(v3s16 p)
        {
        }
        
+       // add urgent task to update the modified node
+       addUpdateMeshTaskForNode(p, false, true);
+
        for(core::map<v3s16, MapBlock * >::Iterator
                        i = modified_blocks.getIterator();
                        i.atEnd() == false; i++)
        {
                v3s16 p = i.getNode()->getKey();
-               //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
                addUpdateMeshTaskWithEdge(p);
        }
 }
@@ -1863,14 +1889,13 @@ void Client::addNode(v3s16 p, MapNode n)
        catch(InvalidPositionException &e)
        {}
        
-       //TimeTaker timer2("Client::addNode(): updateMeshes");
+       //TimeTaker timer2("Client::addNode(): addUpdateMeshTaskWithEdge");
 
        for(core::map<v3s16, MapBlock * >::Iterator
                        i = modified_blocks.getIterator();
                        i.atEnd() == false; i++)
        {
                v3s16 p = i.getNode()->getKey();
-               //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
                addUpdateMeshTaskWithEdge(p);
        }
 }
@@ -2051,51 +2076,47 @@ core::list<std::wstring> Client::getConnectedPlayerNames()
        return playerNames;
 }
 
-u32 Client::getDayNightRatio()
+float Client::getAnimationTime()
 {
-       //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
-       return m_env.getDayNightRatio();
+       return m_animation_time;
 }
 
-u16 Client::getHP()
+int Client::getCrackLevel()
 {
-       Player *player = m_env.getLocalPlayer();
-       assert(player != NULL);
-       return player->hp;
+       return m_crack_level;
 }
 
-void Client::setTempMod(v3s16 p, NodeMod mod)
+void Client::setCrack(int level, v3s16 pos)
 {
-       //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
-       assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
+       int old_crack_level = m_crack_level;
+       v3s16 old_crack_pos = m_crack_pos;
 
-       core::map<v3s16, MapBlock*> affected_blocks;
-       ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
-                       &affected_blocks);
+       m_crack_level = level;
+       m_crack_pos = pos;
 
-       for(core::map<v3s16, MapBlock*>::Iterator
-                       i = affected_blocks.getIterator();
-                       i.atEnd() == false; i++)
+       if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
+       {
+               // remove old crack
+               addUpdateMeshTaskForNode(old_crack_pos, false, true);
+       }
+       if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
        {
-               i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
+               // add new crack
+               addUpdateMeshTaskForNode(pos, false, true);
        }
 }
 
-void Client::clearTempMod(v3s16 p)
+u32 Client::getDayNightRatio()
 {
        //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
-       assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
-
-       core::map<v3s16, MapBlock*> affected_blocks;
-       ((ClientMap&)m_env.getMap()).clearTempMod(p,
-                       &affected_blocks);
+       return m_env.getDayNightRatio();
+}
 
-       for(core::map<v3s16, MapBlock*>::Iterator
-                       i = affected_blocks.getIterator();
-                       i.atEnd() == false; i++)
-       {
-               i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
-       }
+u16 Client::getHP()
+{
+       Player *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+       return player->hp;
 }
 
 bool Client::getChatMessage(std::wstring &message)
@@ -2131,10 +2152,12 @@ void Client::typeChatMessage(const std::wstring &message)
        }
 }
 
-void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
+void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
 {
        /*infostream<<"Client::addUpdateMeshTask(): "
                        <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
+                       <<" ack_to_server="<<ack_to_server
+                       <<" urgent="<<urgent
                        <<std::endl;*/
 
        MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
@@ -2145,45 +2168,29 @@ void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
                Create a task to update the mesh of the block
        */
        
-       MeshMakeData *data = new MeshMakeData;
+       MeshMakeData *data = new MeshMakeData(this);
        
        {
                //TimeTaker timer("data fill");
                // Release: ~0ms
                // Debug: 1-6ms, avg=2ms
-               data->fill(getDayNightRatio(), b);
+               data->fill(b);
+               data->setCrack(m_crack_level, m_crack_pos);
+               data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
        }
 
        // Debug wait
        //while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
        
        // Add task to queue
-       m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
+       m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
 
        /*infostream<<"Mesh update input queue size is "
                        <<m_mesh_update_thread.m_queue_in.size()
                        <<std::endl;*/
-       
-#if 0
-       // Temporary test: make mesh directly in here
-       {
-               //TimeTaker timer("make mesh");
-               // 10ms
-               scene::SMesh *mesh_new = NULL;
-               mesh_new = makeMapBlockMesh(data);
-               b->replaceMesh(mesh_new);
-               delete data;
-       }
-#endif
-
-       /*
-               Mark mesh as non-expired at this point so that it can already
-               be marked as expired again if the data changes
-       */
-       b->setMeshExpired(false);
 }
 
-void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
+void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
 {
        /*{
                v3s16 p = blockpos;
@@ -2195,25 +2202,66 @@ void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
        try{
                v3s16 p = blockpos + v3s16(0,0,0);
                //MapBlock *b = m_env.getMap().getBlockNoCreate(p);
-               addUpdateMeshTask(p, ack_to_server);
+               addUpdateMeshTask(p, ack_to_server, urgent);
        }
        catch(InvalidPositionException &e){}
        // Leading edge
        try{
                v3s16 p = blockpos + v3s16(-1,0,0);
-               addUpdateMeshTask(p);
+               addUpdateMeshTask(p, false, urgent);
        }
        catch(InvalidPositionException &e){}
        try{
                v3s16 p = blockpos + v3s16(0,-1,0);
-               addUpdateMeshTask(p);
+               addUpdateMeshTask(p, false, urgent);
        }
        catch(InvalidPositionException &e){}
        try{
                v3s16 p = blockpos + v3s16(0,0,-1);
-               addUpdateMeshTask(p);
+               addUpdateMeshTask(p, false, urgent);
+       }
+       catch(InvalidPositionException &e){}
+}
+
+void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
+{
+       {
+               v3s16 p = nodepos;
+               infostream<<"Client::addUpdateMeshTaskForNode(): "
+                               <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
+                               <<std::endl;
+       }
+
+       v3s16 blockpos = getNodeBlockPos(nodepos);
+       v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
+
+       try{
+               v3s16 p = blockpos + v3s16(0,0,0);
+               addUpdateMeshTask(p, ack_to_server, urgent);
        }
        catch(InvalidPositionException &e){}
+       // Leading edge
+       if(nodepos.X == blockpos_relative.X){
+               try{
+                       v3s16 p = blockpos + v3s16(-1,0,0);
+                       addUpdateMeshTask(p, false, urgent);
+               }
+               catch(InvalidPositionException &e){}
+       }
+       if(nodepos.Y == blockpos_relative.Y){
+               try{
+                       v3s16 p = blockpos + v3s16(0,-1,0);
+                       addUpdateMeshTask(p, false, urgent);
+               }
+               catch(InvalidPositionException &e){}
+       }
+       if(nodepos.Z == blockpos_relative.Z){
+               try{
+                       v3s16 p = blockpos + v3s16(0,0,-1);
+                       addUpdateMeshTask(p, false, urgent);
+               }
+               catch(InvalidPositionException &e){}
+       }
 }
 
 ClientEvent Client::getClientEvent()
index 4b16b717caf7e8ce26030c4fb30b05b9daec71da..ea3776895630a191b2fad1b605445501355a4450 100644 (file)
@@ -27,6 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "common_irrlicht.h"
 #include "jmutex.h"
 #include <ostream>
+#include <set>
+#include <vector>
 #include "clientobject.h"
 #include "utility.h" // For IntervalLimiter
 #include "gamedef.h"
@@ -34,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "filesys.h"
 
 struct MeshMakeData;
+class MapBlockMesh;
 class IGameDef;
 class IWritableTextureSource;
 class IWritableItemDefManager;
@@ -71,7 +74,8 @@ class MeshUpdateQueue
        /*
                peer_id=0 adds with nobody to send to
        */
-       void addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server);
+       void addBlock(v3s16 p, MeshMakeData *data,
+                       bool ack_block_to_server, bool urgent);
 
        // Returned pointer must be deleted
        // Returns NULL if queue is empty
@@ -84,14 +88,15 @@ class MeshUpdateQueue
        }
        
 private:
-       core::list<QueuedMeshUpdate*> m_queue;
+       std::vector<QueuedMeshUpdate*> m_queue;
+       std::set<v3s16> m_urgents;
        JMutex m_mutex;
 };
 
 struct MeshUpdateResult
 {
        v3s16 p;
-       scene::SMesh *mesh;
+       MapBlockMesh *mesh;
        bool ack_block_to_server;
 
        MeshUpdateResult():
@@ -260,13 +265,15 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
 
        core::list<std::wstring> getConnectedPlayerNames();
 
+       float getAnimationTime();
+
+       int getCrackLevel();
+       void setCrack(int level, v3s16 pos);
+
        u32 getDayNightRatio();
 
        u16 getHP();
 
-       void setTempMod(v3s16 p, NodeMod mod);
-       void clearTempMod(v3s16 p);
-
        float getAvgRtt()
        {
                try{
@@ -281,9 +288,10 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
 
        u64 getMapSeed(){ return m_map_seed; }
 
-       void addUpdateMeshTask(v3s16 blockpos, bool ack_to_server=false);
+       void addUpdateMeshTask(v3s16 blockpos, bool ack_to_server=false, bool urgent=false);
        // Including blocks at appropriate edges
-       void addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server=false);
+       void addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server=false, bool urgent=false);
+       void addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server=false, bool urgent=false);
 
        // Get event from queue. CE_NONE is returned if queue is empty.
        ClientEvent getClientEvent();
@@ -352,6 +360,10 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
        float m_inventory_from_server_age;
        core::map<v3s16, bool> m_active_blocks;
        PacketCounter m_packetcounter;
+       // Block mesh animation parameters
+       float m_animation_time;
+       int m_crack_level;
+       v3s16 m_crack_pos;
        // Received from the server. 0-23999
        u32 m_time_of_day;
        // 0 <= m_daynight_i < DAYNIGHT_CACHE_COUNT
index 402c4e8b4ecfd9e3b5cd54d8ae9456b4b9695a3f..9a427a44ee1385403561e13e6b4e5f74ac85e49b 100644 (file)
@@ -29,9 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 // Create a cuboid.
 //  collector - the MeshCollector for the resulting polygons
 //  box       - the position and size of the box
-//  materials - the materials to use (for all 6 faces)
-//  pa        - texture atlas pointers for the materials
-//  matcount  - number of entries in "materials" and "pa", 1<=matcount<=6
+//  tiles     - the tiles (materials) to use (for all 6 faces)
+//  tilecount - number of entries in tiles, 1<=tilecount<=6
 //  c         - vertex colour - used for all
 //  txc       - texture coordinates - this is a list of texture coordinates
 //              for the opposite corners of each face - therefore, there
@@ -41,10 +40,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 //              (compatible with ContentFeatures). If you specified 0,0,1,1
 //              for each face, that would be the same as passing NULL.
 void makeCuboid(MeshCollector *collector, const aabb3f &box,
-       const video::SMaterial *materials, const AtlasPointer *pa, int matcount,        
+       const TileSpec *tiles, int tilecount,
        video::SColor &c, const f32* txc)
 {
-       assert(matcount >= 1);
+       assert(tilecount >= 1 && tilecount <= 6);
 
        v3f min = box.MinEdge;
        v3f max = box.MaxEdge;
@@ -98,9 +97,9 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box,
 
        for(s32 j=0; j<24; j++)
        {
-               int matindex = MYMIN(j/4, matcount-1);
-               vertices[j].TCoords *= pa[matindex].size;
-               vertices[j].TCoords += pa[matindex].pos;
+               int tileindex = MYMIN(j/4, tilecount-1);
+               vertices[j].TCoords *= tiles[tileindex].texture.size;
+               vertices[j].TCoords += tiles[tileindex].texture.pos;
        }
 
        u16 indices[] = {0,1,2,2,3,0};
@@ -108,17 +107,16 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box,
        // Add to mesh collector
        for(s32 j=0; j<24; j+=4)
        {
-               int matindex = MYMIN(j/4, matcount-1);
-               collector->append(materials[matindex],
+               int tileindex = MYMIN(j/4, tilecount-1);
+               collector->append(tiles[tileindex],
                                vertices+j, 4, indices, 6);
        }
 }
 
 void mapblock_mesh_generate_special(MeshMakeData *data,
-               MeshCollector &collector, IGameDef *gamedef)
+               MeshCollector &collector)
 {
-       INodeDefManager *nodedef = gamedef->ndef();
-       ITextureSource *tsrc = gamedef->getTextureSource();
+       INodeDefManager *nodedef = data->m_gamedef->ndef();
 
        // 0ms
        //TimeTaker timer("mapblock_mesh_generate_special()");
@@ -134,14 +132,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
        
        v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
 
-       /*// General ground material for special output
-       // Texture is modified just before usage
-       video::SMaterial material_general;
-       material_general.setFlag(video::EMF_LIGHTING, false);
-       material_general.setFlag(video::EMF_BILINEAR_FILTER, false);
-       material_general.setFlag(video::EMF_FOG_ENABLE, true);
-       material_general.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;*/
-
        for(s16 z=0; z<MAP_BLOCKSIZE; z++)
        for(s16 y=0; y<MAP_BLOCKSIZE; y++)
        for(s16 x=0; x<MAP_BLOCKSIZE; x++)
@@ -167,16 +157,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                        /*
                                Add water sources to mesh if using new style
                        */
-                       assert(nodedef->get(n).special_materials[0]);
-                       //assert(nodedef->get(n).special_materials[1]);
-                       assert(nodedef->get(n).special_aps[0]);
-
-                       video::SMaterial &liquid_material =
-                                       *nodedef->get(n).special_materials[0];
-                       /*video::SMaterial &liquid_material_bfculled =
-                                       *nodedef->get(n).special_materials[1];*/
-                       AtlasPointer &pa_liquid1 =
-                                       *nodedef->get(n).special_aps[0];
+                       TileSpec tile_liquid = f.special_tiles[0];
+                       AtlasPointer &pa_liquid = tile_liquid.texture;
 
                        bool top_is_air = false;
                        MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
@@ -186,64 +168,55 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                        if(top_is_air == false)
                                continue;
 
-                       u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
-                       video::SColor c = MapBlock_LightColor(
-                                       nodedef->get(n).alpha, l);
+                       u16 l = getInteriorLight(n, 0, data);
+                       video::SColor c = MapBlock_LightColor(f.alpha, l);
                        
                        video::S3DVertex vertices[4] =
                        {
                                video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
-                                               pa_liquid1.x0(), pa_liquid1.y1()),
+                                               pa_liquid.x0(), pa_liquid.y1()),
                                video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
-                                               pa_liquid1.x1(), pa_liquid1.y1()),
+                                               pa_liquid.x1(), pa_liquid.y1()),
                                video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
-                                               pa_liquid1.x1(), pa_liquid1.y0()),
+                                               pa_liquid.x1(), pa_liquid.y0()),
                                video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
-                                               pa_liquid1.x0(), pa_liquid1.y0()),
+                                               pa_liquid.x0(), pa_liquid.y0()),
                        };
 
+                       v3f offset(p.X, p.Y + (-0.5+node_liquid_level)*BS, p.Z);
                        for(s32 i=0; i<4; i++)
                        {
-                               vertices[i].Pos.Y += (-0.5+node_liquid_level)*BS;
-                               vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+                               vertices[i].Pos += offset;
                        }
 
                        u16 indices[] = {0,1,2,2,3,0};
                        // Add to mesh collector
-                       collector.append(liquid_material, vertices, 4, indices, 6);
+                       collector.append(tile_liquid, vertices, 4, indices, 6);
                break;}
                case NDT_FLOWINGLIQUID:
                {
                        /*
                                Add flowing liquid to mesh
                        */
-                       assert(nodedef->get(n).special_materials[0]);
-                       assert(nodedef->get(n).special_materials[1]);
-                       assert(nodedef->get(n).special_aps[0]);
-
-                       video::SMaterial &liquid_material =
-                                       *nodedef->get(n).special_materials[0];
-                       video::SMaterial &liquid_material_bfculled =
-                                       *nodedef->get(n).special_materials[1];
-                       AtlasPointer &pa_liquid1 =
-                                       *nodedef->get(n).special_aps[0];
+                       TileSpec tile_liquid = f.special_tiles[0];
+                       TileSpec tile_liquid_bfculled = f.special_tiles[1];
+                       AtlasPointer &pa_liquid = tile_liquid.texture;
 
                        bool top_is_same_liquid = false;
                        MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
-                       content_t c_flowing = nodedef->getId(nodedef->get(n).liquid_alternative_flowing);
-                       content_t c_source = nodedef->getId(nodedef->get(n).liquid_alternative_source);
+                       content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
+                       content_t c_source = nodedef->getId(f.liquid_alternative_source);
                        if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
                                top_is_same_liquid = true;
                        
-                       u8 l = 0;
+                       u16 l = 0;
                        // Use the light of the node on top if possible
                        if(nodedef->get(ntop).param_type == CPT_LIGHT)
-                               l = decode_light(ntop.getLightBlend(data->m_daynight_ratio, nodedef));
+                               l = getInteriorLight(ntop, 0, data);
                        // Otherwise use the light of this node (the liquid)
                        else
-                               l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
-                       video::SColor c = MapBlock_LightColor(
-                                       nodedef->get(n).alpha, l);
+                               l = getInteriorLight(n, 0, data);
+                       video::SColor c = MapBlock_LightColor(f.alpha, l);
                        
                        // Neighbor liquid levels (key = relative position)
                        // Includes current node
@@ -393,20 +366,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 
                                // Use backface culled material if neighbor doesn't have a
                                // solidness of 0
-                               video::SMaterial *current_material = &liquid_material;
+                               const TileSpec *current_tile = &tile_liquid;
                                if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
-                                       current_material = &liquid_material_bfculled;
+                                       current_tile = &tile_liquid_bfculled;
                                
                                video::S3DVertex vertices[4] =
                                {
                                        video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
-                                                       pa_liquid1.x0(), pa_liquid1.y1()),
+                                                       pa_liquid.x0(), pa_liquid.y1()),
                                        video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
-                                                       pa_liquid1.x1(), pa_liquid1.y1()),
+                                                       pa_liquid.x1(), pa_liquid.y1()),
                                        video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
-                                                       pa_liquid1.x1(), pa_liquid1.y0()),
+                                                       pa_liquid.x1(), pa_liquid.y0()),
                                        video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
-                                                       pa_liquid1.x0(), pa_liquid1.y0()),
+                                                       pa_liquid.x0(), pa_liquid.y0()),
                                };
                                
                                /*
@@ -464,12 +437,12 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                                vertices[j].Pos.Z *= 0.98;
                                        }*/
 
-                                       vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
+                                       vertices[j].Pos += intToFloat(p, BS);
                                }
 
                                u16 indices[] = {0,1,2,2,3,0};
                                // Add to mesh collector
-                               collector.append(*current_material, vertices, 4, indices, 6);
+                               collector.append(*current_tile, vertices, 4, indices, 6);
                        }
                        
                        /*
@@ -481,13 +454,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                video::S3DVertex vertices[4] =
                                {
                                        video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
-                                                       pa_liquid1.x0(), pa_liquid1.y1()),
+                                                       pa_liquid.x0(), pa_liquid.y1()),
                                        video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
-                                                       pa_liquid1.x1(), pa_liquid1.y1()),
+                                                       pa_liquid.x1(), pa_liquid.y1()),
                                        video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
-                                                       pa_liquid1.x1(), pa_liquid1.y0()),
+                                                       pa_liquid.x1(), pa_liquid.y0()),
                                        video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
-                                                       pa_liquid1.x0(), pa_liquid1.y0()),
+                                                       pa_liquid.x0(), pa_liquid.y0()),
                                };
                                
                                // This fixes a strange bug
@@ -499,27 +472,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                        //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
                                        s32 j = corner_resolve[i];
                                        vertices[i].Pos.Y += corner_levels[j];
-                                       vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+                                       vertices[i].Pos += intToFloat(p, BS);
                                }
 
                                u16 indices[] = {0,1,2,2,3,0};
                                // Add to mesh collector
-                               collector.append(liquid_material, vertices, 4, indices, 6);
+                               collector.append(tile_liquid, vertices, 4, indices, 6);
                        }
                break;}
                case NDT_GLASSLIKE:
                {
-                       video::SMaterial material_glass;
-                       material_glass.setFlag(video::EMF_LIGHTING, false);
-                       material_glass.setFlag(video::EMF_BILINEAR_FILTER, false);
-                       material_glass.setFlag(video::EMF_FOG_ENABLE, true);
-                       material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       TileSpec tile_glass = getNodeTile(n, p, v3s16(0,0,0),
-                                       &data->m_temp_mods, tsrc, nodedef);
-                       AtlasPointer pa_glass = tile_glass.texture;
-                       material_glass.setTexture(0, pa_glass.atlas);
-
-                       u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
+                       TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
+                       AtlasPointer ap = tile.texture;
+
+                       u16 l = getInteriorLight(n, 1, data);
                        video::SColor c = MapBlock_LightColor(255, l);
 
                        for(u32 j=0; j<6; j++)
@@ -535,13 +501,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                video::S3DVertex vertices[4] =
                                {
                                        video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
-                                               pa_glass.x0(), pa_glass.y1()),
+                                               ap.x0(), ap.y1()),
                                        video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
-                                               pa_glass.x1(), pa_glass.y1()),
+                                               ap.x1(), ap.y1()),
                                        video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
-                                               pa_glass.x1(), pa_glass.y0()),
+                                               ap.x1(), ap.y0()),
                                        video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
-                                               pa_glass.x0(), pa_glass.y0()),
+                                               ap.x0(), ap.y0()),
                                };
                                
                                // Rotations in the g_6dirs format
@@ -565,36 +531,28 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                                vertices[i].Pos.rotateXZBy(90);
 
                                for(u16 i=0; i<4; i++){
-                                       vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+                                       vertices[i].Pos += intToFloat(p, BS);
                                }
 
                                u16 indices[] = {0,1,2,2,3,0};
                                // Add to mesh collector
-                               collector.append(material_glass, vertices, 4, indices, 6);
+                               collector.append(tile, vertices, 4, indices, 6);
                        }
                break;}
                case NDT_ALLFACES:
                {
-                       video::SMaterial material_leaves1;
-                       material_leaves1.setFlag(video::EMF_LIGHTING, false);
-                       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;
-                       TileSpec tile_leaves1 = getNodeTile(n, p, v3s16(0,0,0),
-                                       &data->m_temp_mods, tsrc, nodedef);
-                       AtlasPointer pa_leaves1 = tile_leaves1.texture;
-                       material_leaves1.setTexture(0, pa_leaves1.atlas);
-
-                       u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
+                       TileSpec tile_leaves = getNodeTile(n, p,
+                                       v3s16(0,0,0), data);
+                       AtlasPointer pa_leaves = tile_leaves.texture;
+
+                       u16 l = getInteriorLight(n, 1, data);
                        video::SColor c = MapBlock_LightColor(255, l);
 
-                       v3f pos = intToFloat(p+blockpos_nodes, BS);
+                       v3f pos = intToFloat(p, BS);
                        aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2);
                        box.MinEdge += pos;
                        box.MaxEdge += pos;
-                       makeCuboid(&collector, box,
-                                       &material_leaves1, &pa_leaves1, 1,
-                                       c, NULL);
+                       makeCuboid(&collector, box, &tile_leaves, 1, c, NULL);
                break;}
                case NDT_ALLFACES_OPTIONAL:
                        // This is always pre-converted to something else
@@ -604,28 +562,23 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                {
                        v3s16 dir = n.getWallMountedDir(nodedef);
                        
-                       AtlasPointer ap(0);
+                       u8 tileindex = 0;
                        if(dir == v3s16(0,-1,0)){
-                               ap = f.tiles[0].texture; // floor
+                               tileindex = 0; // floor
                        } else if(dir == v3s16(0,1,0)){
-                               ap = f.tiles[1].texture; // ceiling
+                               tileindex = 1; // ceiling
                        // For backwards compatibility
                        } else if(dir == v3s16(0,0,0)){
-                               ap = f.tiles[0].texture; // floor
+                               tileindex = 0; // floor
                        } else {
-                               ap = f.tiles[2].texture; // side
+                               tileindex = 2; // side
                        }
 
-                       // Set material
-                       video::SMaterial material;
-                       material.setFlag(video::EMF_LIGHTING, false);
-                       material.setFlag(video::EMF_BACK_FACE_CULLING, false);
-                       material.setFlag(video::EMF_BILINEAR_FILTER, false);
-                       material.setFlag(video::EMF_FOG_ENABLE, true);
-                       //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
-                       material.MaterialType
-                                       = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       material.setTexture(0, ap.atlas);
+                       TileSpec tile = getNodeTileN(n, p, tileindex, data);
+                       tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
+                       tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
+
+                       AtlasPointer ap = tile.texture;
 
                        video::SColor c(255,255,255,255);
 
@@ -657,27 +610,21 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                if(dir == v3s16(0,1,0))
                                        vertices[i].Pos.rotateXZBy(-45);
 
-                               vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+                               vertices[i].Pos += intToFloat(p, BS);
                        }
 
                        u16 indices[] = {0,1,2,2,3,0};
                        // Add to mesh collector
-                       collector.append(material, vertices, 4, indices, 6);
+                       collector.append(tile, vertices, 4, indices, 6);
                break;}
                case NDT_SIGNLIKE:
                {
-                       // Set material
-                       video::SMaterial material;
-                       material.setFlag(video::EMF_LIGHTING, false);
-                       material.setFlag(video::EMF_BACK_FACE_CULLING, false);
-                       material.setFlag(video::EMF_BILINEAR_FILTER, false);
-                       material.setFlag(video::EMF_FOG_ENABLE, true);
-                       material.MaterialType
-                                       = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       AtlasPointer ap = f.tiles[0].texture;
-                       material.setTexture(0, ap.atlas);
-
-                       u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
+                       TileSpec tile = getNodeTileN(n, p, 0, data);
+                       tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
+                       tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
+                       AtlasPointer ap = tile.texture;
+
+                       u16 l = getInteriorLight(n, 0, data);
                        video::SColor c = MapBlock_LightColor(255, l);
                                
                        float d = (float)BS/16;
@@ -711,24 +658,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                if(dir == v3s16(0,1,0))
                                        vertices[i].Pos.rotateXYBy(90);
 
-                               vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+                               vertices[i].Pos += intToFloat(p, BS);
                        }
 
                        u16 indices[] = {0,1,2,2,3,0};
                        // Add to mesh collector
-                       collector.append(material, vertices, 4, indices, 6);
+                       collector.append(tile, vertices, 4, indices, 6);
                break;}
                case NDT_PLANTLIKE:
                {
-                       video::SMaterial material_papyrus;
-                       material_papyrus.setFlag(video::EMF_LIGHTING, false);
-                       material_papyrus.setFlag(video::EMF_BILINEAR_FILTER, false);
-                       material_papyrus.setFlag(video::EMF_FOG_ENABLE, true);
-                       material_papyrus.MaterialType=video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       AtlasPointer pa_papyrus = f.tiles[0].texture;
-                       material_papyrus.setTexture(0, pa_papyrus.atlas);
+                       TileSpec tile = getNodeTileN(n, p, 0, data);
+                       tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
+                       AtlasPointer ap = tile.texture;
                        
-                       u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
+                       u16 l = getInteriorLight(n, 1, data);
                        video::SColor c = MapBlock_LightColor(255, l);
 
                        for(u32 j=0; j<4; j++)
@@ -736,15 +679,15 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                video::S3DVertex vertices[4] =
                                {
                                        video::S3DVertex(-BS/2*f.visual_scale,-BS/2,0, 0,0,0, c,
-                                               pa_papyrus.x0(), pa_papyrus.y1()),
+                                               ap.x0(), ap.y1()),
                                        video::S3DVertex( BS/2*f.visual_scale,-BS/2,0, 0,0,0, c,
-                                               pa_papyrus.x1(), pa_papyrus.y1()),
+                                               ap.x1(), ap.y1()),
                                        video::S3DVertex( BS/2*f.visual_scale,
                                                -BS/2 + f.visual_scale*BS,0, 0,0,0, c,
-                                               pa_papyrus.x1(), pa_papyrus.y0()),
+                                               ap.x1(), ap.y0()),
                                        video::S3DVertex(-BS/2*f.visual_scale,
                                                -BS/2 + f.visual_scale*BS,0, 0,0,0, c,
-                                               pa_papyrus.x0(), pa_papyrus.y0()),
+                                               ap.x0(), ap.y0()),
                                };
 
                                if(j == 0)
@@ -771,45 +714,28 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                for(u16 i=0; i<4; i++)
                                {
                                        vertices[i].Pos *= f.visual_scale;
-                                       vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+                                       vertices[i].Pos += intToFloat(p, BS);
                                }
 
                                u16 indices[] = {0,1,2,2,3,0};
                                // Add to mesh collector
-                               collector.append(material_papyrus, vertices, 4, indices, 6);
+                               collector.append(tile, vertices, 4, indices, 6);
                        }
                break;}
                case NDT_FENCELIKE:
                {
-                       video::SMaterial material_wood;
-                       material_wood.setFlag(video::EMF_LIGHTING, false);
-                       material_wood.setFlag(video::EMF_BILINEAR_FILTER, false);
-                       material_wood.setFlag(video::EMF_FOG_ENABLE, true);
-                       material_wood.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       TileSpec tile_wood = getNodeTile(n, p, v3s16(0,0,0),
-                                       &data->m_temp_mods, tsrc, nodedef);
-                       AtlasPointer pa_wood = tile_wood.texture;
-                       material_wood.setTexture(0, pa_wood.atlas);
-
-                       video::SMaterial material_wood_nomod;
-                       material_wood_nomod.setFlag(video::EMF_LIGHTING, false);
-                       material_wood_nomod.setFlag(video::EMF_BILINEAR_FILTER, false);
-                       material_wood_nomod.setFlag(video::EMF_FOG_ENABLE, true);
-                       material_wood_nomod.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-
-                       TileSpec tile_wood_nomod = getNodeTile(n, p, v3s16(0,0,0),
-                                       NULL, tsrc, nodedef);
-                       AtlasPointer pa_wood_nomod = tile_wood_nomod.texture;
-                       material_wood_nomod.setTexture(0, pa_wood_nomod.atlas);
-
-                       u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
+                       TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
+                       TileSpec tile_nocrack = tile;
+                       tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
+
+                       u16 l = getInteriorLight(n, 1, data);
                        video::SColor c = MapBlock_LightColor(255, l);
 
                        const f32 post_rad=(f32)BS/10;
                        const f32 bar_rad=(f32)BS/20;
                        const f32 bar_len=(f32)(BS/2)-post_rad;
 
-                       v3f pos = intToFloat(p+blockpos_nodes, BS);
+                       v3f pos = intToFloat(p, BS);
 
                        // The post - always present
                        aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad);
@@ -822,8 +748,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                        0.35,0,0.65,1,
                                        0.35,0,0.65,1,
                                        0.35,0,0.65,1};
-                       makeCuboid(&collector, post, &material_wood,
-                                       &pa_wood, 1, c, postuv);
+                       makeCuboid(&collector, post, &tile, 1, c, postuv);
 
                        // Now a section of fence, +X, if there's a post there
                        v3s16 p2 = p;
@@ -843,12 +768,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                        0,0.4,1,0.6,
                                        0,0.4,1,0.6,
                                        0,0.4,1,0.6};
-                               makeCuboid(&collector, bar, &material_wood_nomod,
-                                               &pa_wood_nomod, 1, c, xrailuv);
+                               makeCuboid(&collector, bar, &tile_nocrack, 1,
+                                               c, xrailuv);
                                bar.MinEdge.Y -= BS/2;
                                bar.MaxEdge.Y -= BS/2;
-                               makeCuboid(&collector, bar, &material_wood_nomod,
-                                               &pa_wood_nomod, 1, c, xrailuv);
+                               // TODO: no crack
+                               makeCuboid(&collector, bar, &tile_nocrack, 1,
+                                               c, xrailuv);
                        }
 
                        // Now a section of fence, +Z, if there's a post there
@@ -870,12 +796,12 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                                        0,0.4,1,0.6,
                                        0,0.4,1,0.6};
 
-                               makeCuboid(&collector, bar, &material_wood_nomod,
-                                               &pa_wood_nomod, 1, c, zrailuv);
+                               makeCuboid(&collector, bar, &tile_nocrack, 1,
+                                               c, zrailuv);
                                bar.MinEdge.Y -= BS/2;
                                bar.MaxEdge.Y -= BS/2;
-                               makeCuboid(&collector, bar, &material_wood_nomod,
-                                               &pa_wood_nomod, 1, c, zrailuv);
+                               makeCuboid(&collector, bar, &tile_nocrack, 1,
+                                               c, zrailuv);
                        }
                break;}
                case NDT_RAILLIKE:
@@ -948,31 +874,28 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
                        }
 
                        // Assign textures
-                       AtlasPointer ap = f.tiles[0].texture; // straight
+                       u8 tileindex = 0; // straight
                        if(adjacencies < 2)
-                               ap = f.tiles[0].texture; // straight
+                               tileindex = 0; // straight
                        else if(adjacencies == 2)
                        {
                                if(is_straight)
-                                       ap = f.tiles[0].texture; // straight
+                                       tileindex = 0; // straight
                                else
-                                       ap = f.tiles[1].texture; // curved
+                                       tileindex = 1; // curved
                        }
                        else if(adjacencies == 3)
-                               ap = f.tiles[2].texture; // t-junction
+                               tileindex = 2; // t-junction
                        else if(adjacencies == 4)
-                               ap = f.tiles[3].texture; // crossing
+                               tileindex = 3; // crossing
+
+                       TileSpec tile = getNodeTileN(n, p, tileindex, data);
+                       tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
+                       tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
+
+                       AtlasPointer ap = tile.texture;
                        
-                       video::SMaterial material_rail;
-                       material_rail.setFlag(video::EMF_LIGHTING, false);
-                       material_rail.setFlag(video::EMF_BACK_FACE_CULLING, false);
-                       material_rail.setFlag(video::EMF_BILINEAR_FILTER, false);
-                       material_rail.setFlag(video::EMF_FOG_ENABLE, true);
-                       material_rail.MaterialType
-                                       = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       material_rail.setTexture(0, ap.atlas);
-
-                       u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
+                       u16 l = getInteriorLight(n, 0, data);
                        video::SColor c = MapBlock_LightColor(255, l);
 
                        float d = (float)BS/64;
@@ -1048,11 +971,11 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
 
                        for(s32 i=0; i<4; i++)
                        {
-                               vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
+                               vertices[i].Pos += intToFloat(p, BS);
                        }
 
                        u16 indices[] = {0,1,2,2,3,0};
-                       collector.append(material_rail, vertices, 4, indices, 6);
+                       collector.append(tile, vertices, 4, indices, 6);
                break;}
                }
        }
index eaf74b142623fc65c6e1668725de50106a3ef89f..af25191b4b917694db4500aa35f4cb3e65bf6069 100644 (file)
@@ -20,13 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #ifndef CONTENT_MAPBLOCK_HEADER
 #define CONTENT_MAPBLOCK_HEADER
 
-#ifndef SERVER
-       #include "mapblock_mesh.h"
-       #include "utility.h"
-class IGameDef;
+struct MeshMakeData;
+struct MeshCollector;
 void mapblock_mesh_generate_special(MeshMakeData *data,
-               MeshCollector &collector, IGameDef *gamedef);
-#endif
+               MeshCollector &collector);
 
 #endif
 
index cfe82fb8b537a7a82f5719060e40bc3fa72a0292..c3ff7b75a8160f1d96955c4185561021b2982432 100644 (file)
@@ -890,9 +890,6 @@ void ServerEnvironment::step(float dtime)
        
        //TimeTaker timer("ServerEnv step");
 
-       // Get some settings
-       bool footprints = g_settings->getBool("footprints");
-
        /*
                Increment game time
        */
@@ -921,26 +918,6 @@ void ServerEnvironment::step(float dtime)
                        
                        // Move
                        player->move(dtime, *m_map, 100*BS);
-                       
-                       /*
-                               Add footsteps to grass
-                       */
-                       if(footprints)
-                       {
-                               // Get node that is at BS/4 under player
-                               v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
-                               try{
-                                       MapNode n = m_map->getNode(bottompos);
-                                       if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
-                                       {
-                                               n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
-                                               m_map->setNode(bottompos, n);
-                                       }
-                               }
-                               catch(InvalidPositionException &e)
-                               {
-                               }
-                       }
                }
        }
 
@@ -1873,7 +1850,6 @@ void ClientEnvironment::step(float dtime)
 
        // Get some settings
        bool free_move = g_settings->getBool("free_move");
-       bool footprints = g_settings->getBool("footprints");
 
        // Get local player
        LocalPlayer *lplayer = getLocalPlayer();
@@ -2058,34 +2034,6 @@ void ClientEnvironment::step(float dtime)
                        light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
                }
                player->updateLight(light);
-
-               /*
-                       Add footsteps to grass
-               */
-               if(footprints)
-               {
-                       // Get node that is at BS/4 under player
-                       v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
-                       try{
-                               MapNode n = m_map->getNode(bottompos);
-                               if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
-                               {
-                                       n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
-                                       m_map->setNode(bottompos, n);
-                                       // Update mesh on client
-                                       if(m_map->mapType() == MAPTYPE_CLIENT)
-                                       {
-                                               v3s16 p_blocks = getNodeBlockPos(bottompos);
-                                               MapBlock *b = m_map->getBlockNoCreate(p_blocks);
-                                               //b->updateMesh(getDayNightRatio());
-                                               b->setMeshExpired(true);
-                                       }
-                               }
-                       }
-                       catch(InvalidPositionException &e)
-                       {
-                       }
-               }
        }
        
        /*
@@ -2133,16 +2081,6 @@ void ClientEnvironment::step(float dtime)
                }
        }
 }
-
-void ClientEnvironment::updateMeshes(v3s16 blockpos)
-{
-       m_map->updateMeshes(blockpos, getDayNightRatio());
-}
-
-void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
-{
-       m_map->expireMeshes(only_daynight_diffed);
-}
        
 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
 {
index fcd16e2967bebd271303940541af8894a08f991f..65495fc85088607b0f51d7d8c2a5892e5e65b02a 100644 (file)
@@ -407,24 +407,6 @@ class ClientEnvironment : public Environment
        virtual void addPlayer(Player *player);
        LocalPlayer * getLocalPlayer();
        
-       // Slightly deprecated
-       void updateMeshes(v3s16 blockpos);
-       void expireMeshes(bool only_daynight_diffed);
-
-       void setTimeOfDay(u32 time)
-       {
-               u32 old_dr = getDayNightRatio();
-
-               Environment::setTimeOfDay(time);
-
-               if(getDayNightRatio() != old_dr)
-               {
-                       /*infostream<<"ClientEnvironment: DayNightRatio changed"
-                                       <<" -> expiring meshes"<<std::endl;*/
-                       expireMeshes(true);
-               }
-       }
-
        /*
                ClientSimpleObjects
        */
index 90856effc799c7fb62cea48056f728f066c8e577..b1cc0bfe55648159f3845d109184a9844c11e30d 100644 (file)
@@ -1038,8 +1038,6 @@ void the_game(
        float object_hit_delay_timer = 0.0;
        float time_from_last_punch = 10;
 
-       float crack_update_timer = 0.0;
-       
        bool invert_mouse = g_settings->getBool("invert_mouse");
 
        bool respawn_menu_active = false;
@@ -1185,7 +1183,6 @@ void the_game(
                if(object_hit_delay_timer >= 0)
                        object_hit_delay_timer -= dtime;
                time_from_last_punch += dtime;
-               crack_update_timer += dtime;
 
                g_profiler->add("Elapsed time", dtime);
                g_profiler->avg("FPS", 1./dtime);
@@ -1908,7 +1905,7 @@ void the_game(
                        if(!digging)
                        {
                                client.interact(1, pointed_old);
-                               client.clearTempMod(pointed_old.node_undersurface);
+                               client.setCrack(-1, v3s16(0,0,0));
                                dig_time = 0.0;
                        }
                }
@@ -2003,20 +2000,15 @@ void the_game(
                                }
                                else if(dig_index < CRACK_ANIMATION_LENGTH)
                                {
-                                       // Limit crack update speed
-                                       if(crack_update_timer >= 0.1){
-                                               crack_update_timer = 0.0;
-                                               //infostream<<"dig_index="<<dig_index<<std::endl;
-                                               //TimeTaker timer("client.setTempMod");
-                                               client.setTempMod(nodepos,
-                                                               NodeMod(NODEMOD_CRACK, dig_index));
-                                       }
+                                       //TimeTaker timer("client.setTempMod");
+                                       //infostream<<"dig_index="<<dig_index<<std::endl;
+                                       client.setCrack(dig_index, nodepos);
                                }
                                else
                                {
                                        infostream<<"Digging completed"<<std::endl;
                                        client.interact(2, pointed);
-                                       client.clearTempMod(nodepos);
+                                       client.setCrack(-1, v3s16(0,0,0));
                                        client.removeNode(nodepos);
 
                                        dig_time = 0;
@@ -2171,8 +2163,6 @@ void the_game(
                u32 daynight_ratio = client.getDayNightRatio();
                u8 light8 = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
                brightness = (float)light8/255.0;
-               // Make night look good
-               brightness = brightness * 1.15 - 0.15;
                video::SColor bgcolor;
                if(brightness >= 0.2 && brightness < 0.7)
                        bgcolor = video::SColor(
index ace9c253fbcfd3ab942243c2776461f909cf613b..a646134af7aa41c8e95c1f5ed8b1968d49835f1f 100644 (file)
@@ -381,17 +381,20 @@ class CItemDefManager: public IWritableItemDefManager
                                content_t id = nodedef->getId(def->name);
                                const ContentFeatures &f = nodedef->get(id);
 
+                               u8 param1 = 0;
+                               if(f.param_type == CPT_LIGHT)
+                                       param1 = 0xee;
+
                                /*
                                        Make a mesh from the node
                                */
-                               MeshMakeData mesh_make_data;
-                               MapNode mesh_make_node(
-                                       id,
-                                       (f.param_type == CPT_LIGHT) ? 0xee : 0,
-                                       0);
-                               mesh_make_data.fillSingleNode(1000, &mesh_make_node);
-                               scene::IMesh *node_mesh =
-                                       makeMapBlockMesh(&mesh_make_data, gamedef);
+                               MeshMakeData mesh_make_data(gamedef);
+                               MapNode mesh_make_node(id, param1, 0);
+                               mesh_make_data.fillSingleNode(&mesh_make_node);
+                               MapBlockMesh mapblock_mesh(&mesh_make_data);
+
+                               scene::IMesh *node_mesh = mapblock_mesh.getMesh();
+                               assert(node_mesh);
                                setMeshColor(node_mesh, video::SColor(255, 255, 255, 255));
 
                                /*
@@ -404,7 +407,7 @@ class CItemDefManager: public IWritableItemDefManager
                                /*
                                        Draw node mesh into a render target texture
                                */
-                               if(def->inventory_texture == NULL && node_mesh != NULL)
+                               if(def->inventory_texture == NULL)
                                {
                                        core::dimension2d<u32> dim(64,64);
                                        std::string rtt_texture_name = "INVENTORY_"
@@ -443,7 +446,7 @@ class CItemDefManager: public IWritableItemDefManager
                                /*
                                        Use the node mesh as the wield mesh
                                */
-                               if(def->wield_mesh == NULL && node_mesh != NULL)
+                               if(def->wield_mesh == NULL)
                                {
                                        // Scale to proper wield mesh proportions
                                        scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0)
@@ -452,9 +455,7 @@ class CItemDefManager: public IWritableItemDefManager
                                        def->wield_mesh->grab();
                                }
 
-
-                               if(node_mesh != NULL)
-                                       node_mesh->drop();
+                               // falling outside of here deletes node_mesh
                        }
                }
 #endif
index e0175a4ab31dc0f4d7a3bfe5dfe25e406a3fec48..03a842e7425bb3ae9e4f3014ad79eb9f7f0d25ce 100644 (file)
@@ -21,23 +21,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapsector.h"
 #include "mapblock.h"
 #include "main.h"
-#ifndef SERVER
-#include "client.h"
-#endif
 #include "filesys.h"
 #include "utility.h"
 #include "voxel.h"
 #include "porting.h"
 #include "mapgen.h"
 #include "nodemetadata.h"
-#ifndef SERVER
-#include <IMaterialRenderer.h>
-#endif
 #include "settings.h"
 #include "log.h"
 #include "profiler.h"
 #include "nodedef.h"
 #include "gamedef.h"
+#ifndef SERVER
+#include "client.h"
+#include "mapblock_mesh.h"
+#include <IMaterialRenderer.h>
+#endif
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
@@ -3676,7 +3675,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
        */
        int time1 = time(0);
 
-       //u32 daynight_ratio = m_client->getDayNightRatio();
+       /*
+               Get animation parameters
+       */
+       float animation_time = m_client->getAnimationTime();
+       int crack = m_client->getCrackLevel();
+       u32 daynight_ratio = m_client->getDayNightRatio();
 
        m_camera_mutex.Lock();
        v3f camera_position = m_camera_position;
@@ -3709,8 +3713,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
        u32 vertex_count = 0;
        u32 meshbuffer_count = 0;
        
-       // For limiting number of mesh updates per frame
-       u32 mesh_update_count = 0;
+       // For limiting number of mesh animations per frame
+       u32 mesh_animate_count = 0;
+       u32 mesh_animate_count_far = 0;
        
        // Number of blocks in rendering range
        u32 blocks_in_range = 0;
@@ -3791,57 +3796,18 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
 
                        blocks_in_range++;
                        
-#if 1
                        /*
-                               Update expired mesh (used for day/night change)
-
-                               It doesn't work exactly like it should now with the
-                               tasked mesh update but whatever.
+                               Ignore if mesh doesn't exist
                        */
-
-                       bool mesh_expired = false;
-                       
                        {
-                               JMutexAutoLock lock(block->mesh_mutex);
-
-                               mesh_expired = block->getMeshExpired();
+                               //JMutexAutoLock lock(block->mesh_mutex);
 
-                               // Mesh has not been expired and there is no mesh:
-                               // block has no content
-                               if(block->mesh == NULL && mesh_expired == false){
+                               if(block->mesh == NULL){
                                        blocks_in_range_without_mesh++;
                                        continue;
                                }
                        }
 
-                       f32 faraway = BS*50;
-                       //f32 faraway = m_control.wanted_range * BS;
-                       
-                       /*
-                               This has to be done with the mesh_mutex unlocked
-                       */
-                       // Pretty random but this should work somewhat nicely
-                       if(mesh_expired && (
-                                       (mesh_update_count < 3
-                                               && (d < faraway || mesh_update_count < 2)
-                                       )
-                                       || 
-                                       (m_control.range_all && mesh_update_count < 20)
-                               )
-                       )
-                       /*if(mesh_expired && mesh_update_count < 6
-                                       && (d < faraway || mesh_update_count < 3))*/
-                       {
-                               mesh_update_count++;
-
-                               // Mesh has been expired: generate new mesh
-                               //block->updateMesh(daynight_ratio);
-                               m_client->addUpdateMeshTask(block->getPos());
-
-                               mesh_expired = false;
-                       }
-#endif
-
                        /*
                                Occlusion culling
                        */
@@ -3883,27 +3849,40 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                        // This block is in range. Reset usage timer.
                        block->resetUsageTimer();
 
-                       /*
-                               Ignore if mesh doesn't exist
-                       */
-                       {
-                               JMutexAutoLock lock(block->mesh_mutex);
-
-                               scene::SMesh *mesh = block->mesh;
-                               
-                               if(mesh == NULL){
-                                       blocks_in_range_without_mesh++;
-                                       continue;
-                               }
-                       }
-                       
                        // Limit block count in case of a sudden increase
                        blocks_would_have_drawn++;
                        if(blocks_drawn >= m_control.wanted_max_blocks
                                        && m_control.range_all == false
                                        && d > m_control.wanted_min_range * BS)
                                continue;
-                       
+
+                       // Mesh animation
+                       {
+                               //JMutexAutoLock lock(block->mesh_mutex);
+                               MapBlockMesh *mapBlockMesh = block->mesh;
+                               // Pretty random but this should work somewhat nicely
+                               bool faraway = d >= BS*50;
+                               //bool faraway = d >= m_control.wanted_range * BS;
+                               if(mapBlockMesh->isAnimationForced() ||
+                                               !faraway ||
+                                               mesh_animate_count_far < (m_control.range_all ? 200 : 50))
+                               {
+                                       bool animated = mapBlockMesh->animate(
+                                                       faraway,
+                                                       animation_time,
+                                                       crack,
+                                                       daynight_ratio);
+                                       if(animated)
+                                               mesh_animate_count++;
+                                       if(animated && faraway)
+                                               mesh_animate_count_far++;
+                               }
+                               else
+                               {
+                                       mapBlockMesh->decreaseAnimationForceTimer();
+                               }
+                       }
+
                        // Add to set
                        drawset[block->getPos()] = block;
                        
@@ -3951,11 +3930,14 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                        Draw the faces of the block
                */
                {
-                       JMutexAutoLock lock(block->mesh_mutex);
+                       //JMutexAutoLock lock(block->mesh_mutex);
+
+                       MapBlockMesh *mapBlockMesh = block->mesh;
+                       assert(mapBlockMesh);
 
-                       scene::SMesh *mesh = block->mesh;
+                       scene::SMesh *mesh = mapBlockMesh->getMesh();
                        assert(mesh);
-                       
+
                        u32 c = mesh->getMeshBufferCount();
                        bool stuff_actually_drawn = false;
                        for(u32 i=0; i<c; i++)
@@ -3999,6 +3981,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                        g_profiler->avg("CM: blocks in range without mesh (frac)",
                                        (float)blocks_in_range_without_mesh/blocks_in_range);
                g_profiler->avg("CM: blocks drawn", blocks_drawn);
+               g_profiler->avg("CM: animated meshes", mesh_animate_count);
+               g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far);
        }
        
        g_profiler->avg(prefix+"vertices drawn", vertex_count);
@@ -4047,205 +4031,6 @@ void ClientMap::renderPostFx()
        }
 }
 
-bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
-               core::map<v3s16, MapBlock*> *affected_blocks)
-{
-       bool changed = false;
-       /*
-               Add it to all blocks touching it
-       */
-       v3s16 dirs[7] = {
-               v3s16(0,0,0), // this
-               v3s16(0,0,1), // back
-               v3s16(0,1,0), // top
-               v3s16(1,0,0), // right
-               v3s16(0,0,-1), // front
-               v3s16(0,-1,0), // bottom
-               v3s16(-1,0,0), // left
-       };
-       for(u16 i=0; i<7; i++)
-       {
-               v3s16 p2 = p + dirs[i];
-               // Block position of neighbor (or requested) node
-               v3s16 blockpos = getNodeBlockPos(p2);
-               MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
-               if(blockref == NULL)
-                       continue;
-               // Relative position of requested node
-               v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
-               if(blockref->setTempMod(relpos, mod))
-               {
-                       changed = true;
-               }
-       }
-       if(changed && affected_blocks!=NULL)
-       {
-               for(u16 i=0; i<7; i++)
-               {
-                       v3s16 p2 = p + dirs[i];
-                       // Block position of neighbor (or requested) node
-                       v3s16 blockpos = getNodeBlockPos(p2);
-                       MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
-                       if(blockref == NULL)
-                               continue;
-                       affected_blocks->insert(blockpos, blockref);
-               }
-       }
-       return changed;
-}
-
-bool ClientMap::clearTempMod(v3s16 p,
-               core::map<v3s16, MapBlock*> *affected_blocks)
-{
-       bool changed = false;
-       v3s16 dirs[7] = {
-               v3s16(0,0,0), // this
-               v3s16(0,0,1), // back
-               v3s16(0,1,0), // top
-               v3s16(1,0,0), // right
-               v3s16(0,0,-1), // front
-               v3s16(0,-1,0), // bottom
-               v3s16(-1,0,0), // left
-       };
-       for(u16 i=0; i<7; i++)
-       {
-               v3s16 p2 = p + dirs[i];
-               // Block position of neighbor (or requested) node
-               v3s16 blockpos = getNodeBlockPos(p2);
-               MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
-               if(blockref == NULL)
-                       continue;
-               // Relative position of requested node
-               v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
-               if(blockref->clearTempMod(relpos))
-               {
-                       changed = true;
-               }
-       }
-       if(changed && affected_blocks!=NULL)
-       {
-               for(u16 i=0; i<7; i++)
-               {
-                       v3s16 p2 = p + dirs[i];
-                       // Block position of neighbor (or requested) node
-                       v3s16 blockpos = getNodeBlockPos(p2);
-                       MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
-                       if(blockref == NULL)
-                               continue;
-                       affected_blocks->insert(blockpos, blockref);
-               }
-       }
-       return changed;
-}
-
-void ClientMap::expireMeshes(bool only_daynight_diffed)
-{
-       TimeTaker timer("expireMeshes()");
-
-       core::map<v2s16, MapSector*>::Iterator si;
-       si = m_sectors.getIterator();
-       for(; si.atEnd() == false; si++)
-       {
-               MapSector *sector = si.getNode()->getValue();
-
-               core::list< MapBlock * > sectorblocks;
-               sector->getBlocks(sectorblocks);
-               
-               core::list< MapBlock * >::Iterator i;
-               for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
-               {
-                       MapBlock *block = *i;
-
-                       if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
-                       {
-                               continue;
-                       }
-                       
-                       {
-                               JMutexAutoLock lock(block->mesh_mutex);
-                               if(block->mesh != NULL)
-                               {
-                                       /*block->mesh->drop();
-                                       block->mesh = NULL;*/
-                                       block->setMeshExpired(true);
-                               }
-                       }
-               }
-       }
-}
-
-void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
-{
-       assert(mapType() == MAPTYPE_CLIENT);
-
-       try{
-               v3s16 p = blockpos + v3s16(0,0,0);
-               MapBlock *b = getBlockNoCreate(p);
-               b->updateMesh(daynight_ratio);
-               //b->setMeshExpired(true);
-       }
-       catch(InvalidPositionException &e){}
-       // Leading edge
-       try{
-               v3s16 p = blockpos + v3s16(-1,0,0);
-               MapBlock *b = getBlockNoCreate(p);
-               b->updateMesh(daynight_ratio);
-               //b->setMeshExpired(true);
-       }
-       catch(InvalidPositionException &e){}
-       try{
-               v3s16 p = blockpos + v3s16(0,-1,0);
-               MapBlock *b = getBlockNoCreate(p);
-               b->updateMesh(daynight_ratio);
-               //b->setMeshExpired(true);
-       }
-       catch(InvalidPositionException &e){}
-       try{
-               v3s16 p = blockpos + v3s16(0,0,-1);
-               MapBlock *b = getBlockNoCreate(p);
-               b->updateMesh(daynight_ratio);
-               //b->setMeshExpired(true);
-       }
-       catch(InvalidPositionException &e){}
-}
-
-#if 0
-/*
-       Update mesh of block in which the node is, and if the node is at the
-       leading edge, update the appropriate leading blocks too.
-*/
-void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
-{
-       v3s16 dirs[4] = {
-               v3s16(0,0,0),
-               v3s16(-1,0,0),
-               v3s16(0,-1,0),
-               v3s16(0,0,-1),
-       };
-       v3s16 blockposes[4];
-       for(u32 i=0; i<4; i++)
-       {
-               v3s16 np = nodepos + dirs[i];
-               blockposes[i] = getNodeBlockPos(np);
-               // Don't update mesh of block if it has been done already
-               bool already_updated = false;
-               for(u32 j=0; j<i; j++)
-               {
-                       if(blockposes[j] == blockposes[i])
-                       {
-                               already_updated = true;
-                               break;
-                       }
-               }
-               if(already_updated)
-                       continue;
-               // Update mesh
-               MapBlock *b = getBlockNoCreate(blockposes[i]);
-               b->updateMesh(daynight_ratio);
-       }
-}
-#endif
-
 void ClientMap::PrintInfo(std::ostream &out)
 {
        out<<"ClientMap: ";
index 90255c1d9cf289564f95581d4263d428d92fa955..ada59a4d525114b54ce5e020388ef6775589aba9 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -28,7 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "common_irrlicht.h"
 #include "mapnode.h"
-#include "mapblock_nodemod.h"
 #include "constants.h"
 #include "voxel.h"
 #include "utility.h" // Needed for UniqueQueue, a member of Map
@@ -570,33 +569,6 @@ class ClientMap : public Map, public scene::ISceneNode
 
        void renderPostFx();
 
-       /*
-               Methods for setting temporary modifications to nodes for
-               drawing.
-
-               Returns true if something changed.
-               
-               All blocks whose mesh could have been changed are inserted
-               to affected_blocks.
-       */
-       bool setTempMod(v3s16 p, NodeMod mod,
-                       core::map<v3s16, MapBlock*> *affected_blocks=NULL);
-       bool clearTempMod(v3s16 p,
-                       core::map<v3s16, MapBlock*> *affected_blocks=NULL);
-       // Efficient implementation needs a cache of TempMods
-       //void clearTempMods();
-
-       void expireMeshes(bool only_daynight_diffed);
-       
-       /*
-               Update the faces of the given block and blocks on the
-               leading edge, without threading. Rarely used.
-       */
-       void updateMeshes(v3s16 blockpos, u32 daynight_ratio);
-       
-       // Update meshes that touch the node
-       //void updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio);
-
        // For debug printing
        virtual void PrintInfo(std::ostream &out);
        
index b436378da5a2cbc3f4f907fbabcee7e860640e6a..70cb0e370c18146d1ea4dc7f375ff3c10a0fcef4 100644 (file)
@@ -59,10 +59,8 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
                reallocate();
        
 #ifndef SERVER
-       m_mesh_expired = false;
-       mesh_mutex.Init();
+       //mesh_mutex.Init();
        mesh = NULL;
-       m_temp_mods_mutex.Init();
 #endif
 }
 
@@ -70,11 +68,11 @@ MapBlock::~MapBlock()
 {
 #ifndef SERVER
        {
-               JMutexAutoLock lock(mesh_mutex);
+               //JMutexAutoLock lock(mesh_mutex);
                
                if(mesh)
                {
-                       mesh->drop();
+                       delete mesh;
                        mesh = NULL;
                }
        }
@@ -147,78 +145,6 @@ MapNode MapBlock::getNodeParentNoEx(v3s16 p)
        }
 }
 
-#ifndef SERVER
-
-#if 1
-void MapBlock::updateMesh(u32 daynight_ratio)
-{
-#if 0
-       /*
-               DEBUG: If mesh has been generated, don't generate it again
-       */
-       {
-               JMutexAutoLock meshlock(mesh_mutex);
-               if(mesh != NULL)
-                       return;
-       }
-#endif
-
-       MeshMakeData data;
-       data.fill(daynight_ratio, this);
-       
-       scene::SMesh *mesh_new = makeMapBlockMesh(&data, m_gamedef);
-       
-       /*
-               Replace the mesh
-       */
-
-       replaceMesh(mesh_new);
-
-}
-#endif
-
-void MapBlock::replaceMesh(scene::SMesh *mesh_new)
-{
-       mesh_mutex.Lock();
-
-       //scene::SMesh *mesh_old = mesh[daynight_i];
-       //mesh[daynight_i] = mesh_new;
-
-       scene::SMesh *mesh_old = mesh;
-       mesh = mesh_new;
-       setMeshExpired(false);
-       
-       if(mesh_old != NULL)
-       {
-               // Remove hardware buffers of meshbuffers of mesh
-               // NOTE: No way, this runs in a different thread and everything
-               /*u32 c = mesh_old->getMeshBufferCount();
-               for(u32 i=0; i<c; i++)
-               {
-                       IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
-               }*/
-               
-               /*infostream<<"mesh_old->getReferenceCount()="
-                               <<mesh_old->getReferenceCount()<<std::endl;
-               u32 c = mesh_old->getMeshBufferCount();
-               for(u32 i=0; i<c; i++)
-               {
-                       scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
-                       infostream<<"buf->getReferenceCount()="
-                                       <<buf->getReferenceCount()<<std::endl;
-               }*/
-
-               // Drop the mesh
-               mesh_old->drop();
-
-               //delete mesh_old;
-       }
-
-       mesh_mutex.Unlock();
-}
-       
-#endif // !SERVER
-
 /*
        Propagates sunlight down through the block.
        Doesn't modify nodes that are not affected by sunlight.
@@ -1232,13 +1158,6 @@ std::string analyze_block(MapBlock *block)
        else
                desc<<"is_ug [ ], ";
 
-#ifndef SERVER
-       if(block->getMeshExpired())
-               desc<<"mesh_exp [X], ";
-       else
-               desc<<"mesh_exp [ ], ";
-#endif
-
        if(block->getLightingExpired())
                desc<<"lighting_exp [X], ";
        else
index c9ff366794911526817fe2db5cebbae2df25db87..272da8ce34eea754f5e6a820009e2a5c36322951 100644 (file)
@@ -31,13 +31,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "constants.h"
 #include "voxel.h"
 #include "staticobject.h"
-#include "mapblock_nodemod.h"
 #include "modifiedstate.h"
 
 class Map;
 class NodeMetadataList;
 class IGameDef;
-class IWritableNodeDefManager;
+class MapBlockMesh;
 
 #define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff
 
@@ -193,18 +192,6 @@ class MapBlock /*: public NodeContainer*/
                raiseModified(MOD_STATE_WRITE_NEEDED, "setIsUnderground");
        }
 
-#ifndef SERVER
-       void setMeshExpired(bool expired)
-       {
-               m_mesh_expired = expired;
-       }
-       
-       bool getMeshExpired()
-       {
-               return m_mesh_expired;
-       }
-#endif
-
        void setLightingExpired(bool expired)
        {
                if(expired != m_lighting_expired){
@@ -359,33 +346,6 @@ class MapBlock /*: public NodeContainer*/
                                        setNode(x0+x, y0+y, z0+z, node);
        }
 
-       /*
-               Graphics-related methods
-       */
-       
-#ifndef SERVER // Only on client
-
-       u8 getFaceLight2(u32 daynight_ratio, v3s16 p, v3s16 face_dir,
-                       INodeDefManager *nodemgr)
-       {
-               return getFaceLight(daynight_ratio,
-                               getNodeParentNoEx(p),
-                               getNodeParentNoEx(p + face_dir),
-                               face_dir, nodemgr);
-       }
-       
-#if 1
-       /*
-               Thread-safely updates the whole mesh of the mapblock.
-               NOTE: Prefer generating the mesh separately and then using
-               replaceMesh().
-       */
-       void updateMesh(u32 daynight_ratio);
-#endif
-       // Replace the mesh with a new one
-       void replaceMesh(scene::SMesh *mesh_new);
-#endif
-       
        // See comments in mapblock.cpp
        bool propagateSunlight(core::map<v3s16, bool> & light_sources,
                        bool remove_light=false, bool *black_air_left=NULL);
@@ -395,59 +355,10 @@ class MapBlock /*: public NodeContainer*/
        // Copies data from VoxelManipulator getPosRelative()
        void copyFrom(VoxelManipulator &dst);
 
-#ifndef SERVER // Only on client
-       /*
-               Methods for setting temporary modifications to nodes for
-               drawing
-
-               returns true if the mod was different last time
-       */
-       bool setTempMod(v3s16 p, const NodeMod &mod)
-       {
-               /*dstream<<"setTempMod called on block"
-                               <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
-                               <<", mod.type="<<mod.type
-                               <<", mod.param="<<mod.param
-                               <<std::endl;*/
-               JMutexAutoLock lock(m_temp_mods_mutex);
-
-               return m_temp_mods.set(p, mod);
-       }
-       // Returns true if there was one
-       bool getTempMod(v3s16 p, NodeMod *mod)
-       {
-               JMutexAutoLock lock(m_temp_mods_mutex);
-
-               return m_temp_mods.get(p, mod);
-       }
-       bool clearTempMod(v3s16 p)
-       {
-               JMutexAutoLock lock(m_temp_mods_mutex);
-
-               return m_temp_mods.clear(p);
-       }
-       bool clearTempMods()
-       {
-               JMutexAutoLock lock(m_temp_mods_mutex);
-               
-               return m_temp_mods.clear();
-       }
-       void copyTempMods(NodeModMap &dst)
-       {
-               JMutexAutoLock lock(m_temp_mods_mutex);
-               m_temp_mods.copy(dst);
-       }
-#endif
-
        /*
                Update day-night lighting difference flag.
-               
                Sets m_day_night_differs to appropriate value.
-               
                These methods don't care about neighboring blocks.
-               It means that to know if a block really doesn't need a mesh
-               update between day and night, the neighboring blocks have
-               to be taken into account. Use Map::dayNightDiffed().
        */
        void updateDayNightDiff();
 
@@ -551,8 +462,8 @@ class MapBlock /*: public NodeContainer*/
        */
 
 #ifndef SERVER // Only on client
-       scene::SMesh *mesh;
-       JMutex mesh_mutex;
+       MapBlockMesh *mesh;
+       //JMutex mesh_mutex;
 #endif
        
        NodeMetadataList *m_node_metadata;
@@ -609,20 +520,6 @@ class MapBlock /*: public NodeContainer*/
 
        bool m_generated;
        
-#ifndef SERVER // Only on client
-       /*
-               Set to true if the mesh has been ordered to be updated
-               sometime in the background.
-               In practice this is set when the day/night lighting switches.
-       */
-       bool m_mesh_expired;
-       
-       // Temporary modifications to nodes
-       // These are only used when drawing
-       NodeModMap m_temp_mods;
-       JMutex m_temp_mods_mutex;
-#endif
-       
        /*
                When block is removed from active blocks, this is set to gametime.
                Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
index b843448c484c1cb70461d80593d4d27f433f6b1e..abe23855f247ea71513296b130753a066b766f90 100644 (file)
@@ -21,27 +21,31 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "light.h"
 #include "mapblock.h"
 #include "map.h"
-#include "main.h" // For g_settings and g_texturesource
-#include "settings.h"
+#include "main.h" // for g_profiler
 #include "profiler.h"
 #include "nodedef.h"
 #include "gamedef.h"
+#include "mesh.h"
 #include "content_mapblock.h"
 
-void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
+/*
+       MeshMakeData
+*/
+
+MeshMakeData::MeshMakeData(IGameDef *gamedef):
+       m_vmanip(),
+       m_blockpos(-1337,-1337,-1337),
+       m_crack_pos_relative(-1337, -1337, -1337),
+       m_smooth_lighting(false),
+       m_gamedef(gamedef)
+{}
+
+void MeshMakeData::fill(MapBlock *block)
 {
-       m_daynight_ratio = daynight_ratio;
        m_blockpos = block->getPos();
 
        v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
        
-       /*
-               There is no harm not copying the TempMods of the neighbors
-               because they are already copied to this block
-       */
-       m_temp_mods.clear();
-       block->copyTempMods(m_temp_mods);
-       
        /*
                Copy data
        */
@@ -81,11 +85,9 @@ void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
        }
 }
 
-void MeshMakeData::fillSingleNode(u32 daynight_ratio, MapNode *node)
+void MeshMakeData::fillSingleNode(MapNode *node)
 {
-       m_daynight_ratio = daynight_ratio;
        m_blockpos = v3s16(0,0,0);
-       m_temp_mods.clear();
        
        v3s16 blockpos_nodes = v3s16(0,0,0);
        VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
@@ -114,6 +116,226 @@ void MeshMakeData::fillSingleNode(u32 daynight_ratio, MapNode *node)
        delete[] data;
 }
 
+void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
+{
+       if(crack_level >= 0)
+               m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
+}
+
+void MeshMakeData::setSmoothLighting(bool smooth_lighting)
+{
+       m_smooth_lighting = smooth_lighting;
+}
+
+/*
+       Light and vertex color functions
+*/
+
+/*
+       Calculate non-smooth lighting at interior of node.
+       Single light bank.
+*/
+static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
+               MeshMakeData *data)
+{
+       INodeDefManager *ndef = data->m_gamedef->ndef();
+       u8 light = n.getLight(bank, ndef);
+
+       while(increment > 0)
+       {
+               light = undiminish_light(light);
+               --increment;
+       }
+       while(increment < 0)
+       {
+               light = diminish_light(light);
+               ++increment;
+       }
+
+       return decode_light(light);
+}
+
+/*
+       Calculate non-smooth lighting at interior of node.
+       Both light banks.
+*/
+u16 getInteriorLight(MapNode n, s32 increment, MeshMakeData *data)
+{
+       u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, data);
+       u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, data);
+       return day | (night << 8);
+}
+
+/*
+       Calculate non-smooth lighting at face of node.
+       Single light bank.
+*/
+static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
+               v3s16 face_dir, MeshMakeData *data)
+{
+       INodeDefManager *ndef = data->m_gamedef->ndef();
+
+       u8 light;
+       u8 l1 = n.getLight(bank, ndef);
+       u8 l2 = n2.getLight(bank, ndef);
+       if(l1 > l2)
+               light = l1;
+       else
+               light = l2;
+
+       // Make some nice difference to different sides
+
+       // This makes light come from a corner
+       /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
+               light = diminish_light(diminish_light(light));
+       else if(face_dir.X == -1 || face_dir.Z == -1)
+               light = diminish_light(light);*/
+
+       // All neighboring faces have different shade (like in minecraft)
+       if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
+               light = diminish_light(diminish_light(light));
+       else if(face_dir.Z == 1 || face_dir.Z == -1)
+               light = diminish_light(light);
+
+       return decode_light(light);
+}
+
+/*
+       Calculate non-smooth lighting at face of node.
+       Both light banks.
+*/
+u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, MeshMakeData *data)
+{
+       u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, data);
+       u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, data);
+       return day | (night << 8);
+}
+
+/*
+       Calculate smooth lighting at the XYZ- corner of p.
+       Single light bank.
+*/
+static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
+{
+       static v3s16 dirs8[8] = {
+               v3s16(0,0,0),
+               v3s16(0,0,1),
+               v3s16(0,1,0),
+               v3s16(0,1,1),
+               v3s16(1,0,0),
+               v3s16(1,1,0),
+               v3s16(1,0,1),
+               v3s16(1,1,1),
+       };
+
+       INodeDefManager *ndef = data->m_gamedef->ndef();
+
+       u16 ambient_occlusion = 0;
+       u16 light = 0;
+       u16 light_count = 0;
+       for(u32 i=0; i<8; i++)
+       {
+               MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
+               const ContentFeatures &f = ndef->get(n);
+               // Check f.solidness because fast-style leaves look
+               // better this way
+               if(f.param_type == CPT_LIGHT && f.solidness != 2)
+               {
+                       light += decode_light(n.getLight(bank, ndef));
+                       light_count++;
+               }
+               else if(n.getContent() != CONTENT_IGNORE)
+               {
+                       ambient_occlusion++;
+               }
+       }
+
+       if(light_count == 0)
+               return 255;
+       
+       light /= light_count;
+
+       if(ambient_occlusion > 4)
+       {
+               ambient_occlusion -= 4;
+               light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
+       }
+
+       return light;
+}
+
+/*
+       Calculate smooth lighting at the XYZ- corner of p.
+       Both light banks.
+*/
+static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
+{
+       u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
+       u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
+       return day | (night << 8);
+}
+
+/*
+       Calculate smooth lighting at the given corner of p.
+       Both light banks.
+*/
+u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
+{
+       if(corner.X == 1) p.X += 1;
+       else              assert(corner.X == -1);
+       if(corner.Y == 1) p.Y += 1;
+       else              assert(corner.Y == -1);
+       if(corner.Z == 1) p.Z += 1;
+       else              assert(corner.Z == -1);
+       
+       return getSmoothLight(p, data);
+}
+
+/*
+       Converts from day + night color values (0..255)
+       and a given daynight_ratio to the final SColor shown on screen.
+*/
+static void finalColorBlend(video::SColor& result,
+               u8 day, u8 night, u32 daynight_ratio)
+{
+       s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
+       s32 b = rg;
+
+       // Moonlight is blue
+       b += (day - night) / 13;
+       rg -= (day - night) / 23;
+
+       // Emphase blue a bit in darker places
+       // Each entry of this array represents a range of 8 blue levels
+       static u8 emphase_blue_when_dark[32] = {
+               1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       };
+       if(b < 0)
+               b = 0;
+       if(b > 255)
+               b = 255;
+       b += emphase_blue_when_dark[b / 8];
+
+       // Artificial light is yellow-ish
+       static u8 emphase_yellow_when_artificial[16] = {
+               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
+       };
+       rg += emphase_yellow_when_artificial[night/16];
+       if(rg < 0)
+               rg = 0;
+       if(rg > 255)
+               rg = 255;
+
+       result.setRed(rg);
+       result.setGreen(rg);
+       result.setBlue(b);
+}
+
+/*
+       Mesh generation helpers
+*/
+
 /*
        vertex_dirs: v3s16[4]
 */
@@ -177,41 +399,19 @@ static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
        }
 }
 
-video::SColor MapBlock_LightColor(u8 alpha, u8 light)
-{
-#if 0
-       return video::SColor(alpha,light,light,light);
-#endif
-       //return video::SColor(alpha,light,light,MYMAX(0, (s16)light-25)+25);
-       /*return video::SColor(alpha,light,light,MYMAX(0,
-                       pow((float)light/255.0, 0.8)*255.0));*/
-#if 1
-       // Emphase blue a bit in darker places
-       float lim = 80;
-       float power = 0.8;
-       if(light > lim)
-               return video::SColor(alpha,light,light,light);
-       else
-               return video::SColor(alpha,light,light,MYMAX(0,
-                               pow((float)light/lim, power)*lim));
-#endif
-}
-
 struct FastFace
 {
        TileSpec tile;
        video::S3DVertex vertices[4]; // Precalculated vertices
 };
 
-static void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
-               v3s16 dir, v3f scale, v3f posRelative_f,
-               core::array<FastFace> &dest)
+static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
+               v3f p, v3s16 dir, v3f scale, core::array<FastFace> &dest)
 {
        FastFace face;
        
        // Position is at the center of the cube.
        v3f pos = p * BS;
-       posRelative_f *= BS;
 
        v3f vertex_pos[4];
        v3s16 vertex_dirs[4];
@@ -230,7 +430,7 @@ static void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
                vertex_pos[i].X *= scale.X;
                vertex_pos[i].Y *= scale.Y;
                vertex_pos[i].Z *= scale.Z;
-               vertex_pos[i] += pos + posRelative_f;
+               vertex_pos[i] += pos;
        }
 
        f32 abs_scale = 1.;
@@ -241,26 +441,12 @@ static void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
        v3f normal(dir.X, dir.Y, dir.Z);
 
        u8 alpha = tile.alpha;
-       /*u8 alpha = 255;
-       if(tile.id == TILE_WATER)
-               alpha = WATER_ALPHA;*/
 
        float x0 = tile.texture.pos.X;
        float y0 = tile.texture.pos.Y;
        float w = tile.texture.size.X;
        float h = tile.texture.size.Y;
 
-       /*video::SColor c = MapBlock_LightColor(alpha, li);
-
-       face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
-                       core::vector2d<f32>(x0+w*abs_scale, y0+h));
-       face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
-                       core::vector2d<f32>(x0, y0+h));
-       face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
-                       core::vector2d<f32>(x0, y0));
-       face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
-                       core::vector2d<f32>(x0+w*abs_scale, y0));*/
-
        face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
                        MapBlock_LightColor(alpha, li0),
                        core::vector2d<f32>(x0+w*abs_scale, y0+h));
@@ -275,14 +461,89 @@ static void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
                        core::vector2d<f32>(x0+w*abs_scale, y0));
 
        face.tile = tile;
-       //DEBUG
-       //f->tile = TILE_STONE;
        
        dest.push_back(face);
 }
+
+/*
+       Nodes make a face if contents differ and solidness differs.
+       Return value:
+               0: No face
+               1: Face uses m1's content
+               2: Face uses m2's content
+       equivalent: Whether the blocks share the same face (eg. water and glass)
+
+       TODO: Add 3: Both faces drawn with backface culling, remove equivalent
+*/
+static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
+               INodeDefManager *ndef)
+{
+       *equivalent = false;
+
+       if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
+               return 0;
+       
+       bool contents_differ = (m1 != m2);
+       
+       const ContentFeatures &f1 = ndef->get(m1);
+       const ContentFeatures &f2 = ndef->get(m2);
+
+       // Contents don't differ for different forms of same liquid
+       if(f1.sameLiquid(f2))
+               contents_differ = false;
+       
+       u8 c1 = f1.solidness;
+       u8 c2 = f2.solidness;
+
+       bool solidness_differs = (c1 != c2);
+       bool makes_face = contents_differ && solidness_differs;
+
+       if(makes_face == false)
+               return 0;
        
-static TileSpec getTile(const MapNode &node, v3s16 dir, INodeDefManager *nodemgr)
+       if(c1 == 0)
+               c1 = f1.visual_solidness;
+       if(c2 == 0)
+               c2 = f2.visual_solidness;
+       
+       if(c1 == c2){
+               *equivalent = true;
+               // If same solidness, liquid takes precense
+               if(f1.isLiquid())
+                       return 1;
+               if(f2.isLiquid())
+                       return 2;
+       }
+       
+       if(c1 > c2)
+               return 1;
+       else
+               return 2;
+}
+
+/*
+       Gets nth node tile (0 <= n <= 5).
+*/
+TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
 {
+       INodeDefManager *ndef = data->m_gamedef->ndef();
+       TileSpec spec = ndef->get(mn).tiles[tileindex];
+       // Apply temporary crack
+       if(p == data->m_crack_pos_relative)
+       {
+               spec.material_flags |= MATERIAL_FLAG_CRACK;
+               spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
+       }
+       return spec;
+}
+
+/*
+       Gets node tile given a face direction.
+*/
+TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
+{
+       INodeDefManager *ndef = data->m_gamedef->ndef();
+
        // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
        // (0,0,1), (0,0,-1) or (0,0,0)
        assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
@@ -299,7 +560,7 @@ static TileSpec getTile(const MapNode &node, v3s16 dir, INodeDefManager *nodemgr
        u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
 
        // Get rotation for things like chests
-       u8 facedir = node.getFaceDir(nodemgr);
+       u8 facedir = mn.getFaceDir(ndef);
        assert(facedir <= 3);
        
        static const u8 dir_to_tile[4 * 8] =
@@ -310,196 +571,36 @@ static TileSpec getTile(const MapNode &node, v3s16 dir, INodeDefManager *nodemgr
                   0,  3,  0,  5,  0,  4,  1,  2,  // facedir = 2
                   0,  5,  0,  2,  0,  3,  1,  4,  // facedir = 3
        };
-
-       return nodemgr->get(node).tiles[dir_to_tile[facedir*8 + dir_i]];
-}
-
-/*
-       Gets node tile from any place relative to block.
-       Returns TILE_NODE if doesn't exist or should not be drawn.
-*/
-TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
-               NodeModMap *temp_mods, ITextureSource *tsrc, INodeDefManager *ndef)
-{
-       TileSpec spec;
-       spec = getTile(mn, face_dir, ndef);
-       
-       /*
-               Check temporary modifications on this node
-       */
-       /*core::map<v3s16, NodeMod>::Node *n;
-       n = m_temp_mods.find(p);
-       // If modified
-       if(n != NULL)
-       {
-               struct NodeMod mod = n->getValue();*/
-       NodeMod mod;
-       if(temp_mods && temp_mods->get(p, &mod))
-       {
-               #if 0  // NODEMOD_CHANGECONTENT isn't used at the moment
-               if(mod.type == NODEMOD_CHANGECONTENT)
-               {
-                       MapNode mn2(mod.param);
-                       spec = getTile(mn2, face_dir, ndef);
-               }
-               #endif
-               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 = tsrc->getTextureName(orig_id);
-
-                       // Create new texture name
-                       std::ostringstream os;
-                       os<<orig_name<<"^[crack"<<mod.param;
-
-                       // Get new texture
-                       u32 new_id = tsrc->getTextureId(os.str());
-                       
-                       /*dstream<<"MapBlock::getNodeTile(): Switching from "
-                                       <<orig_name<<" to "<<os.str()<<" ("
-                                       <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
-                       
-                       spec.texture = tsrc->getTexture(new_id);
-               }
-       }
-       
-       return spec;
-}
-
-static content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap *temp_mods)
-{
-       /*
-               Check temporary modifications on this node
-       */
-       #if 0  // NODEMOD_CHANGECONTENT isn't used at the moment
-       NodeMod mod;
-       if(temp_mods && temp_mods->get(p, &mod))
-       {
-               if(mod.type == NODEMOD_CHANGECONTENT)
-               {
-                       // Overrides content
-                       return mod.param;
-               }
-               if(mod.type == NODEMOD_CRACK)
-               {
-                       /*
-                               Content doesn't change.
-                               
-                               face_contents works just like it should, because
-                               there should not be faces between differently cracked
-                               nodes.
-
-                               If a semi-transparent node is cracked in front an
-                               another one, it really doesn't matter whether there
-                               is a cracked face drawn in between or not.
-                       */
-               }
-       }
-       #endif
-
-       return mn.getContent();
-}
-
-v3s16 dirs8[8] = {
-       v3s16(0,0,0),
-       v3s16(0,0,1),
-       v3s16(0,1,0),
-       v3s16(0,1,1),
-       v3s16(1,0,0),
-       v3s16(1,1,0),
-       v3s16(1,0,1),
-       v3s16(1,1,1),
-};
-
-// Calculate lighting at the XYZ- corner of p
-static u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio,
-               INodeDefManager *ndef)
-{
-       u16 ambient_occlusion = 0;
-       u16 light = 0;
-       u16 light_count = 0;
-       for(u32 i=0; i<8; i++)
-       {
-               MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
-               if(ndef->get(n).param_type == CPT_LIGHT
-                               // Fast-style leaves look better this way
-                               && ndef->get(n).solidness != 2)
-               {
-                       light += decode_light(n.getLightBlend(daynight_ratio, ndef));
-                       light_count++;
-               }
-               else
-               {
-                       if(n.getContent() != CONTENT_IGNORE)
-                               ambient_occlusion++;
-               }
-       }
-
-       if(light_count == 0)
-               return 255;
-       
-       light /= light_count;
-
-       if(ambient_occlusion > 4)
-       {
-               ambient_occlusion -= 4;
-               light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
-       }
-
-       return light;
-}
-
-// Calculate lighting at the given corner of p
-static u8 getSmoothLight(v3s16 p, v3s16 corner,
-               VoxelManipulator &vmanip, u32 daynight_ratio, INodeDefManager *ndef)
-{
-       if(corner.X == 1) p.X += 1;
-       else              assert(corner.X == -1);
-       if(corner.Y == 1) p.Y += 1;
-       else              assert(corner.Y == -1);
-       if(corner.Z == 1) p.Z += 1;
-       else              assert(corner.Z == -1);
-       
-       return getSmoothLight(p, vmanip, daynight_ratio, ndef);
+       u8 tileindex = dir_to_tile[facedir*8 + dir_i];
+       return getNodeTileN(mn, p, tileindex, data);
 }
 
 static void getTileInfo(
                // Input:
-               v3s16 blockpos_nodes,
+               MeshMakeData *data,
                v3s16 p,
                v3s16 face_dir,
-               u32 daynight_ratio,
-               VoxelManipulator &vmanip,
-               NodeModMap *temp_mods,
-               bool smooth_lighting,
-               IGameDef *gamedef,
                // Output:
                bool &makes_face,
                v3s16 &p_corrected,
                v3s16 &face_dir_corrected,
-               u8 *lights,
+               u16 *lights,
                TileSpec &tile
        )
 {
-       ITextureSource *tsrc = gamedef->tsrc();
-       INodeDefManager *ndef = gamedef->ndef();
+       VoxelManipulator &vmanip = data->m_vmanip;
+       INodeDefManager *ndef = data->m_gamedef->ndef();
+       v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
 
        MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
        MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
-       TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods, tsrc, ndef);
-       TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods, tsrc, ndef);
+       TileSpec tile0 = getNodeTile(n0, p, face_dir, data);
+       TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, data);
        
        // This is hackish
-       content_t content0 = getNodeContent(p, n0, temp_mods);
-       content_t content1 = getNodeContent(p + face_dir, n1, temp_mods);
        bool equivalent = false;
-       u8 mf = face_contents(content0, content1, &equivalent, ndef);
+       u8 mf = face_contents(n0.getContent(), n1.getContent(),
+                       &equivalent, ndef);
 
        if(mf == 0)
        {
@@ -526,10 +627,10 @@ static void getTileInfo(
        if(equivalent)
                tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
        
-       if(smooth_lighting == false)
+       if(data->m_smooth_lighting == false)
        {
                lights[0] = lights[1] = lights[2] = lights[3] =
-                               decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir, ndef));
+                               getFaceLight(n0, n1, face_dir, data);
        }
        else
        {
@@ -537,8 +638,9 @@ static void getTileInfo(
                getNodeVertexDirs(face_dir_corrected, vertex_dirs);
                for(u16 i=0; i<4; i++)
                {
-                       lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
-                                       vertex_dirs[i], vmanip, daynight_ratio, ndef);
+                       lights[i] = getSmoothLight(
+                                       blockpos_nodes + p_corrected,
+                                       vertex_dirs[i], data);
                }
        }
        
@@ -551,20 +653,13 @@ static void getTileInfo(
        face_dir: unit vector with only one of x, y or z
 */
 static void updateFastFaceRow(
-               u32 daynight_ratio,
-               v3f posRelative_f,
+               MeshMakeData *data,
                v3s16 startpos,
-               u16 length,
                v3s16 translate_dir,
                v3f translate_dir_f,
                v3s16 face_dir,
                v3f face_dir_f,
-               core::array<FastFace> &dest,
-               NodeModMap *temp_mods,
-               VoxelManipulator &vmanip,
-               v3s16 blockpos_nodes,
-               bool smooth_lighting,
-               IGameDef *gamedef)
+               core::array<FastFace> &dest)
 {
        v3s16 p = startpos;
        
@@ -573,13 +668,13 @@ static void updateFastFaceRow(
        bool makes_face = false;
        v3s16 p_corrected;
        v3s16 face_dir_corrected;
-       u8 lights[4] = {0,0,0,0};
+       u16 lights[4] = {0,0,0,0};
        TileSpec tile;
-       getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
-                       vmanip, temp_mods, smooth_lighting, gamedef,
-                       makes_face, p_corrected, face_dir_corrected, lights, tile);
+       getTileInfo(data, p, face_dir, 
+                       makes_face, p_corrected, face_dir_corrected,
+                       lights, tile);
 
-       for(u16 j=0; j<length; j++)
+       for(u16 j=0; j<MAP_BLOCKSIZE; j++)
        {
                // If tiling can be done, this is set to false in the next step
                bool next_is_different = true;
@@ -589,17 +684,16 @@ static void updateFastFaceRow(
                bool next_makes_face = false;
                v3s16 next_p_corrected;
                v3s16 next_face_dir_corrected;
-               u8 next_lights[4] = {0,0,0,0};
+               u16 next_lights[4] = {0,0,0,0};
                TileSpec next_tile;
                
                // If at last position, there is nothing to compare to and
                // the face must be drawn anyway
-               if(j != length - 1)
+               if(j != MAP_BLOCKSIZE - 1)
                {
                        p_next = p + translate_dir;
                        
-                       getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
-                                       vmanip, temp_mods, smooth_lighting, gamedef,
+                       getTileInfo(data, p_next, face_dir,
                                        next_makes_face, next_p_corrected,
                                        next_face_dir_corrected, next_lights,
                                        next_tile);
@@ -688,7 +782,7 @@ static void updateFastFaceRow(
                                
                                makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
                                                sp, face_dir_corrected, scale,
-                                               posRelative_f, dest);
+                                               dest);
                                
                                g_profiler->avg("Meshgen: faces drawn by tiling", 0);
                                for(int i=1; i<continuous_tiles_count; i++){
@@ -712,26 +806,74 @@ static void updateFastFaceRow(
        }
 }
 
-scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef)
+static void updateAllFastFaceRows(MeshMakeData *data,
+               core::array<FastFace> &dest)
 {
-       // 4-21ms for MAP_BLOCKSIZE=16
-       // 24-155ms for MAP_BLOCKSIZE=32
-       //TimeTaker timer1("makeMapBlockMesh()");
+       /*
+               Go through every y,z and get top(y+) faces in rows of x+
+       */
+       for(s16 y=0; y<MAP_BLOCKSIZE; y++){
+               for(s16 z=0; z<MAP_BLOCKSIZE; z++){
+                       updateFastFaceRow(data,
+                                       v3s16(0,y,z),
+                                       v3s16(1,0,0), //dir
+                                       v3f  (1,0,0),
+                                       v3s16(0,1,0), //face dir
+                                       v3f  (0,1,0),
+                                       dest);
+               }
+       }
 
-       core::array<FastFace> fastfaces_new;
+       /*
+               Go through every x,y and get right(x+) faces in rows of z+
+       */
+       for(s16 x=0; x<MAP_BLOCKSIZE; x++){
+               for(s16 y=0; y<MAP_BLOCKSIZE; y++){
+                       updateFastFaceRow(data,
+                                       v3s16(x,y,0),
+                                       v3s16(0,0,1), //dir
+                                       v3f  (0,0,1),
+                                       v3s16(1,0,0), //face dir
+                                       v3f  (1,0,0),
+                                       dest);
+               }
+       }
 
-       v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
-       
-       // floating point conversion
-       v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
-       
        /*
-               Some settings
+               Go through every y,z and get back(z+) faces in rows of x+
        */
-       //bool new_style_water = g_settings->getBool("new_style_water");
-       //bool new_style_leaves = g_settings->getBool("new_style_leaves");
-       bool smooth_lighting = g_settings->getBool("smooth_lighting");
-       
+       for(s16 z=0; z<MAP_BLOCKSIZE; z++){
+               for(s16 y=0; y<MAP_BLOCKSIZE; y++){
+                       updateFastFaceRow(data,
+                                       v3s16(0,y,z),
+                                       v3s16(1,0,0), //dir
+                                       v3f  (1,0,0),
+                                       v3s16(0,0,1), //face dir
+                                       v3f  (0,0,1),
+                                       dest);
+               }
+       }
+}
+
+/*
+       MapBlockMesh
+*/
+
+MapBlockMesh::MapBlockMesh(MeshMakeData *data):
+       m_mesh(new scene::SMesh()),
+       m_gamedef(data->m_gamedef),
+       m_animation_force_timer(0), // force initial animation
+       m_last_crack(-1),
+       m_crack_materials(),
+       m_last_daynight_ratio((u32) -1),
+       m_daynight_diffs()
+{
+       // 4-21ms for MAP_BLOCKSIZE=16  (NOTE: probably outdated)
+       // 24-155ms for MAP_BLOCKSIZE=32  (NOTE: probably outdated)
+       //TimeTaker timer1("MapBlockMesh()");
+
+       core::array<FastFace> fastfaces_new;
+
        /*
                We are including the faces of the trailing edges of the block.
                This means that when something changes, the caller must
@@ -739,92 +881,23 @@ scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef)
 
                NOTE: This is the slowest part of this method.
        */
-       
        {
-               // 4-23ms for MAP_BLOCKSIZE=16
-               //TimeTaker timer2("updateMesh() collect");
-
-               /*
-                       Go through every y,z and get top(y+) faces in rows of x+
-               */
-               for(s16 y=0; y<MAP_BLOCKSIZE; y++){
-                       for(s16 z=0; z<MAP_BLOCKSIZE; z++){
-                               updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
-                                               v3s16(0,y,z), MAP_BLOCKSIZE,
-                                               v3s16(1,0,0), //dir
-                                               v3f  (1,0,0),
-                                               v3s16(0,1,0), //face dir
-                                               v3f  (0,1,0),
-                                               fastfaces_new,
-                                               &data->m_temp_mods,
-                                               data->m_vmanip,
-                                               blockpos_nodes,
-                                               smooth_lighting,
-                                               gamedef);
-                       }
-               }
-               /*
-                       Go through every x,y and get right(x+) faces in rows of z+
-               */
-               for(s16 x=0; x<MAP_BLOCKSIZE; x++){
-                       for(s16 y=0; y<MAP_BLOCKSIZE; y++){
-                               updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
-                                               v3s16(x,y,0), MAP_BLOCKSIZE,
-                                               v3s16(0,0,1),
-                                               v3f  (0,0,1),
-                                               v3s16(1,0,0),
-                                               v3f  (1,0,0),
-                                               fastfaces_new,
-                                               &data->m_temp_mods,
-                                               data->m_vmanip,
-                                               blockpos_nodes,
-                                               smooth_lighting,
-                                               gamedef);
-                       }
-               }
-               /*
-                       Go through every y,z and get back(z+) faces in rows of x+
-               */
-               for(s16 z=0; z<MAP_BLOCKSIZE; z++){
-                       for(s16 y=0; y<MAP_BLOCKSIZE; y++){
-                               updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
-                                               v3s16(0,y,z), MAP_BLOCKSIZE,
-                                               v3s16(1,0,0),
-                                               v3f  (1,0,0),
-                                               v3s16(0,0,1),
-                                               v3f  (0,0,1),
-                                               fastfaces_new,
-                                               &data->m_temp_mods,
-                                               data->m_vmanip,
-                                               blockpos_nodes,
-                                               smooth_lighting,
-                                               gamedef);
-                       }
-               }
+               // 4-23ms for MAP_BLOCKSIZE=16  (NOTE: probably outdated)
+               //TimeTaker timer2("updateAllFastFaceRows()");
+               updateAllFastFaceRows(data, fastfaces_new);
        }
-
        // End of slow part
 
        /*
-               Convert FastFaces to SMesh
+               Convert FastFaces to MeshCollector
        */
 
        MeshCollector collector;
 
-       if(fastfaces_new.size() > 0)
        {
                // avg 0ms (100ms spikes when loading textures the first time)
-               //TimeTaker timer2("updateMesh() mesh building");
-
-               video::SMaterial material;
-               material.setFlag(video::EMF_LIGHTING, false);
-               material.setFlag(video::EMF_BACK_FACE_CULLING, true);
-               material.setFlag(video::EMF_BILINEAR_FILTER, false);
-               material.setFlag(video::EMF_FOG_ENABLE, true);
-               //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
-               //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
-               material.MaterialType
-                               = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+               // (NOTE: probably outdated)
+               //TimeTaker timer2("MeshCollector building");
 
                for(u32 i=0; i<fastfaces_new.size(); i++)
                {
@@ -833,25 +906,21 @@ scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef)
                        const u16 indices[] = {0,1,2,2,3,0};
                        const u16 indices_alternate[] = {0,1,3,2,3,1};
                        
-                       video::ITexture *texture = f.tile.texture.atlas;
-                       if(texture == NULL)
+                       if(f.tile.texture.atlas == NULL)
                                continue;
 
-                       material.setTexture(0, texture);
-                       
-                       f.tile.applyMaterialOptions(material);
-
                        const u16 *indices_p = indices;
                        
                        /*
                                Revert triangles for nicer looking gradient if vertices
                                1 and 3 have same color or 0 and 2 have different color.
+                               getRed() is the day color.
                        */
-                       if(f.vertices[0].Color != f.vertices[2].Color
-                                       || f.vertices[1].Color == f.vertices[3].Color)
+                       if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
+                                       || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
                                indices_p = indices_alternate;
                        
-                       collector.append(material, f.vertices, 4, indices_p, 6);
+                       collector.append(f.tile, f.vertices, 4, indices_p, 6);
                }
        }
 
@@ -863,45 +932,91 @@ scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef)
                - whatever
        */
 
-       mapblock_mesh_generate_special(data, collector, gamedef);
+       mapblock_mesh_generate_special(data, collector);
        
-       /*
-               Add stuff from collector to mesh
-       */
-       
-       scene::SMesh *mesh_new = NULL;
-       mesh_new = new scene::SMesh();
-       
-       collector.fillMesh(mesh_new);
 
        /*
-               Do some stuff to the mesh
+               Convert MeshCollector to SMesh
+               Also store animation info
        */
+       for(u32 i = 0; i < collector.prebuffers.size(); i++)
+       {
+               PreMeshBuffer &p = collector.prebuffers[i];
+               /*dstream<<"p.vertices.size()="<<p.vertices.size()
+                               <<", p.indices.size()="<<p.indices.size()
+                               <<std::endl;*/
+
+               // Generate animation data
+               // - Cracks
+               if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
+               {
+                       ITextureSource *tsrc = data->m_gamedef->tsrc();
+                       std::string crack_basename = tsrc->getTextureName(p.tile.texture.id);
+                       if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
+                               crack_basename += "^[cracko";
+                       else
+                               crack_basename += "^[crack";
+                       m_crack_materials.insert(std::make_pair(i, crack_basename));
+               }
+               // - Lighting
+               for(u32 j = 0; j < p.vertices.size(); j++)
+               {
+                       video::SColor &vc = p.vertices[j].Color;
+                       u8 day = vc.getRed();
+                       u8 night = vc.getGreen();
+                       finalColorBlend(vc, day, night, 1000);
+                       if(day != night)
+                               m_daynight_diffs[i][j] = std::make_pair(day, night);
+               }
+
 
-       mesh_new->recalculateBoundingBox();
+               // Create material
+               video::SMaterial material;
+               material.setFlag(video::EMF_LIGHTING, false);
+               material.setFlag(video::EMF_BACK_FACE_CULLING, true);
+               material.setFlag(video::EMF_BILINEAR_FILTER, false);
+               material.setFlag(video::EMF_FOG_ENABLE, true);
+               //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
+               //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
+               material.MaterialType
+                               = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+               material.setTexture(0, p.tile.texture.atlas);
+               p.tile.applyMaterialOptions(material);
+
+               // Create meshbuffer
+
+               // This is a "Standard MeshBuffer",
+               // it's a typedeffed CMeshBuffer<video::S3DVertex>
+               scene::SMeshBuffer *buf = new scene::SMeshBuffer();
+               // Set material
+               buf->Material = material;
+               // Add to mesh
+               m_mesh->addMeshBuffer(buf);
+               // Mesh grabbed it
+               buf->drop();
+               buf->append(p.vertices.pointer(), p.vertices.size(),
+                               p.indices.pointer(), p.indices.size());
+       }
 
        /*
-               Delete new mesh if it is empty
+               Do some stuff to the mesh
        */
 
-       if(mesh_new->getMeshBufferCount() == 0)
-       {
-               mesh_new->drop();
-               mesh_new = NULL;
-       }
+       translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
+       m_mesh->recalculateBoundingBox(); // translateMesh already does this
 
-       if(mesh_new)
+       if(m_mesh)
        {
 #if 0
                // Usually 1-700 faces and 1-7 materials
                std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
-                               <<"and uses "<<mesh_new->getMeshBufferCount()
+                               <<"and uses "<<m_mesh->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);
+               //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
 
                /*
                        NOTE: If that is enabled, some kind of a queue to the main
@@ -909,9 +1024,118 @@ scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef)
                        the hardware buffer and then delete the mesh
                */
        }
-
-       return mesh_new;
        
        //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
+
+       // Check if animation is required for this mesh
+       m_has_animation =
+               !m_crack_materials.empty() ||
+               !m_daynight_diffs.empty();
+}
+
+MapBlockMesh::~MapBlockMesh()
+{
+       m_mesh->drop();
+       m_mesh = NULL;
 }
 
+bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
+{
+       if(!m_has_animation)
+       {
+               m_animation_force_timer = 100000;
+               return false;
+       }
+
+       m_animation_force_timer = myrand_range(5, 100);
+
+       // Cracks
+       if(crack != m_last_crack)
+       {
+               for(std::map<u32, std::string>::iterator
+                               i = m_crack_materials.begin();
+                               i != m_crack_materials.end(); i++)
+               {
+                       scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
+                       std::string basename = i->second;
+
+                       // Create new texture name from original
+                       ITextureSource *tsrc = m_gamedef->getTextureSource();
+                       std::ostringstream os;
+                       os<<basename<<crack;
+                       AtlasPointer ap = tsrc->getTexture(os.str());
+                       buf->getMaterial().setTexture(0, ap.atlas);
+               }
+
+               m_last_crack = crack;
+       }
+
+       // Day-night transition
+       if(daynight_ratio != m_last_daynight_ratio)
+       {
+               for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
+                               i = m_daynight_diffs.begin();
+                               i != m_daynight_diffs.end(); i++)
+               {
+                       scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
+                       video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
+                       for(std::map<u32, std::pair<u8, u8 > >::iterator
+                                       j = i->second.begin();
+                                       j != i->second.end(); j++)
+                       {
+                               u32 vertexIndex = j->first;
+                               u8 day = j->second.first;
+                               u8 night = j->second.second;
+                               finalColorBlend(vertices[vertexIndex].Color,
+                                               day, night, daynight_ratio);
+                       }
+               }
+               m_last_daynight_ratio = daynight_ratio;
+       }
+
+       return true;
+}
+
+/*
+       MeshCollector
+*/
+
+void MeshCollector::append(const TileSpec &tile,
+               const video::S3DVertex *vertices, u32 numVertices,
+               const u16 *indices, u32 numIndices)
+{
+       PreMeshBuffer *p = NULL;
+       for(u32 i=0; i<prebuffers.size(); i++)
+       {
+               PreMeshBuffer &pp = prebuffers[i];
+               if(pp.tile != tile)
+                       continue;
+
+               p = &pp;
+               break;
+       }
+
+       if(p == NULL)
+       {
+               PreMeshBuffer pp;
+               pp.tile = tile;
+               prebuffers.push_back(pp);
+               p = &prebuffers[prebuffers.size()-1];
+       }
+
+       u32 vertex_count = p->vertices.size();
+       for(u32 i=0; i<numIndices; i++)
+       {
+               u32 j = indices[i] + vertex_count;
+               if(j > 65535)
+               {
+                       dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
+                       // NOTE: Fix is to just add an another MeshBuffer
+               }
+               p->indices.push_back(j);
+       }
+       for(u32 i=0; i<numVertices; i++)
+       {
+               p->vertices.push_back(vertices[i]);
+       }
+}
index 4d3e7d29d1d73232bab8a3b5324a6241e6c95e72..5028873c47236b1d30b03cc7ae42d5ba5e358b16 100644 (file)
@@ -21,9 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define MAPBLOCK_MESH_HEADER
 
 #include "common_irrlicht.h"
-#include "mapblock_nodemod.h"
 #include "tile.h"
 #include "voxel.h"
+#include <map>
 
 class IGameDef;
 
@@ -31,127 +31,143 @@ class IGameDef;
        Mesh making stuff
 */
 
-/*
-       This is used because CMeshBuffer::append() is very slow
-*/
-struct PreMeshBuffer
+
+class MapBlock;
+
+struct MeshMakeData
 {
-       video::SMaterial material;
-       core::array<u16> indices;
-       core::array<video::S3DVertex> vertices;
+       VoxelManipulator m_vmanip;
+       v3s16 m_blockpos;
+       v3s16 m_crack_pos_relative;
+       bool m_smooth_lighting;
+       IGameDef *m_gamedef;
+
+       MeshMakeData(IGameDef *gamedef);
+
+       /*
+               Copy central data directly from block, and other data from
+               parent of block.
+       */
+       void fill(MapBlock *block);
+
+       /*
+               Set up with only a single node at (1,1,1)
+       */
+       void fillSingleNode(MapNode *node);
+
+       /*
+               Set the (node) position of a crack
+       */
+       void setCrack(int crack_level, v3s16 crack_pos);
+
+       /*
+               Enable or disable smooth lighting
+       */
+       void setSmoothLighting(bool smooth_lighting);
 };
 
-class MeshCollector
+/*
+       Holds a mesh for a mapblock.
+
+       Besides the SMesh*, this contains information used for animating
+       the vertex positions, colors and texture coordinates of the mesh.
+       For example:
+       - cracks [implemented]
+       - day/night transitions [implemented]
+       - animated flowing liquids [not implemented]
+       - animating vertex positions for e.g. axles [not implemented]
+*/
+class MapBlockMesh
 {
 public:
-       void append(
-                       video::SMaterial material,
-                       const video::S3DVertex* const vertices,
-                       u32 numVertices,
-                       const u16* const indices,
-                       u32 numIndices
-               )
+       // Builds the mesh given
+       MapBlockMesh(MeshMakeData *data);
+       ~MapBlockMesh();
+
+       // Main animation function, parameters:
+       //   faraway: whether the block is far away from the camera (~50 nodes)
+       //   time: the global animation time, 0 .. 60 (repeats every minute)
+       //   daynight_ratio: 0 .. 1000
+       //   crack: -1 .. CRACK_ANIMATION_LENGTH-1 (-1 for off)
+       // Returns true if anything has been changed.
+       bool animate(bool faraway, float time, int crack, u32 daynight_ratio);
+
+       scene::SMesh* getMesh()
        {
-               PreMeshBuffer *p = NULL;
-               for(u32 i=0; i<m_prebuffers.size(); i++)
-               {
-                       PreMeshBuffer &pp = m_prebuffers[i];
-                       if(pp.material != material)
-                               continue;
-
-                       p = &pp;
-                       break;
-               }
-
-               if(p == NULL)
-               {
-                       PreMeshBuffer pp;
-                       pp.material = material;
-                       m_prebuffers.push_back(pp);
-                       p = &m_prebuffers[m_prebuffers.size()-1];
-               }
-
-               u32 vertex_count = p->vertices.size();
-               for(u32 i=0; i<numIndices; i++)
-               {
-                       u32 j = indices[i] + vertex_count;
-                       if(j > 65535)
-                       {
-                               dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
-                               // NOTE: Fix is to just add an another MeshBuffer
-                       }
-                       p->indices.push_back(j);
-               }
-               for(u32 i=0; i<numVertices; i++)
-               {
-                       p->vertices.push_back(vertices[i]);
-               }
+               return m_mesh;
        }
 
-       void fillMesh(scene::SMesh *mesh)
+       bool isAnimationForced() const
        {
-               /*dstream<<"Filling mesh with "<<m_prebuffers.size()
-                               <<" meshbuffers"<<std::endl;*/
-               for(u32 i=0; i<m_prebuffers.size(); i++)
-               {
-                       PreMeshBuffer &p = m_prebuffers[i];
-
-                       /*dstream<<"p.vertices.size()="<<p.vertices.size()
-                                       <<", p.indices.size()="<<p.indices.size()
-                                       <<std::endl;*/
-                       
-                       // Create meshbuffer
-                       
-                       // This is a "Standard MeshBuffer",
-                       // it's a typedeffed CMeshBuffer<video::S3DVertex>
-                       scene::SMeshBuffer *buf = new scene::SMeshBuffer();
-                       // Set material
-                       buf->Material = p.material;
-                       //((scene::SMeshBuffer*)buf)->Material = p.material;
-                       // Use VBO
-                       //buf->setHardwareMappingHint(scene::EHM_STATIC);
-                       // Add to mesh
-                       mesh->addMeshBuffer(buf);
-                       // Mesh grabbed it
-                       buf->drop();
-
-                       buf->append(p.vertices.pointer(), p.vertices.size(),
-                                       p.indices.pointer(), p.indices.size());
-               }
+               return m_animation_force_timer == 0;
+       }
+
+       void decreaseAnimationForceTimer()
+       {
+               if(m_animation_force_timer > 0)
+                       m_animation_force_timer--;
        }
 
 private:
-       core::array<PreMeshBuffer> m_prebuffers;
+       scene::SMesh *m_mesh;
+       IGameDef *m_gamedef;
+
+       // Must animate() be called before rendering?
+       bool m_has_animation;
+       int m_animation_force_timer;
+
+       // Animation info: cracks
+       // Last crack value passed to animate()
+       int m_last_crack;
+       // Maps mesh buffer (i.e. material) indices to base texture names
+       std::map<u32, std::string> m_crack_materials;
+
+       // Animation info: day/night transitions
+       // Last daynight_ratio value passed to animate()
+       u32 m_last_daynight_ratio;
+       // For each meshbuffer, maps vertex indices to (day,night) pairs
+       std::map<u32, std::map<u32, std::pair<u8, u8> > > m_daynight_diffs;
 };
 
-// Helper functions
-video::SColor MapBlock_LightColor(u8 alpha, u8 light);
-TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
-               NodeModMap *temp_mods, ITextureSource *tsrc, INodeDefManager *ndef);
 
-class MapBlock;
 
-struct MeshMakeData
+/*
+       This is used because CMeshBuffer::append() is very slow
+*/
+struct PreMeshBuffer
 {
-       u32 m_daynight_ratio;
-       NodeModMap m_temp_mods;
-       VoxelManipulator m_vmanip;
-       v3s16 m_blockpos;
-       
-       /*
-               Copy central data directly from block, and other data from
-               parent of block.
-       */
-       void fill(u32 daynight_ratio, MapBlock *block);
+       TileSpec tile;
+       core::array<u16> indices;
+       core::array<video::S3DVertex> vertices;
+};
 
-       /*
-               Set up with only a single node at (1,1,1)
-       */
-       void fillSingleNode(u32 daynight_ratio, MapNode *node);
+struct MeshCollector
+{
+       core::array<PreMeshBuffer> prebuffers;
+
+       void append(const TileSpec &material,
+                       const video::S3DVertex *vertices, u32 numVertices,
+                       const u16 *indices, u32 numIndices);
 };
 
-// This is the highest-level function in here
-scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef);
+// This encodes
+//   alpha in the A channel of the returned SColor
+//   day light (0-255) in the R channel of the returned SColor
+//   night light (0-255) in the G channel of the returned SColor
+inline video::SColor MapBlock_LightColor(u8 alpha, u16 light)
+{
+       return video::SColor(alpha, (light & 0xff), (light >> 8), 0);
+}
+
+// Compute light at node
+u16 getInteriorLight(MapNode n, s32 increment, MeshMakeData *data);
+u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, MeshMakeData *data);
+u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data);
+
+// Retrieves the TileSpec of a face of a node
+// Adds MATERIAL_FLAG_CRACK if the node is cracked
+TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data);
+TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data);
 
 #endif
 
diff --git a/src/mapblock_nodemod.h b/src/mapblock_nodemod.h
deleted file mode 100644 (file)
index 4952df1..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
-Minetest-c55
-Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#ifndef MAPBLOCK_NODEMOD_HEADER
-#define MAPBLOCK_NODEMOD_HEADER
-
-enum NodeModType
-{
-       NODEMOD_NONE,
-       NODEMOD_CHANGECONTENT, //param is content id
-       NODEMOD_CRACK // param is crack progression
-};
-
-struct NodeMod
-{
-       NodeMod(enum NodeModType a_type=NODEMOD_NONE, u16 a_param=0)
-       {
-               type = a_type;
-               param = a_param;
-       }
-       bool operator==(const NodeMod &other)
-       {
-               return (type == other.type && param == other.param);
-       }
-       enum NodeModType type;
-       u16 param;
-};
-
-class NodeModMap
-{
-public:
-       /*
-               returns true if the mod was different last time
-       */
-       bool set(v3s16 p, const NodeMod &mod)
-       {
-               // See if old is different, cancel if it is not different.
-               core::map<v3s16, NodeMod>::Node *n = m_mods.find(p);
-               if(n)
-               {
-                       NodeMod old = n->getValue();
-                       if(old == mod)
-                               return false;
-
-                       n->setValue(mod);
-               }
-               else
-               {
-                       m_mods.insert(p, mod);
-               }
-               
-               return true;
-       }
-       // Returns true if there was one
-       bool get(v3s16 p, NodeMod *mod)
-       {
-               core::map<v3s16, NodeMod>::Node *n;
-               n = m_mods.find(p);
-               if(n == NULL)
-                       return false;
-               if(mod)
-                       *mod = n->getValue();
-               return true;
-       }
-       bool clear(v3s16 p)
-       {
-               if(m_mods.find(p))
-               {
-                       m_mods.remove(p);
-                       return true;
-               }
-               return false;
-       }
-       bool clear()
-       {
-               if(m_mods.size() == 0)
-                       return false;
-               m_mods.clear();
-               return true;
-       }
-       void copy(NodeModMap &dest)
-       {
-               dest.m_mods.clear();
-
-               for(core::map<v3s16, NodeMod>::Iterator
-                               i = m_mods.getIterator();
-                               i.atEnd() == false; i++)
-               {
-                       dest.m_mods.insert(i.getNode()->getKey(), i.getNode()->getValue());
-               }
-       }
-
-private:
-       core::map<v3s16, NodeMod> m_mods;
-};
-
-#endif
-
index 6cb9671b51b998671a920cdfa24f801f1677043b..54be5d1d6c64d64d7c63f4cba76605c84b49799a 100644 (file)
@@ -353,115 +353,3 @@ void MapNode::deSerialize_pre22(u8 *source, u8 version)
        // Translate to our known version
        *this = mapnode_translate_to_internal(*this, version);
 }
-
-
-#ifndef SERVER
-
-/*
-       Nodes make a face if contents differ and solidness differs.
-       Return value:
-               0: No face
-               1: Face uses m1's content
-               2: Face uses m2's content
-       equivalent: Whether the blocks share the same face (eg. water and glass)
-
-       TODO: Add 3: Both faces drawn with backface culling, remove equivalent
-*/
-u8 face_contents(content_t m1, content_t m2, bool *equivalent,
-               INodeDefManager *nodemgr)
-{
-       *equivalent = false;
-
-       if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
-               return 0;
-       
-       bool contents_differ = (m1 != m2);
-       
-       const ContentFeatures &f1 = nodemgr->get(m1);
-       const ContentFeatures &f2 = nodemgr->get(m2);
-
-       // Contents don't differ for different forms of same liquid
-       if(f1.sameLiquid(f2))
-               contents_differ = false;
-       
-       u8 c1 = f1.solidness;
-       u8 c2 = f2.solidness;
-
-       bool solidness_differs = (c1 != c2);
-       bool makes_face = contents_differ && solidness_differs;
-
-       if(makes_face == false)
-               return 0;
-       
-       if(c1 == 0)
-               c1 = f1.visual_solidness;
-       if(c2 == 0)
-               c2 = f2.visual_solidness;
-       
-       if(c1 == c2){
-               *equivalent = true;
-               // If same solidness, liquid takes precense
-               if(f1.isLiquid())
-                       return 1;
-               if(f2.isLiquid())
-                       return 2;
-       }
-       
-       if(c1 > c2)
-               return 1;
-       else
-               return 2;
-}
-
-/*
-       Gets lighting value at face of node
-       
-       Parameters must consist of air and !air.
-       Order doesn't matter.
-
-       If either of the nodes doesn't exist, light is 0.
-       
-       parameters:
-               daynight_ratio: 0...1000
-               n: getNode(p) (uses only the lighting value)
-               n2: getNode(p + face_dir) (uses only the lighting value)
-               face_dir: axis oriented unit vector from p to p2
-       
-       returns encoded light value.
-*/
-u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
-               v3s16 face_dir, INodeDefManager *nodemgr)
-{
-       try{
-               u8 light;
-               u8 l1 = n.getLightBlend(daynight_ratio, nodemgr);
-               u8 l2 = n2.getLightBlend(daynight_ratio, nodemgr);
-               if(l1 > l2)
-                       light = l1;
-               else
-                       light = l2;
-
-               // Make some nice difference to different sides
-
-               // This makes light come from a corner
-               /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
-                       light = diminish_light(diminish_light(light));
-               else if(face_dir.X == -1 || face_dir.Z == -1)
-                       light = diminish_light(light);*/
-               
-               // All neighboring faces have different shade (like in minecraft)
-               if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
-                       light = diminish_light(diminish_light(light));
-               else if(face_dir.Z == 1 || face_dir.Z == -1)
-                       light = diminish_light(light);
-
-               return light;
-       }
-       catch(InvalidPositionException &e)
-       {
-               return 0;
-       }
-}
-
-#endif
-
index 5e066604bfc00ddc02bd23b7cd4b9739effb399d..c3ba9c6a35888bcc8102532fa0aa02d4b0412af7 100644 (file)
@@ -223,45 +223,5 @@ struct MapNode
        void deSerialize_pre22(u8 *source, u8 version);
 };
 
-
-/*
-       MapNode helpers for mesh making stuff
-*/
-
-#ifndef SERVER
-
-/*
-       Nodes make a face if contents differ and solidness differs.
-       Return value:
-               0: No face
-               1: Face uses m1's content
-               2: Face uses m2's content
-       equivalent: Whether the blocks share the same face (eg. water and glass)
-*/
-u8 face_contents(content_t m1, content_t m2, bool *equivalent,
-               INodeDefManager *nodemgr);
-
-/*
-       Gets lighting value at face of node
-       
-       Parameters must consist of air and !air.
-       Order doesn't matter.
-
-       If either of the nodes doesn't exist, light is 0.
-       
-       parameters:
-               daynight_ratio: 0...1000
-               n: getNode(p) (uses only the lighting value)
-               n2: getNode(p + face_dir) (uses only the lighting value)
-               face_dir: axis oriented unit vector from p to p2
-       
-       returns encoded light value.
-*/
-u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
-               v3s16 face_dir, INodeDefManager *nodemgr);
-
-#endif
-
-
 #endif
 
index f24497dd820def43d55ee1e8286813c0cf0bd1a3..e9abc323b123586bb4cf783175a63a7348e1d35d 100644 (file)
@@ -89,12 +89,6 @@ ContentFeatures::ContentFeatures()
 
 ContentFeatures::~ContentFeatures()
 {
-#ifndef SERVER
-       for(u16 j=0; j<CF_SPECIAL_COUNT; j++){
-               delete special_materials[j];
-               delete special_aps[j];
-       }
-#endif
 }
 
 void ContentFeatures::reset()
@@ -103,10 +97,6 @@ void ContentFeatures::reset()
                Cached stuff
        */
 #ifndef SERVER
-       for(u16 j=0; j<CF_SPECIAL_COUNT; j++){
-               special_materials[j] = NULL;
-               special_aps[j] = NULL;
-       }
        solidness = 2;
        visual_solidness = 0;
        backface_culling = true;
@@ -526,38 +516,21 @@ class CNodeDefManager: public IWritableNodeDefManager
                                        f->tiles[j].material_type = MATERIAL_ALPHA_SIMPLE;
                                else
                                        f->tiles[j].material_type = MATERIAL_ALPHA_VERTEX;
+                               f->tiles[j].material_flags = 0;
                                if(f->backface_culling)
                                        f->tiles[j].material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
-                               else
-                                       f->tiles[j].material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
                        }
-                       // Special textures
+                       // Special tiles
                        for(u16 j=0; j<CF_SPECIAL_COUNT; j++){
-                               // Remove all stuff
-                               if(f->special_aps[j]){
-                                       delete f->special_aps[j];
-                                       f->special_aps[j] = NULL;
-                               }
-                               if(f->special_materials[j]){
-                                       delete f->special_materials[j];
-                                       f->special_materials[j] = NULL;
-                               }
-                               // Skip if should not exist
-                               if(f->mspec_special[j].tname == "")
-                                       continue;
-                               // Create all stuff
-                               f->special_aps[j] = new AtlasPointer(
-                                               tsrc->getTexture(f->mspec_special[j].tname));
-                               f->special_materials[j] = new video::SMaterial;
-                               f->special_materials[j]->setFlag(video::EMF_LIGHTING, false);
-                               f->special_materials[j]->setFlag(video::EMF_BACK_FACE_CULLING,
-                                               f->mspec_special[j].backface_culling);
-                               f->special_materials[j]->setFlag(video::EMF_BILINEAR_FILTER, false);
-                               f->special_materials[j]->setFlag(video::EMF_FOG_ENABLE, true);
-                               f->special_materials[j]->setTexture(0, f->special_aps[j]->atlas);
-                               if(f->alpha != 255)
-                                       f->special_materials[j]->MaterialType =
-                                                       video::EMT_TRANSPARENT_VERTEX_ALPHA;
+                               f->special_tiles[j].texture = tsrc->getTexture(f->mspec_special[j].tname);
+                               f->special_tiles[j].alpha = f->alpha;
+                               if(f->alpha == 255)
+                                       f->special_tiles[j].material_type = MATERIAL_ALPHA_SIMPLE;
+                               else
+                                       f->special_tiles[j].material_type = MATERIAL_ALPHA_VERTEX;
+                               f->special_tiles[j].material_flags = 0;
+                               if(f->mspec_special[j].backface_culling)
+                                       f->special_tiles[j].material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
                        }
                }
 #endif
index c875e0b752ef602a7c3412c37f54a50edd786a53..2842b81e727204c606950c000ed92e73a5c87a05 100644 (file)
@@ -135,10 +135,9 @@ struct ContentFeatures
        // 0     1     2     3     4     5
        // up    down  right left  back  front 
        TileSpec tiles[6];
-       // Special material/texture
+       // Special tiles
        // - Currently used for flowing liquids
-       video::SMaterial *special_materials[CF_SPECIAL_COUNT];
-       AtlasPointer *special_aps[CF_SPECIAL_COUNT];
+       TileSpec special_tiles[CF_SPECIAL_COUNT];
        u8 solidness; // Used when choosing which face is drawn
        u8 visual_solidness; // When solidness=0, this tells how it looks like
        bool backface_culling;
index 5ef469944fe4c36b6af35f8b70111d6c10d36a9c..fc371c9417ae16c949a462afa03de283c079fbeb 100644 (file)
@@ -279,12 +279,14 @@ class TextureSource : public IWritableTextureSource
                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),
+               - Now getNodeTile() stumbles upon a node which uses
+                 texture id 1, and determines that MATERIAL_FLAG_CRACK
+                 must be applied to the tile
+               - MapBlockMesh::animate() finds the MATERIAL_FLAG_CRACK and
+                 has received the current crack level 0 from the client. It
+                 finds out the name of the texture with getTextureName(1),
                  appends "^crack0" to it and gets a new texture id with
-                 getTextureId("stone.png^mineral1^crack0")
+                 getTextureId("stone.png^mineral1^crack0").
 
        */
        
@@ -337,6 +339,12 @@ class TextureSource : public IWritableTextureSource
                return ap.atlas;
        }
 
+       // Gets a separate texture atlas pointer
+       AtlasPointer getTextureRawAP(const AtlasPointer &ap)
+       {
+               return getTexture(getTextureName(ap.id) + "^[forcesingle");
+       }
+
        // Returns a pointer to the irrlicht device
        virtual IrrlichtDevice* getDevice()
        {
@@ -475,6 +483,9 @@ u32 TextureSource::getTextureId(const std::string &name)
        return 0;
 }
 
+// Overlay image on top of another image (used for cracks)
+void overlay(video::IImage *image, video::IImage *overlay);
+
 // Brighten image
 void brighten(video::IImage *image);
 
@@ -1183,8 +1194,19 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                                return false;
                        }
                        
-                       // Crack image number
-                       u16 progression = stoi(part_of_name.substr(6));
+                       // Crack image number and overlay option
+                       s32 progression = 0;
+                       bool use_overlay = false;
+                       if(part_of_name.substr(6,1) == "o")
+                       {
+                               progression = stoi(part_of_name.substr(7));
+                               use_overlay = true;
+                       }
+                       else
+                       {
+                               progression = stoi(part_of_name.substr(6));
+                               use_overlay = false;
+                       }
 
                        // Size of the base image
                        core::dimension2d<u32> dim_base = baseimg->getDimension();
@@ -1197,65 +1219,56 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
                        */
                        video::IImage *img_crack = sourcecache->getOrLoad("crack.png", device);
                
-                       if(img_crack)
+                       if(img_crack && progression >= 0)
                        {
                                // Dimension of original image
                                core::dimension2d<u32> dim_crack
                                                = img_crack->getDimension();
                                // Count of crack stages
-                               u32 crack_count = dim_crack.Height / dim_crack.Width;
+                               s32 crack_count = dim_crack.Height / dim_crack.Width;
                                // Limit progression
                                if(progression > crack_count-1)
                                        progression = crack_count-1;
-                               // Dimension of a single scaled crack stage
-                               core::dimension2d<u32> dim_crack_scaled_single(
-                                       dim_base.Width,
-                                       dim_base.Height
+                               // Dimension of a single crack stage
+                               core::dimension2d<u32> dim_crack_cropped(
+                                       dim_crack.Width,
+                                       dim_crack.Width
                                );
-                               // Dimension of scaled size
-                               core::dimension2d<u32> dim_crack_scaled(
-                                       dim_crack_scaled_single.Width,
-                                       dim_crack_scaled_single.Height * crack_count
-                               );
-                               // Create scaled crack image
+                               // Create cropped and scaled crack images
+                               video::IImage *img_crack_cropped = driver->createImage(
+                                               video::ECF_A8R8G8B8, dim_crack_cropped);
                                video::IImage *img_crack_scaled = driver->createImage(
-                                               video::ECF_A8R8G8B8, dim_crack_scaled);
-                               if(img_crack_scaled)
+                                               video::ECF_A8R8G8B8, dim_base);
+
+                               if(img_crack_cropped && img_crack_scaled)
                                {
+                                       // Crop crack image
+                                       v2s32 pos_crack(0, progression*dim_crack.Width);
+                                       img_crack->copyTo(img_crack_cropped,
+                                                       v2s32(0,0),
+                                                       core::rect<s32>(pos_crack, dim_crack_cropped));
                                        // Scale crack image by copying
-                                       img_crack->copyToScaling(img_crack_scaled);
-                                       
-                                       // Position to copy the crack from
-                                       core::position2d<s32> pos_crack_scaled(
-                                               0,
-                                               dim_crack_scaled_single.Height * progression
-                                       );
-                                       
-                                       // This tiling does nothing currently but is useful
-                                       for(u32 y0=0; y0<dim_base.Height
-                                                       / dim_crack_scaled_single.Height; y0++)
-                                       for(u32 x0=0; x0<dim_base.Width
-                                                       / dim_crack_scaled_single.Width; x0++)
+                                       img_crack_cropped->copyToScaling(img_crack_scaled);
+                                       // Copy or overlay crack image
+                                       if(use_overlay)
+                                       {
+                                               overlay(baseimg, img_crack_scaled);
+                                       }
+                                       else
                                        {
-                                               // Position to copy the crack to in the base image
-                                               core::position2d<s32> pos_base(
-                                                       x0*dim_crack_scaled_single.Width,
-                                                       y0*dim_crack_scaled_single.Height
-                                               );
-                                               // Rectangle to copy the crack from on the scaled image
-                                               core::rect<s32> rect_crack_scaled(
-                                                       pos_crack_scaled,
-                                                       dim_crack_scaled_single
-                                               );
-                                               // Copy it
-                                               img_crack_scaled->copyToWithAlpha(baseimg, pos_base,
-                                                               rect_crack_scaled,
-                                                               video::SColor(255,255,255,255),
-                                                               NULL);
+                                               img_crack_scaled->copyToWithAlpha(
+                                                               baseimg,
+                                                               v2s32(0,0),
+                                                               core::rect<s32>(v2s32(0,0), dim_base),
+                                                               video::SColor(255,255,255,255));
                                        }
+                               }
 
+                               if(img_crack_scaled)
                                        img_crack_scaled->drop();
-                               }
+
+                               if(img_crack_cropped)
+                                       img_crack_cropped->drop();
                                
                                img_crack->drop();
                        }
@@ -1511,6 +1524,37 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
        return true;
 }
 
+void overlay(video::IImage *image, video::IImage *overlay)
+{
+       /*
+               Copy overlay to image, taking alpha into account.
+               Where image is transparent, don't copy from overlay.
+               Images sizes must be identical.
+       */
+       if(image == NULL || overlay == NULL)
+               return;
+       
+       core::dimension2d<u32> dim = image->getDimension();
+       core::dimension2d<u32> dim_overlay = overlay->getDimension();
+       assert(dim == dim_overlay);
+
+       for(u32 y=0; y<dim.Height; y++)
+       for(u32 x=0; x<dim.Width; x++)
+       {
+               video::SColor c1 = image->getPixel(x,y);
+               video::SColor c2 = overlay->getPixel(x,y);
+               u32 a1 = c1.getAlpha();
+               u32 a2 = c2.getAlpha();
+               if(a1 == 255 && a2 != 0)
+               {
+                       c1.setRed((c1.getRed()*(255-a2) + c2.getRed()*a2)/255);
+                       c1.setGreen((c1.getGreen()*(255-a2) + c2.getGreen()*a2)/255);
+                       c1.setBlue((c1.getBlue()*(255-a2) + c2.getBlue()*a2)/255);
+               }
+               image->setPixel(x,y,c1);
+       }
+}
+
 void brighten(video::IImage *image)
 {
        if(image == NULL)
index c0d8914b09b6c09d288dc045f8caa9271588d0da..698ce3dcfbcc0f0390f113d22d135a236c0b5a55 100644 (file)
@@ -73,7 +73,7 @@ struct AtlasPointer
        {
        }
 
-       bool operator==(const AtlasPointer &other)
+       bool operator==(const AtlasPointer &other) const
        {
                return (
                        id == other.id
@@ -87,6 +87,11 @@ struct AtlasPointer
                );*/
        }
 
+       bool operator!=(const AtlasPointer &other) const
+       {
+               return !(*this == other);
+       }
+
        float x0(){ return pos.X; }
        float x1(){ return pos.X + size.X; }
        float y0(){ return pos.Y; }
@@ -110,6 +115,8 @@ class ITextureSource
                {return AtlasPointer(0);}
        virtual video::ITexture* getTextureRaw(const std::string &name)
                {return NULL;}
+       virtual AtlasPointer getTextureRawAP(const AtlasPointer &ap)
+               {return AtlasPointer(0);}
        virtual IrrlichtDevice* getDevice()
                {return NULL;}
        virtual void updateAP(AtlasPointer &ap){};
@@ -148,7 +155,13 @@ enum MaterialType{
 };
 
 // Material flags
+// Should backface culling be enabled?
 #define MATERIAL_FLAG_BACKFACE_CULLING 0x01
+// Should a crack be drawn?
+#define MATERIAL_FLAG_CRACK 0x02
+// Should the crack be drawn on transparent pixels (unset) or not (set)?
+// Ignored if MATERIAL_FLAG_CRACK is not set.
+#define MATERIAL_FLAG_CRACK_OVERLAY 0x04
 
 /*
        This fully defines the looks of a tile.
@@ -169,7 +182,7 @@ struct TileSpec
        {
        }
 
-       bool operator==(TileSpec &other)
+       bool operator==(const TileSpec &other) const
        {
                return (
                        texture == other.texture &&
@@ -178,6 +191,11 @@ struct TileSpec
                        material_flags == other.material_flags
                );
        }
+
+       bool operator!=(const TileSpec &other) const
+       {
+               return !(*this == other);
+       }
        
        // Sets everything else except the texture in the material
        void applyMaterialOptions(video::SMaterial &material) const
index aa64c28bb601ceb3be158917d939c461c60a89ed..15c01373561c8320202eb7a4d924cec876b9bb77 100644 (file)
@@ -1753,18 +1753,17 @@ std::string deSerializeJsonString(std::istream &is);
 
 inline u32 time_to_daynight_ratio(u32 time_of_day)
 {
-       const s32 daylength = 16;
-       const s32 nightlength = 6;
-       const s32 daytimelength = 8;
-       s32 d = daylength;
-       s32 t = (((time_of_day)%24000)/(24000/d));
-       if(t < nightlength/2 || t >= d - nightlength/2)
-               //return 300;
+       s32 t = time_of_day%24000;
+       if(t < 4500 || t >= 19500)
+               return 150;
+       else if(t < 5000 || t >= 19000)
                return 350;
-       else if(t >= d/2 - daytimelength/2 && t < d/2 + daytimelength/2)
-               return 1000;
-       else
+       else if(t < 5500 || t >= 18500)
+               return 500;
+       else if(t < 6000 || t >= 18000)
                return 750;
+       else
+               return 1000;
 }
 
 // Random helper. Usually d=BS