cube inventory texture rendering thing (not good yet)
[oweals/minetest.git] / src / mapblock.cpp
index 4ba597f6a215b68bec33c996067cdc71b43d7ebb..63f33ffc0d2d0cedb4a3264a728ced5a70946c3b 100644 (file)
@@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "mapblock.h"
 #include "map.h"
-// For g_materials
+// For g_settings and g_irrlicht
 #include "main.h"
 #include "light.h"
 #include <sstream>
@@ -29,6 +29,46 @@ with this program; if not, write to the Free Software Foundation, Inc.,
        MapBlock
 */
 
+MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
+               m_parent(parent),
+               m_pos(pos),
+               changed(true),
+               is_underground(false),
+               m_day_night_differs(false),
+               m_objects(this)
+{
+       data = NULL;
+       if(dummy == false)
+               reallocate();
+       
+       m_spawn_timer = -10000;
+
+#ifndef SERVER
+       m_mesh_expired = false;
+       mesh_mutex.Init();
+       mesh = NULL;
+       m_temp_mods_mutex.Init();
+#endif
+}
+
+MapBlock::~MapBlock()
+{
+#ifndef SERVER
+       {
+               JMutexAutoLock lock(mesh_mutex);
+               
+               if(mesh)
+               {
+                       mesh->drop();
+                       mesh = NULL;
+               }
+       }
+#endif
+
+       if(data)
+               delete[] data;
+}
+
 bool MapBlock::isValidPositionParent(v3s16 p)
 {
        if(isValidPosition(p))
@@ -68,10 +108,83 @@ void MapBlock::setNodeParent(v3s16 p, MapNode & n)
        }
 }
 
-FastFace * MapBlock::makeFastFace(u16 tile, u8 light, v3f p,
-               v3f dir, v3f scale, v3f posRelative_f)
+MapNode MapBlock::getNodeParentNoEx(v3s16 p)
+{
+       if(isValidPosition(p) == false)
+       {
+               try{
+                       return m_parent->getNode(getPosRelative() + p);
+               }
+               catch(InvalidPositionException &e)
+               {
+                       return MapNode(CONTENT_IGNORE);
+               }
+       }
+       else
+       {
+               if(data == NULL)
+               {
+                       return MapNode(CONTENT_IGNORE);
+               }
+               return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
+       }
+}
+
+/*
+       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: getNodeParent(p)
+               n2: getNodeParent(p + face_dir)
+               face_dir: axis oriented unit vector from p to p2
+       
+       returns encoded light value.
+*/
+u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
+               v3s16 face_dir)
+{
+       try{
+               u8 light;
+               u8 l1 = n.getLightBlend(daynight_ratio);
+               u8 l2 = n2.getLightBlend(daynight_ratio);
+               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;
+       }
+}
+
+#ifndef SERVER
+
+void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
+               v3s16 dir, v3f scale, v3f posRelative_f,
+               core::array<FastFace> &dest)
 {
-       FastFace *f = new FastFace;
+       FastFace face;
        
        // Position is at the center of the cube.
        v3f pos = p * BS;
@@ -80,19 +193,44 @@ FastFace * MapBlock::makeFastFace(u16 tile, u8 light, v3f p,
        v3f vertex_pos[4];
        // If looking towards z+, this is the face that is behind
        // the center point, facing towards z+.
-       vertex_pos[0] = v3f( BS/2,-BS/2,BS/2);
-       vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2);
-       vertex_pos[2] = v3f(-BS/2, BS/2,BS/2);
-       vertex_pos[3] = v3f( BS/2, BS/2,BS/2);
+       vertex_pos[0] = v3f(-BS/2,-BS/2,BS/2);
+       vertex_pos[1] = v3f( BS/2,-BS/2,BS/2);
+       vertex_pos[2] = v3f( BS/2, BS/2,BS/2);
+       vertex_pos[3] = v3f(-BS/2, BS/2,BS/2);
        
-       /*
-               TODO: Rotate it the right way (one side comes upside down)
-       */
-       core::CMatrix4<f32> m;
-       m.buildRotateFromTo(v3f(0,0,1), dir);
-       
-       for(u16 i=0; i<4; i++){
-               m.rotateVect(vertex_pos[i]);
+       if(dir == v3s16(0,0,1))
+       {
+               for(u16 i=0; i<4; i++)
+                       vertex_pos[i].rotateXZBy(0);
+       }
+       else if(dir == v3s16(0,0,-1))
+       {
+               for(u16 i=0; i<4; i++)
+                       vertex_pos[i].rotateXZBy(180);
+       }
+       else if(dir == v3s16(1,0,0))
+       {
+               for(u16 i=0; i<4; i++)
+                       vertex_pos[i].rotateXZBy(-90);
+       }
+       else if(dir == v3s16(-1,0,0))
+       {
+               for(u16 i=0; i<4; i++)
+                       vertex_pos[i].rotateXZBy(90);
+       }
+       else if(dir == v3s16(0,1,0))
+       {
+               for(u16 i=0; i<4; i++)
+                       vertex_pos[i].rotateYZBy(-90);
+       }
+       else if(dir == v3s16(0,-1,0))
+       {
+               for(u16 i=0; i<4; i++)
+                       vertex_pos[i].rotateYZBy(90);
+       }
+
+       for(u16 i=0; i<4; i++)
+       {
                vertex_pos[i].X *= scale.X;
                vertex_pos[i].Y *= scale.Y;
                vertex_pos[i].Z *= scale.Z;
@@ -106,98 +244,145 @@ FastFace * MapBlock::makeFastFace(u16 tile, u8 light, v3f p,
 
        v3f zerovector = v3f(0,0,0);
        
-       u8 li = decode_light(light);
-       //u8 li = 150;
+       //u8 li = decode_light(light);
+       u8 li = light;
+       //u8 li = 255; //DEBUG
 
-       u8 alpha = 255;
-
-       //if(material == CONTENT_WATER || material == CONTENT_OCEAN)
-       if(tile == CONTENT_WATER || tile == CONTENT_OCEAN)
-       //if(tile == TILE_WATER)
-       {
-               alpha = 128;
-       }
+       u8 alpha = tile.alpha;
+       /*u8 alpha = 255;
+       if(tile.id == TILE_WATER)
+               alpha = WATER_ALPHA;*/
 
        video::SColor c = video::SColor(alpha,li,li,li);
 
-       /*f->vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
-                       core::vector2d<f32>(0,1));
-       f->vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
+       face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
                        core::vector2d<f32>(abs_scale,1));
-       f->vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
-                       core::vector2d<f32>(abs_scale,0));
-       f->vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
-                       core::vector2d<f32>(0,0));*/
-       f->vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
+       face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
                        core::vector2d<f32>(0,1));
-       f->vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
-                       core::vector2d<f32>(abs_scale,1));
-       f->vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
-                       core::vector2d<f32>(abs_scale,0));
-       f->vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
+       face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
                        core::vector2d<f32>(0,0));
