Optimize shaders code. Add settings at compile time.
[oweals/minetest.git] / src / mapblock_mesh.cpp
index abe23855f247ea71513296b130753a066b766f90..afea3dcce2e9224d3d894b69c366834ae99d20ec 100644 (file)
@@ -1,18 +1,18 @@
 /*
-Minetest-c55
-Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
+Minetest
+Copyright (C) 2010-2013 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
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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.
+GNU Lesser General Public License for more details.
 
-You should have received a copy of the GNU General Public License along
+You should have received a copy of the GNU Lesser 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.
 */
@@ -27,6 +27,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "gamedef.h"
 #include "mesh.h"
 #include "content_mapblock.h"
+#include "noise.h"
+#include "shader.h"
+#include "settings.h"
+#include "util/directiontables.h"
+
+float srgb_linear_multiply(float f, float m, float max)
+{
+       f = f * f; // SRGB -> Linear
+       f *= m;
+       f = sqrt(f); // Linear -> SRGB
+       if(f > max)
+               f = max;
+       return f;
+}
 
 /*
        MeshMakeData
@@ -74,9 +88,9 @@ void MeshMakeData::fill(MapBlock *block)
                // Get map
                Map *map = block->getParent();
 
-               for(u16 i=0; i<6; i++)
+               for(u16 i=0; i<26; i++)
                {
-                       const v3s16 &dir = g_6dirs[i];
+                       const v3s16 &dir = g_26dirs[i];
                        v3s16 bp = m_blockpos + dir;
                        MapBlock *b = map->getBlockNoCreateNoEx(bp);
                        if(b)
@@ -183,6 +197,15 @@ static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
        else
                light = l2;
 
+       // Boost light level for light sources
+       u8 light_source = MYMAX(ndef->get(n).light_source,
+                       ndef->get(n2).light_source);
+       //if(light_source >= light)
+               //return decode_light(undiminish_light(light_source));
+       if(light_source > light)
+               //return decode_light(light_source);
+               light = light_source;
+
        // Make some nice difference to different sides
 
        // This makes light come from a corner
@@ -233,10 +256,13 @@ static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
        u16 ambient_occlusion = 0;
        u16 light = 0;
        u16 light_count = 0;
+       u8 light_source_max = 0;
        for(u32 i=0; i<8; i++)
        {
                MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
                const ContentFeatures &f = ndef->get(n);
+               if(f.light_source > light_source_max)
+                       light_source_max = f.light_source;
                // Check f.solidness because fast-style leaves look
                // better this way
                if(f.param_type == CPT_LIGHT && f.solidness != 2)
@@ -255,10 +281,23 @@ static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
        
        light /= light_count;
 
+       // Boost brightness around light sources
+       if(decode_light(light_source_max) >= light)
+               //return decode_light(undiminish_light(light_source_max));
+               return decode_light(light_source_max);
+
        if(ambient_occlusion > 4)
        {
-               ambient_occlusion -= 4;
-               light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
+               //ambient_occlusion -= 4;
+               //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
+               float light_amount = (8 - ambient_occlusion) / 4.0;
+               float light_f = (float)light / 255.0;
+               light_f = pow(light_f, 2.2f); // gamma -> linear space
+               light_f = light_f * light_amount;
+               light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
+               if(light_f > 1.0)
+                       light_f = 1.0;
+               light = 255.0 * light_f + 0.5;
        }
 
        return light;
@@ -406,16 +445,134 @@ struct FastFace
 };
 
 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
-               v3f p, v3s16 dir, v3f scale, core::array<FastFace> &dest)
+               v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
 {
        FastFace face;
-       
+
        // Position is at the center of the cube.
        v3f pos = p * BS;
 
+       float x0 = 0.0;
+       float y0 = 0.0;
+       float w = 1.0;
+       float h = 1.0;
+
        v3f vertex_pos[4];
        v3s16 vertex_dirs[4];
        getNodeVertexDirs(dir, vertex_dirs);
+
+       v3s16 t;
+       u16 t1;
+       switch (tile.rotation)
+       {
+       case 0:
+               break;
+       case 1: //R90
+               t = vertex_dirs[0];
+               vertex_dirs[0] = vertex_dirs[3];
+               vertex_dirs[3] = vertex_dirs[2];
+               vertex_dirs[2] = vertex_dirs[1];
+               vertex_dirs[1] = t;
+               t1=li0;
+               li0=li3;
+               li3=li2;
+               li2=li1;
+               li1=t1;
+               break;
+       case 2: //R180
+               t = vertex_dirs[0];
+               vertex_dirs[0] = vertex_dirs[2];
+               vertex_dirs[2] = t;
+               t = vertex_dirs[1];
+               vertex_dirs[1] = vertex_dirs[3];
+               vertex_dirs[3] = t;
+               t1  = li0;
+               li0 = li2;
+               li2 = t1;
+               t1  = li1;
+               li1 = li3;
+               li3 = t1;
+               break;
+       case 3: //R270
+               t = vertex_dirs[0];
+               vertex_dirs[0] = vertex_dirs[1];
+               vertex_dirs[1] = vertex_dirs[2];
+               vertex_dirs[2] = vertex_dirs[3];
+               vertex_dirs[3] = t;
+               t1  = li0;
+               li0 = li1;
+               li1 = li2;
+               li2 = li3;
+               li3 = t1;
+               break;
+       case 4: //FXR90
+               t = vertex_dirs[0];
+               vertex_dirs[0] = vertex_dirs[3];
+               vertex_dirs[3] = vertex_dirs[2];
+               vertex_dirs[2] = vertex_dirs[1];
+               vertex_dirs[1] = t;
+               t1  = li0;
+               li0 = li3;
+               li3 = li2;
+               li2 = li1;
+               li1 = t1;
+               y0 += h;
+               h *= -1;
+               break;
+       case 5: //FXR270
+               t = vertex_dirs[0];
+               vertex_dirs[0] = vertex_dirs[1];
+               vertex_dirs[1] = vertex_dirs[2];
+               vertex_dirs[2] = vertex_dirs[3];
+               vertex_dirs[3] = t;
+               t1  = li0;
+               li0 = li1;
+               li1 = li2;
+               li2 = li3;
+               li3 = t1;
+               y0 += h;
+               h *= -1;
+               break;
+       case 6: //FYR90
+               t = vertex_dirs[0];
+               vertex_dirs[0] = vertex_dirs[3];
+               vertex_dirs[3] = vertex_dirs[2];
+               vertex_dirs[2] = vertex_dirs[1];
+               vertex_dirs[1] = t;
+               t1  = li0;
+               li0 = li3;
+               li3 = li2;
+               li2 = li1;
+               li1 = t1;
+               x0 += w;
+               w *= -1;
+               break;
+       case 7: //FYR270
+               t = vertex_dirs[0];
+               vertex_dirs[0] = vertex_dirs[1];
+               vertex_dirs[1] = vertex_dirs[2];
+               vertex_dirs[2] = vertex_dirs[3];
+               vertex_dirs[3] = t;
+               t1  = li0;
+               li0 = li1;
+               li1 = li2;
+               li2 = li3;
+               li3 = t1;
+               x0 += w;
+               w *= -1;
+               break;
+       case 8: //FX
+               y0 += h;
+               h *= -1;
+               break;
+       case 9: //FY
+               x0 += w;
+               w *= -1;
+               break;
+       default:
+               break;
+       }
+
        for(u16 i=0; i<4; i++)
        {
                vertex_pos[i] = v3f(
@@ -433,7 +590,7 @@ static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
                vertex_pos[i] += pos;
        }
 
-       f32 abs_scale = 1.;
+       f32 abs_scale = 1.0;
        if     (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
        else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
        else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
@@ -442,26 +599,20 @@ static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
 
        u8 alpha = tile.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;
-
        face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
-                       MapBlock_LightColor(alpha, li0),
+                       MapBlock_LightColor(alpha, li0, light_source),
                        core::vector2d<f32>(x0+w*abs_scale, y0+h));
        face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
-                       MapBlock_LightColor(alpha, li1),
+                       MapBlock_LightColor(alpha, li1, light_source),
                        core::vector2d<f32>(x0, y0+h));
        face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
-                       MapBlock_LightColor(alpha, li2),
+                       MapBlock_LightColor(alpha, li2, light_source),
                        core::vector2d<f32>(x0, y0));
        face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
-                       MapBlock_LightColor(alpha, li3),
+                       MapBlock_LightColor(alpha, li3, light_source),
                        core::vector2d<f32>(x0+w*abs_scale, y0));
 
        face.tile = tile;
-       
        dest.push_back(face);
 }
 
@@ -532,7 +683,6 @@ TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
        if(p == data->m_crack_pos_relative)
        {
                spec.material_flags |= MATERIAL_FLAG_CRACK;
-               spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
        }
        return spec;
 }
@@ -557,22 +707,51 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
        //  5 = (0,0,-1)
        //  6 = (0,-1,0)
        //  7 = (-1,0,0)
-       u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
+       u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
 
        // Get rotation for things like chests
        u8 facedir = mn.getFaceDir(ndef);
-       assert(facedir <= 3);
-       
-       static const u8 dir_to_tile[4 * 8] =
+       if (facedir > 23)
+               facedir = 0;
+       static const u16 dir_to_tile[24 * 16] =
        {
-               // 0  +X  +Y  +Z   0  -Z  -Y  -X
-                  0,  2,  0,  4,  0,  5,  1,  3,  // facedir = 0
-                  0,  4,  0,  3,  0,  2,  1,  5,  // facedir = 1
-                  0,  3,  0,  5,  0,  4,  1,  2,  // facedir = 2
-                  0,  5,  0,  2,  0,  3,  1,  4,  // facedir = 3
+               // 0     +X    +Y    +Z           -Z    -Y    -X   ->   value=tile,rotation  
+                  0,0,  2,0 , 0,0 , 4,0 ,  0,0,  5,0 , 1,0 , 3,0 ,  // rotate around y+ 0 - 3
+                  0,0,  4,0 , 0,3 , 3,0 ,  0,0,  2,0 , 1,1 , 5,0 ,
+                  0,0,  3,0 , 0,2 , 5,0 ,  0,0,  4,0 , 1,2 , 2,0 ,
+                  0,0,  5,0 , 0,1 , 2,0 ,  0,0,  3,0 , 1,3 , 4,0 ,
+
+                  0,0,  2,3 , 5,0 , 0,2 ,  0,0,  1,0 , 4,2 , 3,1 ,  // rotate around z+ 4 - 7
+                  0,0,  4,3 , 2,0 , 0,3 ,  0,0,  1,1 , 3,2 , 5,1 ,
+                  0,0,  3,3 , 4,0 , 0,0 ,  0,0,  1,2 , 5,2 , 2,1 ,
+                  0,0,  5,3 , 3,0 , 0,1 ,  0,0,  1,3 , 2,2 , 4,1 ,
+
+                  0,0,  2,1 , 4,2 , 1,2 ,  0,0,  0,0 , 5,0 , 3,3 ,  // rotate around z- 8 - 11
+                  0,0,  4,1 , 3,2 , 1,3 ,  0,0,  0,3 , 2,0 , 5,3 ,
+                  0,0,  3,1 , 5,2 , 1,0 ,  0,0,  0,2 , 4,0 , 2,3 ,
+                  0,0,  5,1 , 2,2 , 1,1 ,  0,0,  0,1 , 3,0 , 4,3 ,
+
+                  0,0,  0,3 , 3,3 , 4,1 ,  0,0,  5,3 , 2,3 , 1,3 ,  // rotate around x+ 12 - 15
+                  0,0,  0,2 , 5,3 , 3,1 ,  0,0,  2,3 , 4,3 , 1,0 ,
+                  0,0,  0,1 , 2,3 , 5,1 ,  0,0,  4,3 , 3,3 , 1,1 ,
+                  0,0,  0,0 , 4,3 , 2,1 ,  0,0,  3,3 , 5,3 , 1,2 ,
+
+                  0,0,  1,1 , 2,1 , 4,3 ,  0,0,  5,1 , 3,1 , 0,1 ,  // rotate around x- 16 - 19  
+                  0,0,  1,2 , 4,1 , 3,3 ,  0,0,  2,1 , 5,1 , 0,0 ,
+                  0,0,  1,3 , 3,1 , 5,3 ,  0,0,  4,1 , 2,1 , 0,3 ,  
+                  0,0,  1,0 , 5,1 , 2,3 ,  0,0,  3,1 , 4,1 , 0,2 ,  
+
+                  0,0,  3,2 , 1,2 , 4,2 ,  0,0,  5,2 , 0,2 , 2,2 ,  // rotate around y- 20 - 23
+                  0,0,  5,2 , 1,3 , 3,2 ,  0,0,  2,2 , 0,1 , 4,2 ,  
+                  0,0,  2,2 , 1,0 , 5,2 ,  0,0,  4,2 , 0,0 , 3,2 ,  
+                  0,0,  4,2 , 1,1 , 2,2 ,  0,0,  3,2 , 0,3 , 5,2   
+
        };
-       u8 tileindex = dir_to_tile[facedir*8 + dir_i];
-       return getNodeTileN(mn, p, tileindex, data);
+       u16 tile_index=facedir*16 + dir_i;
+       TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
+       spec.rotation=dir_to_tile[tile_index + 1];
+       spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
+       return spec;
 }
 
 static void getTileInfo(
@@ -585,7 +764,8 @@ static void getTileInfo(
                v3s16 &p_corrected,
                v3s16 &face_dir_corrected,
                u16 *lights,
-               TileSpec &tile
+               TileSpec &tile,
+               u8 &light_source
        )
 {
        VoxelManipulator &vmanip = data->m_vmanip;
@@ -615,18 +795,20 @@ static void getTileInfo(
                tile = tile0;
                p_corrected = p;
                face_dir_corrected = face_dir;
+               light_source = ndef->get(n0).light_source;
        }
        else
        {
                tile = tile1;
                p_corrected = p + face_dir;
                face_dir_corrected = -face_dir;
+               light_source = ndef->get(n1).light_source;
        }
        
        // eg. water and glass
        if(equivalent)
                tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
-       
+
        if(data->m_smooth_lighting == false)
        {
                lights[0] = lights[1] = lights[2] = lights[3] =
@@ -659,7 +841,7 @@ static void updateFastFaceRow(
                v3f translate_dir_f,
                v3s16 face_dir,
                v3f face_dir_f,
-               core::array<FastFace> &dest)
+               std::vector<FastFace> &dest)
 {
        v3s16 p = startpos;
        
@@ -670,9 +852,10 @@ static void updateFastFaceRow(
        v3s16 face_dir_corrected;
        u16 lights[4] = {0,0,0,0};
        TileSpec tile;
+       u8 light_source = 0;
        getTileInfo(data, p, face_dir, 
                        makes_face, p_corrected, face_dir_corrected,
-                       lights, tile);
+                       lights, tile, light_source);
 
        for(u16 j=0; j<MAP_BLOCKSIZE; j++)
        {
@@ -686,6 +869,7 @@ static void updateFastFaceRow(
                v3s16 next_face_dir_corrected;
                u16 next_lights[4] = {0,0,0,0};
                TileSpec next_tile;
+               u8 next_light_source = 0;
                
                // If at last position, there is nothing to compare to and
                // the face must be drawn anyway
@@ -696,7 +880,7 @@ static void updateFastFaceRow(
                        getTileInfo(data, p_next, face_dir,
                                        next_makes_face, next_p_corrected,
                                        next_face_dir_corrected, next_lights,
-                                       next_tile);
+                                       next_tile, next_light_source);
                        
                        if(next_makes_face == makes_face
                                        && next_p_corrected == p_corrected + translate_dir
@@ -705,7 +889,9 @@ static void updateFastFaceRow(
                                        && next_lights[1] == lights[1]
                                        && next_lights[2] == lights[2]
                                        && next_lights[3] == lights[3]
-                                       && next_tile == tile)
+                                       && next_tile == tile
+                                       && tile.rotation == 0
+                                       && next_light_source == light_source)
                        {
                                next_is_different = false;
                        }
@@ -736,23 +922,7 @@ static void updateFastFaceRow(
 
                continuous_tiles_count++;
                
-               // 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.
-               */
-               if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
-               {
-                       if(tile.texture.tiled <= continuous_tiles_count)
-                               end_of_texture = true;
-               }
-               
-               // Do this to disable tiling textures
-               //end_of_texture = true; //DEBUG
-               
-               if(next_is_different || end_of_texture)
+               if(next_is_different)
                {
                        /*
                                Create a face if there should be one
@@ -781,7 +951,7 @@ static void updateFastFaceRow(
                                }
                                
                                makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
-                                               sp, face_dir_corrected, scale,
+                                               sp, face_dir_corrected, scale, light_source,
                                                dest);
                                
                                g_profiler->avg("Meshgen: faces drawn by tiling", 0);
@@ -800,6 +970,7 @@ static void updateFastFaceRow(
                        lights[2] = next_lights[2];
                        lights[3] = next_lights[3];
                        tile = next_tile;
+                       light_source = next_light_source;
                }
                
                p = p_next;
@@ -807,7 +978,7 @@ static void updateFastFaceRow(
 }
 
 static void updateAllFastFaceRows(MeshMakeData *data,
-               core::array<FastFace> &dest)
+               std::vector<FastFace> &dest)
 {
        /*
                Go through every y,z and get top(y+) faces in rows of x+
@@ -872,7 +1043,7 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
        // 24-155ms for MAP_BLOCKSIZE=32  (NOTE: probably outdated)
        //TimeTaker timer1("MapBlockMesh()");
 
-       core::array<FastFace> fastfaces_new;
+       std::vector<FastFace> fastfaces_new;
 
        /*
                We are including the faces of the trailing edges of the block.
@@ -906,7 +1077,7 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
                        const u16 indices[] = {0,1,2,2,3,0};
                        const u16 indices_alternate[] = {0,1,3,2,3,1};
                        
-                       if(f.tile.texture.atlas == NULL)
+                       if(f.tile.texture == NULL)
                                continue;
 
                        const u16 *indices_p = indices;
@@ -937,8 +1108,25 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
 
        /*
                Convert MeshCollector to SMesh
-               Also store animation info
        */
+       bool enable_shaders     = g_settings->getBool("enable_shaders");
+       bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
+       bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
+
+       video::E_MATERIAL_TYPE  shadermat1, shadermat2, shadermat3,
+                                                       shadermat4, shadermat5;
+       shadermat1 = shadermat2 = shadermat3 = shadermat4 = shadermat5 = 
+               video::EMT_SOLID;
+
+       if (enable_shaders) {
+               IShaderSource *shdrsrc = m_gamedef->getShaderSource();
+               shadermat1 = shdrsrc->getShader("solids_shader").material;
+               shadermat2 = shdrsrc->getShader("liquids_shader").material;
+               shadermat3 = shdrsrc->getShader("alpha_shader").material;
+               shadermat4 = shdrsrc->getShader("leaves_shader").material;
+               shadermat5 = shdrsrc->getShader("plants_shader").material;
+       }
+
        for(u32 i = 0; i < collector.prebuffers.size(); i++)
        {
                PreMeshBuffer &p = collector.prebuffers[i];
@@ -951,24 +1139,63 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
                if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
                {
                        ITextureSource *tsrc = data->m_gamedef->tsrc();
-                       std::string crack_basename = tsrc->getTextureName(p.tile.texture.id);
+                       // Find the texture name plus ^[crack:N:
+                       std::ostringstream os(std::ios::binary);
+                       os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
                        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));
+                               os<<"o";  // use ^[cracko
+                       os<<":"<<(u32)p.tile.animation_frame_count<<":";
+                       m_crack_materials.insert(std::make_pair(i, os.str()));
+                       // Replace tile texture with the cracked one
+                       p.tile.texture = tsrc->getTexture(
+                                       os.str()+"0",
+                                       &p.tile.texture_id);
                }
-               // - Lighting
-               for(u32 j = 0; j < p.vertices.size(); j++)
+               // - Texture animation
+               if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
                {
-                       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);
+                       ITextureSource *tsrc = data->m_gamedef->tsrc();
+                       // Add to MapBlockMesh in order to animate these tiles
+                       m_animation_tiles[i] = p.tile;
+                       m_animation_frames[i] = 0;
+                       if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
+                               // Get starting position from noise
+                               m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
+                                               data->m_blockpos.X, data->m_blockpos.Y,
+                                               data->m_blockpos.Z, 0));
+                       } else {
+                               // Play all synchronized
+                               m_animation_frame_offsets[i] = 0;
+                       }
+                       // Replace tile texture with the first animation frame
+                       std::ostringstream os(std::ios::binary);
+                       os<<tsrc->getTextureName(p.tile.texture_id);
+                       os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
+                       p.tile.texture = tsrc->getTexture(
+                                       os.str(),
+                                       &p.tile.texture_id);
+               }
+               // - Classic lighting (shaders handle this by themselves)
+               if(!enable_shaders)
+               {
+                       for(u32 j = 0; j < p.vertices.size(); j++)
+                       {
+                               video::SColor &vc = p.vertices[j].Color;
+                               // Set initial real color and store for later updates
+                               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);
+                               // Brighten topside (no shaders)
+                               if(p.vertices[j].Normal.Y > 0.5)
+                               {
+                                       vc.setRed  (srgb_linear_multiply(vc.getRed(),   1.3, 255.0));
+                                       vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
+                                       vc.setBlue (srgb_linear_multiply(vc.getBlue(),  1.3, 255.0));
+                               }
+                       }
                }