-
-       f->tile = tile;
-
-       return f;
+       face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
+                       core::vector2d<f32>(abs_scale,0));
+       
+       /*float x0 = (float)tile.tx/256.0;
+       float y0 = (float)tile.ty/256.0;
+       float w = ((float)tile.tw + 1.0)/256.0;
+       float h = ((float)tile.th + 1.0)/256.0;*/
+
+       float x0 = tile.texture.pos.X;
+       float y0 = tile.texture.pos.Y;
+       float w = tile.texture.size.X;
+       float h = tile.texture.size.Y;
+
+       face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
+                       core::vector2d<f32>(x0+w*abs_scale, y0+h));
+       face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
+                       core::vector2d<f32>(x0, y0+h));
+       face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
+                       core::vector2d<f32>(x0, y0));
+       face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
+                       core::vector2d<f32>(x0+w*abs_scale, y0));
+
+       face.tile = tile;
+       //DEBUG
+       //f->tile = TILE_STONE;
+       
+       dest.push_back(face);
+       //return f;
 }
        
 /*
-       Parameters must consist of air and !air.
-       Order doesn't matter.
-
-       If either of the nodes doesn't exist, light is 0.
+       Gets node tile from any place relative to block.
+       Returns TILE_NODE if doesn't exist or should not be drawn.
 */
-u8 MapBlock::getFaceLight(v3s16 p, v3s16 face_dir)
+TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
+               NodeModMap &temp_mods)
 {
-       try{
-               MapNode n = getNodeParent(p);
-               MapNode n2 = getNodeParent(p + face_dir);
-               u8 light;
-               /*if(n.solidness() < n2.solidness())
-                       light = n.getLight();
-               else
-                       light = n2.getLight();*/
-               if(n.getLight() > n2.getLight())
-                       light = n.getLight();
-               else
-                       light = n2.getLight();
-
-               // Make some nice difference to different sides
+       TileSpec spec;
+       spec = mn.getTile(face_dir);
+       
+       /*
+               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.get(p, &mod))
+       {
+               if(mod.type == NODEMOD_CHANGECONTENT)
+               {
+                       MapNode mn2(mod.param);
+                       spec = mn2.getTile(face_dir);
+               }
+               if(mod.type == NODEMOD_CRACK)
+               {
+                       /*
+                               Get texture id, translate it to name, append stuff to
+                               name, get texture id
+                       */
 
-               /*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);*/
+                       // Get original texture name
+                       u32 orig_id = spec.texture.id;
+                       std::string orig_name = g_texturesource->getTextureName(orig_id);
 
-               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);
+                       // Create new texture name
+                       std::ostringstream os;
+                       os<<orig_name<<"^[crack"<<mod.param;
 
-               return light;
-       }
-       catch(InvalidPositionException &e)
-       {
-               return 0;
+                       // Get new texture
+                       u32 new_id = g_texturesource->getTextureId(os.str());
+                       
+                       /*dstream<<"MapBlock::getNodeTile(): Switching from "
+                                       <<orig_name<<" to "<<os.str()<<" ("
+                                       <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
+                       
+                       spec.texture = g_texturesource->getTexture(new_id);
+               }
        }
+       
+       return spec;
 }
 
-/*
-       Gets node material from any place relative to block.
-       Returns CONTENT_IGNORE if doesn't exist or should not be drawn.
-*/
-u8 MapBlock::getNodeTile(v3s16 p)
+u8 MapBlock::getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
 {
-       try{
-               MapNode n = getNodeParent(p);
-               
-               return content_tile(n.d);
-       }
-       catch(InvalidPositionException &e)
+       /*
+               Check temporary modifications on this node
+       */
+       /*core::map<v3s16, NodeMod>::Node *n;
+       n = m_temp_mods.find(p);
+       // If modified
+       if(n != NULL)
        {
-               return CONTENT_IGNORE;
+               struct NodeMod mod = n->getValue();*/
+       NodeMod mod;
+       if(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.
+                       */
+               }
        }
+
+       return mn.d;
 }
 
 /*
@@ -205,48 +390,51 @@ u8 MapBlock::getNodeTile(v3s16 p)
        translate_dir: unit vector with only one of x, y or z
        face_dir: unit vector with only one of x, y or z
 */
-void MapBlock::updateFastFaceRow(v3s16 startpos,
+void MapBlock::updateFastFaceRow(
+               u32 daynight_ratio,
+               v3f posRelative_f,
+               v3s16 startpos,
                u16 length,
                v3s16 translate_dir,
+               v3f translate_dir_f,
                v3s16 face_dir,
-               core::list<FastFace*> &dest)
+               v3f face_dir_f,
+               core::array<FastFace> &dest,
+               NodeModMap &temp_mods)
 {
-       /*
-               Precalculate some variables
-       */
-       v3f translate_dir_f(translate_dir.X, translate_dir.Y,
-                       translate_dir.Z); // floating point conversion
-       v3f face_dir_f(face_dir.X, face_dir.Y,
-                       face_dir.Z); // floating point conversion
-       v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
-                       getPosRelative().Z); // floating point conversion
-
        v3s16 p = startpos;
-       /*
-               The light in the air lights the surface is taken from
-               the node that is air.
-       */
-       u8 light = getFaceLight(p, face_dir);
        
        u16 continuous_tiles_count = 0;
        
-       u8 tile0 = getNodeTile(p);
-       u8 tile1 = getNodeTile(p + face_dir);
+       MapNode n0 = getNodeParentNoEx(p);
+       MapNode n1 = getNodeParentNoEx(p + face_dir);
+
+       u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
                
+       TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
+       TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
+
        for(u16 j=0; j<length; j++)
        {
                bool next_is_different = true;
                
                v3s16 p_next;
-               u8 tile0_next = 0;
-               u8 tile1_next = 0;
+               MapNode n0_next;
+               MapNode n1_next;
+               TileSpec tile0_next;
+               TileSpec tile1_next;
                u8 light_next = 0;
-
-               if(j != length - 1){
+               
+               // If at last position, there is nothing to compare to and
+               // the face must be drawn anyway
+               if(j != length - 1)
+               {
                        p_next = p + translate_dir;
-                       tile0_next = getNodeTile(p_next);
-                       tile1_next = getNodeTile(p_next + face_dir);
-                       light_next = getFaceLight(p_next, face_dir);
+                       n0_next = getNodeParentNoEx(p_next);
+                       n1_next = getNodeParentNoEx(p_next + face_dir);
+                       tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
+                       tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
+                       light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
 
                        if(tile0_next == tile0
                                        && tile1_next == tile1
@@ -258,12 +446,40 @@ void MapBlock::updateFastFaceRow(v3s16 startpos,
 
                continuous_tiles_count++;
                
-               if(next_is_different)
+               // This is set to true if the texture doesn't allow more tiling
+               bool end_of_texture = false;
+               /*
+                       If there is no texture, it can be tiled infinitely.
+                       If tiled==0, it means the texture can be tiled infinitely.
+                       Otherwise check tiled agains continuous_tiles_count.
+
+                       This check has to be made for both tiles, because this is
+                       a bit hackish and we know which one we're using only when
+                       the decision to make the faces is made.
+               */
+               if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
+               {
+                       if(tile0.texture.tiled <= continuous_tiles_count)
+                               end_of_texture = true;
+               }
+               if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
+               {
+                       if(tile1.texture.tiled <= continuous_tiles_count)
+                               end_of_texture = true;
+               }
+               
+               //end_of_texture = true; //DEBUG
+               
+               if(next_is_different || end_of_texture)
                {
                        /*
                                Create a face if there should be one
                        */
-                       u8 mf = face_contents(tile0, tile1);
+                       //u8 mf = face_contents(tile0, tile1);
+                       // This is hackish
+                       u8 content0 = getNodeContent(p, n0, temp_mods);
+                       u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
+                       u8 mf = face_contents(content0, content1);
                        
                        if(mf != 0)
                        {
@@ -282,26 +498,28 @@ void MapBlock::updateFastFaceRow(v3s16 startpos,
                                        scale.Z = continuous_tiles_count;
                                }
                                
-                               FastFace *f;
+                               //FastFace *f;
 
                                // If node at sp (tile0) is more solid
                                if(mf == 1)
                                {
-                                       f = makeFastFace(tile0, light,
-                                                       sp, face_dir_f, scale,
-                                                       posRelative_f);
+                                       makeFastFace(tile0, decode_light(light),
+                                                       sp, face_dir, scale,
+                                                       posRelative_f, dest);
                                }
                                // If node at sp is less solid (mf == 2)
                                else
                                {
-                                       f = makeFastFace(tile1, light,
-                                                       sp+face_dir_f, -1*face_dir_f, scale,
-                                                       posRelative_f);
+                                       makeFastFace(tile1, decode_light(light),
+                                                       sp+face_dir_f, -face_dir, scale,
+                                                       posRelative_f, dest);
                                }
-                               dest.push_back(f);
+                               //dest.push_back(f);
                        }
 
                        continuous_tiles_count = 0;
+                       n0 = n0_next;
+                       n1 = n1_next;
                        tile0 = tile0_next;
                        tile1 = tile1_next;
                        light = light_next;
@@ -404,194 +622,640 @@ private:
        core::array<PreMeshBuffer> m_prebuffers;
 };
 
-void MapBlock::updateMesh()
+void MapBlock::updateMesh(u32 daynight_ratio)
 {
-       /*v3s16 p = getPosRelative();
-       std::cout<<"MapBlock("<<p.X<<","<<p.Y<<","<<p.Z<<")"
-                       <<"::updateMesh(): ";*/
-                       //<<"::updateMesh()"<<std::endl;
-       
+#if 0
        /*
-               TODO: Change this to directly generate the mesh (and get rid
-                     of FastFaces)
+               DEBUG: If mesh has been generated, don't generate it again
        */
+       {
+               JMutexAutoLock meshlock(mesh_mutex);
+               if(mesh != NULL)
+                       return;
+       }
+#endif
+       
+       // 4-21ms for MAP_BLOCKSIZE=16
+       // 24-155ms for MAP_BLOCKSIZE=32
+       //TimeTaker timer1("updateMesh()");
 
-       core::list<FastFace*> *fastfaces_new = new core::list<FastFace*>;
+       core::array<FastFace> fastfaces_new;
+       
+       v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
+                       getPosRelative().Z); // floating point conversion
+       
+       /*
+               Avoid interlocks by copying m_temp_mods
+       */
+       NodeModMap temp_mods;
+       {
+               JMutexAutoLock lock(m_temp_mods_mutex);
+               m_temp_mods.copy(temp_mods);
+       }
+       
+       /*
+               Some settings
+       */
+       bool new_style_water = g_settings.getBool("new_style_water");
+       bool new_style_leaves = g_settings.getBool("new_style_leaves");
+       
+       float node_water_level = 1.0;
+       if(new_style_water)
+               node_water_level = 0.85;
        
        /*
                We are including the faces of the trailing edges of the block.
                This means that when something changes, the caller must
                also update the meshes of the blocks at the leading edges.
-       */
 
-       /*
-               Go through every y,z and get top faces in rows of x+
+               NOTE: This is the slowest part of this method.
        */
-       for(s16 y=0; y<MAP_BLOCKSIZE; y++){
-       //for(s16 y=-1; y<MAP_BLOCKSIZE; y++){
+       
+       {
+               // 4-23ms for MAP_BLOCKSIZE=16
+               //TimeTaker timer2("updateMesh() collect");
+
+               /*
+                       Go through every y,z and get top faces in rows of x+
+               */
+               for(s16 y=0; y<MAP_BLOCKSIZE; y++){
+                       for(s16 z=0; z<MAP_BLOCKSIZE; z++){
+                               updateFastFaceRow(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,
+                                               temp_mods);
+                       }
+               }
+               /*
+                       Go through every x,y and get right faces in rows of z+
+               */
+               for(s16 x=0; x<MAP_BLOCKSIZE; x++){
+                       for(s16 y=0; y<MAP_BLOCKSIZE; y++){
+                               updateFastFaceRow(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,
+                                               temp_mods);
+                       }
+               }
+               /*
+                       Go through every y,z and get back faces in rows of x+
+               */
                for(s16 z=0; z<MAP_BLOCKSIZE; z++){
-                       updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
-                                       v3s16(1,0,0),
-                                       v3s16(0,1,0),
-                                       *fastfaces_new);
+                       for(s16 y=0; y<MAP_BLOCKSIZE; y++){
+                               updateFastFaceRow(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,
+                                               temp_mods);
+                       }
                }
        }
+
+       // End of slow part
+
        /*
-               Go through every x,y and get right faces in rows of z+
+               Convert FastFaces to SMesh
        */
-       for(s16 x=0; x<MAP_BLOCKSIZE; x++){
-       //for(s16 x=-1; x<MAP_BLOCKSIZE; x++){
-               for(s16 y=0; y<MAP_BLOCKSIZE; y++){
-                       updateFastFaceRow(v3s16(x,y,0), MAP_BLOCKSIZE,
-                                       v3s16(0,0,1),
-                                       v3s16(1,0,0),
-                                       *fastfaces_new);
+
+       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.Lighting = false;
+               material.BackfaceCulling = false;
+               material.setFlag(video::EMF_BILINEAR_FILTER, false);
+               //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
+               //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
+               material.setFlag(video::EMF_FOG_ENABLE, true);
+
+               for(u32 i=0; i<fastfaces_new.size(); i++)
+               {
+                       FastFace &f = fastfaces_new[i];
+
+                       const u16 indices[] = {0,1,2,2,3,0};
+                       
+                       //video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
+                       video::ITexture *texture = f.tile.texture.atlas;
+                       if(texture == NULL)
+                               continue;
+
+                       material.setTexture(0, texture);
+                       
+                       f.tile.applyMaterialOptions(material);
+                       
+                       collector.append(material, f.vertices, 4, indices, 6);
                }
        }
+
        /*
-               Go through every y,z and get back faces in rows of x+
+               Add special graphics:
+               - torches
+               - flowing water
        */
-       for(s16 z=0; z<MAP_BLOCKSIZE; z++){
-       //for(s16 z=-1; z<MAP_BLOCKSIZE; z++){
-               for(s16 y=0; y<MAP_BLOCKSIZE; y++){
-                       updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
-                                       v3s16(1,0,0),
-                                       v3s16(0,0,1),
-                                       *fastfaces_new);
-               }
-       }
 
-       scene::SMesh *mesh_new = NULL;
-       
-       mesh_new = new scene::SMesh();
-       
-       if(fastfaces_new->getSize() > 0)
-       {
-               MeshCollector collector;
+       // 0ms
+       //TimeTaker timer2("updateMesh() adding special stuff");
+
+       // Flowing water material
+       video::SMaterial material_water1;
+       material_water1.setFlag(video::EMF_LIGHTING, false);
+       //material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
+       material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
+       material_water1.setFlag(video::EMF_FOG_ENABLE, true);
+       material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
+       //TODO
+       //material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
+       AtlasPointer pa_water1 = g_texturesource->getTexture(
+                       g_texturesource->getTextureId("water.png"));
+       material_water1.setTexture(0, pa_water1.atlas);
+
+       // New-style leaves material
+       video::SMaterial material_leaves1;
+       material_leaves1.setFlag(video::EMF_LIGHTING, false);
+       //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, 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;
+       //TODO
+       //material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
+       AtlasPointer pa_leaves1 = g_texturesource->getTexture(
+                       g_texturesource->getTextureId("leaves.png"));
+       material_leaves1.setTexture(0, pa_leaves1.atlas);
 
-               core::list<FastFace*>::Iterator i = fastfaces_new->begin();
+       for(s16 z=0; z<MAP_BLOCKSIZE; z++)
+       for(s16 y=0; y<MAP_BLOCKSIZE; y++)
+       for(s16 x=0; x<MAP_BLOCKSIZE; x++)
+       {
+               v3s16 p(x,y,z);
 
-               for(; i != fastfaces_new->end(); i++)
+               MapNode &n = getNodeRef(x,y,z);
+               
+               /*
+                       Add torches to mesh
+               */
+               if(n.d == CONTENT_TORCH)
                {
-                       FastFace *f = *i;
+                       video::SColor c(255,255,255,255);
 
-                       const u16 indices[] = {0,1,2,2,3,0};
+                       video::S3DVertex vertices[4] =
+                       {
+                               video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
+                               video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
+                               video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
+                               video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
+                       };
+
+                       v3s16 dir = unpackDir(n.dir);
 
-                       /*collector.append(g_materials[f->material], f->vertices, 4,
-                                       indices, 6);*/
-                       collector.append(g_materials[f->tile], f->vertices, 4,
-                                       indices, 6);
+                       for(s32 i=0; i<4; i++)
+                       {
+                               if(dir == v3s16(1,0,0))
+                                       vertices[i].Pos.rotateXZBy(0);
+                               if(dir == v3s16(-1,0,0))
+                                       vertices[i].Pos.rotateXZBy(180);
+                               if(dir == v3s16(0,0,1))
+                                       vertices[i].Pos.rotateXZBy(90);
+                               if(dir == v3s16(0,0,-1))
+                                       vertices[i].Pos.rotateXZBy(-90);
+                               if(dir == v3s16(0,-1,0))
+                                       vertices[i].Pos.rotateXZBy(45);
+                               if(dir == v3s16(0,1,0))
+                                       vertices[i].Pos.rotateXZBy(-45);
+
+                               vertices[i].Pos += intToFloat(p + getPosRelative());
+                       }
+
+                       // 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.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+                       material.MaterialType
+                                       = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+
+                       if(dir == v3s16(0,-1,0))
+                               material.setTexture(0,
+                                               g_texturesource->getTextureRaw("torch_on_floor.png"));
+                       else if(dir == v3s16(0,1,0))
+                               material.setTexture(0,
+                                               g_texturesource->getTextureRaw("torch_on_ceiling.png"));
+                       // For backwards compatibility
+                       else if(dir == v3s16(0,0,0))
+                               material.setTexture(0,
+                                               g_texturesource->getTextureRaw("torch_on_floor.png"));
+                       else
+                               material.setTexture(0, 
+                                               g_texturesource->getTextureRaw("torch.png"));
+
+                       u16 indices[] = {0,1,2,2,3,0};
+                       // Add to mesh collector
+                       collector.append(material, vertices, 4, indices, 6);
                }
+               /*
+                       Add flowing water to mesh
+               */
+               else if(n.d == CONTENT_WATER)
+               {
+                       bool top_is_water = false;
+                       try{
+                               MapNode n = getNodeParent(v3s16(x,y+1,z));
+                               if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
+                                       top_is_water = true;
+                       }catch(InvalidPositionException &e){}
+                       
+                       u8 l = decode_light(n.getLightBlend(daynight_ratio));
+                       video::SColor c(WATER_ALPHA,l,l,l);
+                       
+                       // Neighbor water levels (key = relative position)
+                       // Includes current node
+                       core::map<v3s16, f32> neighbor_levels;
+                       core::map<v3s16, u8> neighbor_contents;
+                       core::map<v3s16, u8> neighbor_flags;
+                       const u8 neighborflag_top_is_water = 0x01;
+                       v3s16 neighbor_dirs[9] = {
+                               v3s16(0,0,0),
+                               v3s16(0,0,1),
+                               v3s16(0,0,-1),
+                               v3s16(1,0,0),
+                               v3s16(-1,0,0),
+                               v3s16(1,0,1),
+                               v3s16(-1,0,-1),
+                               v3s16(1,0,-1),
+                               v3s16(-1,0,1),
+                       };
+                       for(u32 i=0; i<9; i++)
+                       {
+                               u8 content = CONTENT_AIR;
+                               float level = -0.5 * BS;
+                               u8 flags = 0;
+                               try{
+                                       // Check neighbor
+                                       v3s16 p2 = p + neighbor_dirs[i];
+                                       MapNode n2 = getNodeParent(p2);
+
+                                       content = n2.d;
+
+                                       if(n2.d == CONTENT_WATERSOURCE)
+                                               level = (-0.5+node_water_level) * BS;
+                                       else if(n2.d == CONTENT_WATER)
+                                               level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
+                                                               * node_water_level) * BS;
+
+                                       // Check node above neighbor.
+                                       // NOTE: This doesn't get executed if neighbor
+                                       //       doesn't exist
+                                       p2.Y += 1;
+                                       n2 = getNodeParent(p2);
+                                       if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
+                                               flags |= neighborflag_top_is_water;
+                               }
+                               catch(InvalidPositionException &e){}
+                               
+                               neighbor_levels.insert(neighbor_dirs[i], level);
+                               neighbor_contents.insert(neighbor_dirs[i], content);
+                               neighbor_flags.insert(neighbor_dirs[i], flags);
+                       }
 
-               collector.fillMesh(mesh_new);
+                       //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
+                       //float water_level = neighbor_levels[v3s16(0,0,0)];
 
-               // Use VBO for mesh (this just would set this for ever buffer)
-               //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
-               
-               /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
-                               <<"and uses "<<mesh_new->getMeshBufferCount()
-                               <<" materials (meshbuffers)"<<std::endl;*/
-       }
-       
-       /*
-               Clear temporary FastFaces
-       */
+                       // Corner heights (average between four waters)
+                       f32 corner_levels[4];
+                       
+                       v3s16 halfdirs[4] = {
+                               v3s16(0,0,0),
+                               v3s16(1,0,0),
+                               v3s16(1,0,1),
+                               v3s16(0,0,1),
+                       };
+                       for(u32 i=0; i<4; i++)
+                       {
+                               v3s16 cornerdir = halfdirs[i];
+                               float cornerlevel = 0;
+                               u32 valid_count = 0;
+                               for(u32 j=0; j<4; j++)
+                               {
+                                       v3s16 neighbordir = cornerdir - halfdirs[j];
+                                       u8 content = neighbor_contents[neighbordir];
+                                       // Special case for source nodes
+                                       if(content == CONTENT_WATERSOURCE)
+                                       {
+                                               cornerlevel = (-0.5+node_water_level)*BS;
+                                               valid_count = 1;
+                                               break;
+                                       }
+                                       else if(content == CONTENT_WATER)
+                                       {
+                                               cornerlevel += neighbor_levels[neighbordir];
+                                               valid_count++;
+                                       }
+                                       else if(content == CONTENT_AIR)
+                                       {
+                                               cornerlevel += -0.5*BS;
+                                               valid_count++;
+                                       }
+                               }
+                               if(valid_count > 0)
+                                       cornerlevel /= valid_count;
+                               corner_levels[i] = cornerlevel;
+                       }
 
-       core::list<FastFace*>::Iterator i;
-       i = fastfaces_new->begin();
-       for(; i != fastfaces_new->end(); i++)
-       {
-               delete *i;
-       }
-       fastfaces_new->clear();
-       delete fastfaces_new;
+                       /*
+                               Generate sides
+                       */
 
-       /*
-               Add special graphics:
-               - torches
-               
-               TODO: Optimize by using same meshbuffer for same textures
-       */
+                       v3s16 side_dirs[4] = {
+                               v3s16(1,0,0),
+                               v3s16(-1,0,0),
+                               v3s16(0,0,1),
+                               v3s16(0,0,-1),
+                       };
+                       s16 side_corners[4][2] = {
+                               {1, 2},
+                               {3, 0},
+                               {2, 3},
+                               {0, 1},
+                       };
+                       for(u32 i=0; i<4; i++)
+                       {
+                               v3s16 dir = side_dirs[i];
+
+                               /*
+                                       If our topside is water and neighbor's topside
+                                       is water, don't draw side face
+                               */
+                               if(top_is_water &&
+                                               neighbor_flags[dir] & neighborflag_top_is_water)
+                                       continue;
+
+                               u8 neighbor_content = neighbor_contents[dir];
+                               
+                               // Don't draw face if neighbor is not air or water
+                               if(neighbor_content != CONTENT_AIR
+                                               && neighbor_content != CONTENT_WATER)
+                                       continue;
+                               
+                               bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
+                               
+                               // Don't draw any faces if neighbor is water and top is water
+                               if(neighbor_is_water == true && top_is_water == false)
+                                       continue;
+                               
+                               video::S3DVertex vertices[4] =
+                               {
+                                       /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
+                                       video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
+                                       video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
+                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
+                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
+                                                       pa_water1.x0(), pa_water1.y1()),
+                                       video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
+                                                       pa_water1.x1(), pa_water1.y1()),
+                                       video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
+                                                       pa_water1.x1(), pa_water1.y0()),
+                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
+                                                       pa_water1.x0(), pa_water1.y0()),
+                               };
+                               
+                               /*
+                                       If our topside is water, set upper border of face
+                                       at upper border of node
+                               */
+                               if(top_is_water)
+                               {
+                                       vertices[2].Pos.Y = 0.5*BS;
+                                       vertices[3].Pos.Y = 0.5*BS;
+                               }
+                               /*
+                                       Otherwise upper position of face is corner levels
+                               */
+                               else
+                               {
+                                       vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
+                                       vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
+                               }
+                               
+                               /*
+                                       If neighbor is water, lower border of face is corner
+                                       water levels
+                               */
+                               if(neighbor_is_water)
+                               {
+                                       vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
+                                       vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
+                               }
+                               /*
+                                       If neighbor is not water, lower border of face is
+                                       lower border of node
+                               */
+                               else
+                               {
+                                       vertices[0].Pos.Y = -0.5*BS;
+                                       vertices[1].Pos.Y = -0.5*BS;
+                               }
+                               
+                               for(s32 j=0; j<4; j++)
+                               {
+                                       if(dir == v3s16(0,0,1))
+                                               vertices[j].Pos.rotateXZBy(0);
+                                       if(dir == v3s16(0,0,-1))
+                                               vertices[j].Pos.rotateXZBy(180);
+                                       if(dir == v3s16(-1,0,0))
+                                               vertices[j].Pos.rotateXZBy(90);
+                                       if(dir == v3s16(1,0,-0))
+                                               vertices[j].Pos.rotateXZBy(-90);
+
+                                       vertices[j].Pos += intToFloat(p + getPosRelative());
+                               }
 
-       /*scene::ISceneManager *smgr = NULL;
-       video::IVideoDriver* driver = NULL;
-       if(g_device)
-       {
-               smgr = g_device->getSceneManager();
-               driver = smgr->getVideoDriver();
-       }*/
+                               u16 indices[] = {0,1,2,2,3,0};
+                               // Add to mesh collector
+                               collector.append(material_water1, vertices, 4, indices, 6);
+                       }
                        
-       for(s16 z=0; z<MAP_BLOCKSIZE; z++)
-       for(s16 y=0; y<MAP_BLOCKSIZE; y++)
-       for(s16 x=0; x<MAP_BLOCKSIZE; x++)
-       {
-               v3s16 p(x,y,z);
+                       /*
+                               Generate top side, if appropriate
+                       */
+                       
+                       if(top_is_water == false)
+                       {
+                               video::S3DVertex vertices[4] =
+                               {
+                                       /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
+                                       video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
+                                       video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
+                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
+                                       video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
+                                                       pa_water1.x0(), pa_water1.y1()),
+                                       video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
+                                                       pa_water1.x1(), pa_water1.y1()),
+                                       video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
+                                                       pa_water1.x1(), pa_water1.y0()),
+                                       video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
+                                                       pa_water1.x0(), pa_water1.y0()),
+                               };
+                               
+                               // This fixes a strange bug
+                               s32 corner_resolve[4] = {3,2,1,0};
 