-
 
                // Create material
                video::SMaterial material;
@@ -978,11 +1205,34 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
                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);
+               //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+               material.setTexture(0, p.tile.texture);
 
+               if (enable_shaders) {
+                       ITextureSource *tsrc = data->m_gamedef->tsrc();
+                       material.setTexture(2, tsrc->getTexture("disable_img.png"));
+                       if (enable_bumpmapping || enable_parallax_occlusion) {
+                               std::string fname_base = tsrc->getTextureName(p.tile.texture_id);
+                               std::string normal_ext = "_normal.png";
+                               size_t pos = fname_base.find(".");
+                               std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
+
+                               if (tsrc->isKnownSourceImage(fname_normal)) {
+                                       // look for image extension and replace it 
+                                       size_t i = 0;
+                                       while ((i = fname_base.find(".", i)) != std::string::npos) {
+                                               fname_base.replace(i, 4, normal_ext);
+                                               i += normal_ext.length();
+                                       }
+                                       material.setTexture(1, tsrc->getTexture(fname_base));
+                                       material.setTexture(2, tsrc->getTexture("enable_img.png"));
+                               }
+                       }
+                       p.tile.applyMaterialOptionsWithShaders(material,
+                               shadermat1, shadermat2, shadermat3, shadermat4, shadermat5);
+               } else {
+                       p.tile.applyMaterialOptions(material);
+               }
                // Create meshbuffer
 
                // This is a "Standard MeshBuffer",