-               MapNode &n = getNodeRef(x,y,z);
-               
-               if(n.d == CONTENT_LIGHT)
+                               for(s32 i=0; i<4; i++)
+                               {
+                                       //vertices[i].Pos.Y += water_level;
+                                       //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 + getPosRelative());
+                               }
+
+                               u16 indices[] = {0,1,2,2,3,0};
+                               // Add to mesh collector
+                               collector.append(material_water1, vertices, 4, indices, 6);
+                       }
+               }
+               /*
+                       Add water sources to mesh if using new style
+               */
+               else if(n.d == CONTENT_WATERSOURCE && new_style_water)
                {
-                       //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
-                       scene::SMeshBuffer *buf = new scene::SMeshBuffer();
-                       video::SColor c(255,255,255,255);
+                       //bool top_is_water = false;
+                       bool top_is_air = false;
+                       try{
+                               MapNode n = getNodeParent(v3s16(x,y+1,z));
+                               /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
+                                       top_is_water = true;*/
+                               if(n.d == CONTENT_AIR)
+                                       top_is_air = true;
+                       }catch(InvalidPositionException &e){}
+                       
+                       /*if(top_is_water == true)
+                               continue;*/
+                       if(top_is_air == false)
+                               continue;
 
+                       u8 l = decode_light(n.getLightBlend(daynight_ratio));
+                       video::SColor c(WATER_ALPHA,l,l,l);
+                       
                        video::S3DVertex vertices[4] =
                        {
-                               video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
-                               video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
-                               video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
-                               video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
+                               /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
+                               video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
+                               video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
+                               video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
+                               video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
+                                               pa_water1.x0(), pa_water1.y1()),
+                               video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
+                                               pa_water1.x1(), pa_water1.y1()),
+                               video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
+                                               pa_water1.x1(), pa_water1.y0()),
+                               video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
+                                               pa_water1.x0(), pa_water1.y0()),
                        };
 
-                       v3s16 dir = unpackDir(n.dir);
-
                        for(s32 i=0; i<4; i++)
                        {
-                               if(dir == v3s16(1,0,0))
-                                       vertices[i].Pos.rotateXZBy(0);
-                               if(dir == v3s16(-1,0,0))
-                                       vertices[i].Pos.rotateXZBy(180);
-                               if(dir == v3s16(0,0,1))
-                                       vertices[i].Pos.rotateXZBy(90);
-                               if(dir == v3s16(0,0,-1))
-                                       vertices[i].Pos.rotateXZBy(-90);
-                               if(dir == v3s16(0,-1,0))
-                                       vertices[i].Pos.rotateXZBy(45);
-                               if(dir == v3s16(0,1,0))
-                                       vertices[i].Pos.rotateXZBy(-45);
-
+                               vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
                                vertices[i].Pos += intToFloat(p + getPosRelative());
                        }
 
                        u16 indices[] = {0,1,2,2,3,0};
-                       buf->append(vertices, 4, indices, 6);
+                       // Add to mesh collector
+                       collector.append(material_water1, vertices, 4, indices, 6);
+               }
+               /*
+                       Add leaves if using new style
+               */
+               else if(n.d == CONTENT_LEAVES && new_style_leaves)
+               {
+                       /*u8 l = decode_light(n.getLightBlend(daynight_ratio));*/
+                       u8 l = decode_light(undiminish_light(n.getLightBlend(daynight_ratio)));
+                       video::SColor c(255,l,l,l);
 
-                       // Set material
-                       buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
-                       buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
-                       buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
-                       //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
-                       buf->getMaterial().MaterialType
-                                       = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       if(dir == v3s16(0,-1,0))
-                               buf->getMaterial().setTexture(0,
-                                               g_texturecache.get("torch_on_floor"));
-                       else if(dir == v3s16(0,1,0))
-                               buf->getMaterial().setTexture(0,
-                                               g_texturecache.get("torch_on_ceiling"));
-                       // For backwards compatibility
-                       else if(dir == v3s16(0,0,0))
-                               buf->getMaterial().setTexture(0,
-                                               g_texturecache.get("torch_on_floor"));
-                       else
-                               buf->getMaterial().setTexture(0, g_texturecache.get("torch"));
+                       for(u32 j=0; j<6; j++)
+                       {
+                               video::S3DVertex vertices[4] =
+                               {
+                                       /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
+                                       video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
+                                       video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
+                                       video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
+                                       video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
+                                               pa_leaves1.x0(), pa_leaves1.y1()),
+                                       video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
+                                               pa_leaves1.x1(), pa_leaves1.y1()),
+                                       video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
+                                               pa_leaves1.x1(), pa_leaves1.y0()),
+                                       video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
+                                               pa_leaves1.x0(), pa_leaves1.y0()),
+                               };
+
+                               if(j == 0)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateXZBy(0);
+                               }
+                               else if(j == 1)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateXZBy(180);
+                               }
+                               else if(j == 2)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateXZBy(-90);
+                               }
+                               else if(j == 3)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateXZBy(90);
+                               }
+                               else if(j == 4)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateYZBy(-90);
+                               }
+                               else if(j == 5)
+                               {
+                                       for(u16 i=0; i<4; i++)
+                                               vertices[i].Pos.rotateYZBy(90);
+                               }
 