@@ -994,8 +1244,8 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
                m_mesh->addMeshBuffer(buf);
                // Mesh grabbed it
                buf->drop();
-               buf->append(p.vertices.pointer(), p.vertices.size(),
-                               p.indices.pointer(), p.indices.size());
+               buf->append(&p.vertices[0], p.vertices.size(),
+                               &p.indices[0], p.indices.size());
        }
 
        /*
@@ -1003,7 +1253,6 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
        */
 
        translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
-       m_mesh->recalculateBoundingBox(); // translateMesh already does this
 
        if(m_mesh)
        {
@@ -1030,7 +1279,8 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data):
        // Check if animation is required for this mesh
        m_has_animation =
                !m_crack_materials.empty() ||
-               !m_daynight_diffs.empty();
+               !m_daynight_diffs.empty() ||
+               !m_animation_tiles.empty();
 }
 
 MapBlockMesh::~MapBlockMesh()
@@ -1041,6 +1291,10 @@ MapBlockMesh::~MapBlockMesh()
 
 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
 {
+       bool enable_shaders = g_settings->getBool("enable_shaders");
+       bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
+       bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
+
        if(!m_has_animation)
        {
                m_animation_force_timer = 100000;
@@ -1063,12 +1317,69 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
                        ITextureSource *tsrc = m_gamedef->getTextureSource();
                        std::ostringstream os;
                        os<<basename<<crack;
-                       AtlasPointer ap = tsrc->getTexture(os.str());
-                       buf->getMaterial().setTexture(0, ap.atlas);
+                       u32 new_texture_id = 0;
+                       video::ITexture *new_texture =
+                               tsrc->getTexture(os.str(), &new_texture_id);
+                       buf->getMaterial().setTexture(0, new_texture);
+
+                       // If the current material is also animated,
+                       // update animation info
+                       std::map<u32, TileSpec>::iterator anim_iter =
+                               m_animation_tiles.find(i->first);
+                       if(anim_iter != m_animation_tiles.end()){
+                               TileSpec &tile = anim_iter->second;
+                               tile.texture = new_texture;
+                               tile.texture_id = new_texture_id;
+                               // force animation update
+                               m_animation_frames[i->first] = -1;
+                       }
                }
 
                m_last_crack = crack;
        }
+       
+       // Texture animation
+       for(std::map<u32, TileSpec>::iterator
+                       i = m_animation_tiles.begin();
+                       i != m_animation_tiles.end(); i++)
+       {
+               const TileSpec &tile = i->second;
+               // Figure out current frame
+               int frameoffset = m_animation_frame_offsets[i->first];
+               int frame = (int)(time * 1000 / tile.animation_frame_length_ms
+                               + frameoffset) % tile.animation_frame_count;
+               // If frame doesn't change, skip
+               if(frame == m_animation_frames[i->first])
+                       continue;
+
+               m_animation_frames[i->first] = frame;
+
+               scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
+               ITextureSource *tsrc = m_gamedef->getTextureSource();
+
+               // Create new texture name from original
+               std::ostringstream os(std::ios::binary);
+               os<<tsrc->getTextureName(tile.texture_id);
+               os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
+               // Set the texture
+               buf->getMaterial().setTexture(0, tsrc->getTexture(os.str()));
+               buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
+               if (enable_shaders && (enable_bumpmapping || enable_parallax_occlusion))
+                       {
+                               std::string fname_base,fname_normal;
+                               fname_base = tsrc->getTextureName(tile.texture_id);
+                               unsigned pos;
+                               pos = fname_base.find(".");
+                               fname_normal = fname_base.substr (0, pos);
+                               fname_normal += "_normal.png";
+                               if (tsrc->isKnownSourceImage(fname_normal)){
+                                       os.str("");
+                                       os<<fname_normal<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
+                                       buf->getMaterial().setTexture(1, tsrc->getTexture(os.str()));
+                                       buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
+                               }
+                       }
+       }
 
        // Day-night transition
        if(daynight_ratio != m_last_daynight_ratio)