-                       // Add to mesh
-                       mesh_new->addMeshBuffer(buf);
-                       buf->drop();
+                               for(u16 i=0; i<4; i++)
+                               {
+                                       vertices[i].Pos += intToFloat(p + getPosRelative());
+                               }
+
+                               u16 indices[] = {0,1,2,2,3,0};
+                               // Add to mesh collector
+                               collector.append(material_leaves1, vertices, 4, indices, 6);
+                       }
                }
        }
 
+       /*
+               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
        */
@@ -608,15 +1272,38 @@ void MapBlock::updateMesh()
                mesh_new = NULL;
        }
 
+       if(mesh_new)
+       {
+#if 0
+               // Usually 1-700 faces and 1-7 materials
+               std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
+                               <<"and uses "<<mesh_new->getMeshBufferCount()
+                               <<" materials (meshbuffers)"<<std::endl;
+#endif
+
+               // Use VBO for mesh (this just would set this for ever buffer)
+               // This will lead to infinite memory usage because or irrlicht.
+               //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
+
+               /*
+                       NOTE: If that is enabled, some kind of a queue to the main
+                       thread should be made which would call irrlicht to delete
+                       the hardware buffer and then delete the mesh
+               */
+       }
+       
        /*
                Replace the mesh
        */
 
        mesh_mutex.Lock();
 