@@ -1088,6 +1399,14 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
                                u8 night = j->second.second;
                                finalColorBlend(vertices[vertexIndex].Color,
                                                day, night, daynight_ratio);
+                               // Brighten topside (no shaders)
+                               if(vertices[vertexIndex].Normal.Y > 0.5)
+                               {
+                                       video::SColor &vc = vertices[vertexIndex].Color;
+                                       vc.setRed  (srgb_linear_multiply(vc.getRed(),   1.3, 255.0));
+                                       vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
+                                       vc.setBlue (srgb_linear_multiply(vc.getBlue(),  1.3, 255.0));
+                               }
                        }
                }
                m_last_daynight_ratio = daynight_ratio;
@@ -1104,12 +1423,20 @@ void MeshCollector::append(const TileSpec &tile,
                const video::S3DVertex *vertices, u32 numVertices,
                const u16 *indices, u32 numIndices)
 {
+       if(numIndices > 65535)
+       {
+               dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
+               return;
+       }
+
        PreMeshBuffer *p = NULL;
        for(u32 i=0; i<prebuffers.size(); i++)
        {
                PreMeshBuffer &pp = prebuffers[i];
                if(pp.tile != tile)
                        continue;
+               if(pp.indices.size() + numIndices > 65535)
+                       continue;
 
                p = &pp;
                break;
@@ -1127,11 +1454,6 @@ void MeshCollector::append(const TileSpec &tile,
        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++)