-       scene::SMesh *mesh_old = mesh;
+       //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)
        {
@@ -627,8 +1314,20 @@ void MapBlock::updateMesh()
                {
                        IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
                }*/
+               
+               /*dstream<<"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);
+                       dstream<<"buf->getReferenceCount()="
+                                       <<buf->getReferenceCount()<<std::endl;
+               }*/
+
                // Drop the mesh
                mesh_old->drop();
+
                //delete mesh_old;
        }
 
@@ -637,6 +1336,20 @@ void MapBlock::updateMesh()
        //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
 }
 
+/*void MapBlock::updateMeshes(s32 first_i)
+{
+       assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
+       updateMesh(first_i);
+       for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
+       {
+               if(i == first_i)
+                       continue;
+               updateMesh(i);
+       }
+}*/
+
+#endif // !SERVER
+
 /*
        Propagates sunlight down through the block.
        Doesn't modify nodes that are not affected by sunlight.
@@ -646,12 +1359,21 @@ void MapBlock::updateMesh()
 
        If there is a block above, continues from it.
        If there is no block above, assumes there is sunlight, unless
-       is_underground is set.
+       is_underground is set or highest node is water.
 
        At the moment, all sunlighted nodes are added to light_sources.
-       TODO: This could be optimized.
+       - SUGG: This could be optimized
+
+       Turns sunglighted mud into grass.
+
+       if remove_light==true, sets non-sunlighted nodes black.
+
+       if black_air_left!=NULL, it is set to true if non-sunlighted
+       air is left in block.
 */
-bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
+bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
+               bool remove_light, bool *black_air_left,
+               bool grow_grass)
 {
        // Whether the sunlight at the top of the bottom block is valid
        bool block_below_is_valid = true;
@@ -662,17 +1384,14 @@ bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
        {
                for(s16 z=0; z<MAP_BLOCKSIZE; z++)
                {
+#if 1
                        bool no_sunlight = false;
                        bool no_top_block = false;
                        // Check if node above block has sunlight
                        try{
                                MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
-                               if(n.getLight() != LIGHT_SUN)
+                               if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
                                {
-                                       /*if(is_underground)
-                                       {
-                                               no_sunlight = true;
-                                       }*/
                                        no_sunlight = true;
                                }
                        }
@@ -680,18 +1399,41 @@ bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
                        {
                                no_top_block = true;
                                
-                               // TODO: This makes over-ground roofed places sunlighted
+                               // NOTE: This makes over-ground roofed places sunlighted
                                // Assume sunlight, unless is_underground==true
                                if(is_underground)
                                {
                                        no_sunlight = true;
                                }
-                               
-                               // TODO: There has to be some way to allow this behaviour
-                               // As of now, it just makes everything dark.
+                               else
+                               {
+                                       MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
+                                       if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
+                                       {
+                                               no_sunlight = true;
+                                       }
+                               }
+                               // NOTE: As of now, this just would make everything dark.
                                // No sunlight here
                                //no_sunlight = true;
                        }
+#endif
+#if 0 // Doesn't work; nothing gets light.
+                       bool no_sunlight = true;
+                       bool no_top_block = false;
+                       // Check if node above block has sunlight
+                       try{
+                               MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
+                               if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
+                               {
+                                       no_sunlight = false;
+                               }
+                       }
+                       catch(InvalidPositionException &e)
+                       {
+                               no_top_block = true;
+                       }
+#endif
 
                        /*std::cout<<"("<<x<<","<<z<<"): "
                                        <<"no_top_block="<<no_top_block
@@ -701,45 +1443,81 @@ bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
                
                        s16 y = MAP_BLOCKSIZE-1;
                        
-                       if(no_sunlight == false)
+                       // This makes difference to diminishing in water.
+                       bool stopped_to_solid_object = false;
+                       
+                       u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
+
+                       for(; y >= 0; y--)
                        {
-                               // Continue spreading sunlight downwards through transparent
-                               // nodes
-                               for(; y >= 0; y--)
+                               v3s16 pos(x, y, z);
+                               MapNode &n = getNodeRef(pos);
+                               
+                               if(current_light == 0)
                                {
-                                       v3s16 pos(x, y, z);
-                                       
-                                       MapNode &n = getNodeRef(pos);
-
-                                       if(n.sunlight_propagates())
+                                       // Do nothing
+                               }
+                               else if(current_light == LIGHT_SUN && n.sunlight_propagates())
+                               {
+                                       // Do nothing: Sunlight is continued
+                               }
+                               else if(n.light_propagates() == false)
+                               {
+                                       if(grow_grass)
                                        {
-                                               n.setLight(LIGHT_SUN);
-
-                                               light_sources.insert(pos_relative + pos, true);
-                                       }
-                                       else{
-                                               break;
+                                               bool upper_is_air = false;
+                                               try
+                                               {
+                                                       if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
+                                                               upper_is_air = true;
+                                               }
+                                               catch(InvalidPositionException &e)
+                                               {
+                                               }
+                                               // Turn mud into grass
+                                               if(upper_is_air && n.d == CONTENT_MUD
+                                                               && current_light == LIGHT_SUN)
+                                               {
+                                                       n.d = CONTENT_GRASS;
+                                               }
                                        }
+
+                                       // A solid object is on the way.
+                                       stopped_to_solid_object = true;
+                                       
+                                       // Light stops.
+                                       current_light = 0;
+                               }
+                               else
+                               {
+                                       // Diminish light
+                                       current_light = diminish_light(current_light);
                                }
-                       }
 
-                       bool sunlight_should_go_down = (y==-1);
+                               u8 old_light = n.getLight(LIGHTBANK_DAY);
 
-                       // Fill rest with black (only transparent ones)
-                       for(; y >= 0; y--){
-                               v3s16 pos(x, y, z);
+                               if(current_light > old_light || remove_light)
+                               {
+                                       n.setLight(LIGHTBANK_DAY, current_light);
+                               }
                                
-                               MapNode &n = getNodeRef(pos);
-
-                               if(n.light_propagates())
+                               if(diminish_light(current_light) != 0)
                                {
-                                       n.setLight(0);
+                                       light_sources.insert(pos_relative + pos, true);
                                }
-                               else{
-                                       break;
+
+                               if(current_light == 0 && stopped_to_solid_object)
+                               {
+                                       if(black_air_left)
+                                       {
+                                               *black_air_left = true;
+                                       }
                                }
                        }
 
+                       // Whether or not the block below should see LIGHT_SUN
+                       bool sunlight_should_go_down = (current_light == LIGHT_SUN);
+
                        /*
                                If the block below hasn't already been marked invalid:
 
@@ -755,10 +1533,10 @@ bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
                                MapNode n = getNodeParent(v3s16(x, -1, z));
                                if(n.light_propagates())
                                {
-                                       if(n.getLight() == LIGHT_SUN
+                                       if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
                                                        && sunlight_should_go_down == false)
                                                block_below_is_valid = false;
-                                       else if(n.getLight() != LIGHT_SUN
+                                       else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
                                                        && sunlight_should_go_down == true)
                                                block_below_is_valid = false;
                                }
@@ -781,14 +1559,141 @@ void MapBlock::copyTo(VoxelManipulator &dst)
        v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
        VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
        
+       // Copy from data to VoxelManipulator
        dst.copyFrom(data, data_area, v3s16(0,0,0),
                        getPosRelative(), data_size);
 }
 
-/*void getPseudoObjects(v3f origin, f32 max_d,
-               core::array<DistanceSortedObject> &dest)
+void MapBlock::copyFrom(VoxelManipulator &dst)
 {
-}*/
+       v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
+       VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
+       
+       // Copy from VoxelManipulator to data
+       dst.copyTo(data, data_area, v3s16(0,0,0),
+                       getPosRelative(), data_size);
+}
+
+void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
+{
+       /*
+               Step objects
+       */
+       m_objects.step(dtime, server, daynight_ratio);
+       
+       /*
+               Spawn some objects at random.
+
+               Use dayNightDiffed() to approximate being near ground level
+       */
+       if(m_spawn_timer < -999)
+       {
+               m_spawn_timer = 60;
+       }
+       if(dayNightDiffed() == true && getObjectCount() == 0)
+       {
+               m_spawn_timer -= dtime;
+               if(m_spawn_timer <= 0.0)
+               {
+                       m_spawn_timer += myrand() % 300;
+                       
+                       v2s16 p2d(
+                               (myrand()%(MAP_BLOCKSIZE-1))+0,
+                               (myrand()%(MAP_BLOCKSIZE-1))+0
+                       );
+
+                       s16 y = getGroundLevel(p2d);
+                       
+                       if(y >= 0)
+                       {
+                               v3s16 p(p2d.X, y+1, p2d.Y);
+
+                               if(getNode(p).d == CONTENT_AIR
+                                               && getNode(p).getLightBlend(daynight_ratio) <= 11)
+                               {
+                                       RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
+                                       addObject(obj);
+                               }
+                       }
+               }
+       }
+
+       setChangedFlag();
+}
+
+
+void MapBlock::updateDayNightDiff()
+{
+       if(data == NULL)
+       {
+               m_day_night_differs = false;
+               return;
+       }
+
+       bool differs = false;
+
+       /*
+               Check if any lighting value differs
+       */
+       for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
+       {
+               MapNode &n = data[i];
+               if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
+               {
+                       differs = true;
+                       break;
+               }
+       }
+
+       /*
+               If some lighting values differ, check if the whole thing is
+               just air. If it is, differ = false
+       */
+       if(differs)
+       {
+               bool only_air = true;
+               for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
+               {
+                       MapNode &n = data[i];
+                       if(n.d != CONTENT_AIR)
+                       {
+                               only_air = false;
+                               break;
+                       }
+               }
+               if(only_air)
+                       differs = false;
+       }
+
+       // Set member variable
+       m_day_night_differs = differs;
+}
+
+s16 MapBlock::getGroundLevel(v2s16 p2d)
+{
+       if(isDummy())
+               return -3;
+       try
+       {
+               s16 y = MAP_BLOCKSIZE-1;
+               for(; y>=0; y--)
+               {
+                       //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
+                       if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
+                       {
+                               if(y == MAP_BLOCKSIZE-1)
+                                       return -2;
+                               else
+                                       return y;
+                       }
+               }
+               return -1;
+       }
+       catch(InvalidPositionException &e)
+       {
+               return -3;
+       }
+}
 
 /*
        Serialization
@@ -851,20 +1756,27 @@ void MapBlock::serialize(std::ostream &os, u8 version)
                
                if(version >= 10)
                {
-                       // Get and compress pressure
-                       SharedBuffer<u8> pressuredata(nodecount);
+                       // Get and compress param2
+                       SharedBuffer<u8> param2data(nodecount);
                        for(u32 i=0; i<nodecount; i++)
                        {
-                               pressuredata[i] = data[i].pressure;
+                               param2data[i] = data[i].param2;
                        }
-                       compress(pressuredata, os, version);
+                       compress(param2data, os, version);
                }
        }
        // All other versions (newest)
        else
        {
                // First byte
-               os.write((char*)&is_underground, 1);
+               u8 flags = 0;
+               if(is_underground)
+                       flags |= 1;
+               if(m_day_night_differs)
+                       flags |= 2;
+               if(m_lighting_expired)
+                       flags |= 3;
+               os.write((char*)&flags, 1);
 
                u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
 
@@ -886,10 +1798,10 @@ void MapBlock::serialize(std::ostream &os, u8 version)
                        databuf[i+nodecount] = data[i].param;
                }
 
-               // Get pressure
+               // Get param2
                for(u32 i=0; i<nodecount; i++)
                {
-                       databuf[i+nodecount*2] = data[i].pressure;
+                       databuf[i+nodecount*2] = data[i].param2;
                }
 
                /*
@@ -963,7 +1875,7 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
        
                if(version >= 10)
                {
-                       // Uncompress and set pressure data
+                       // Uncompress and set param2 data
                        std::ostringstream os(std::ios_base::binary);
                        decompress(is, os, version);
                        std::string s = os.str();
@@ -972,7 +1884,7 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
                                                ("MapBlock::deSerialize: invalid format");
                        for(u32 i=0; i<s.size(); i++)
                        {
-                               data[i].pressure = s[i];
+                               data[i].param2 = s[i];
                        }
                }
        }
@@ -981,9 +1893,11 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
        {
                u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
 
-               u8 t8;
-               is.read((char*)&t8, 1);
-               is_underground = t8;
+               u8 flags;
+               is.read((char*)&flags, 1);
+               is_underground = (flags & 1) ? true : false;
+               m_day_night_differs = (flags & 2) ? true : false;
+               m_lighting_expired = (flags & 3) ? true : false;
 
                // Uncompress data
                std::ostringstream os(std::ios_base::binary);
@@ -1003,10 +1917,27 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
                {
                        data[i].param = s[i+nodecount];
                }
-               // Set pressure
+               // Set param2
                for(u32 i=0; i<nodecount; i++)
                {
-                       data[i].pressure = s[i+nodecount*2];
+                       data[i].param2 = s[i+nodecount*2];
+               }
+       }
+       
+       /*
+               Translate nodes as specified in the translate_to fields of
+               node features
+       */
+       for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
+       {
+               MapNode &n = data[i];
+
+               MapNode *translate_to = content_features(n.d).translate_to;
+               if(translate_to)
+               {
+                       dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
+                                       <<translate_to->d<<std::endl;
+                       n = *translate_to;
                }
        }
 }