Soft node overlay (#5186)
authorDániel Juhász <juhdanad@gmail.com>
Fri, 21 Apr 2017 13:34:59 +0000 (15:34 +0200)
committerZeno- <kde.psych@gmail.com>
Fri, 21 Apr 2017 13:34:59 +0000 (23:34 +1000)
This commit adds node overlays, which are tiles that are drawn on top of
other tiles.

18 files changed:
doc/lua_api.txt
src/client.cpp
src/client/tile.h
src/clientmap.cpp
src/content_mapblock.cpp
src/content_mapblock.h
src/hud.cpp
src/mapblock_mesh.cpp
src/mapblock_mesh.h
src/mesh.cpp
src/mesh.h
src/network/networkprotocol.h
src/nodedef.cpp
src/nodedef.h
src/particles.cpp
src/script/common/c_content.cpp
src/wieldmesh.cpp
src/wieldmesh.h

index 16e662e0c069580634b3d68003a0eaaab551c44b..4e328ac765a293a23441051d406bdf9fe137bd3f 100644 (file)
@@ -3908,6 +3908,12 @@ Definition tables
         tiles = {tile definition 1, def2, def3, def4, def5, def6}, --[[
         ^ Textures of node; +Y, -Y, +X, -X, +Z, -Z (old field name: tile_images)
         ^ List can be shortened to needed length ]]
+        overlay_tiles = {tile definition 1, def2, def3, def4, def5, def6}, --[[
+        ^ Same as `tiles`, but these textures are drawn on top of the
+        ^ base tiles. You can use this to colorize only specific parts of
+        ^ your texture. If the texture name is an empty string, that
+        ^ overlay is not drawn. Since such tiles are drawn twice, it
+        ^ is not recommended to use overlays on very common nodes.
         special_tiles = {tile definition 1, Tile definition 2}, --[[
         ^ Special textures of node; used rarely (old field name: special_materials)
         ^ List can be shortened to needed length ]]
index 3cea4fbf40079559197bc7cb0b09e12cf1cd8c61..ce42d025efbe0ff87723141534a4b2460615c9de 100644 (file)
@@ -487,13 +487,17 @@ void Client::step(float dtime)
                                        minimap_mapblock = r.mesh->moveMinimapMapblock();
                                        if (minimap_mapblock == NULL)
                                                do_mapper_update = false;
-                               }
 
-                               if (r.mesh && r.mesh->getMesh()->getMeshBufferCount() == 0) {
-                                       delete r.mesh;
-                               } else {
-                                       // Replace with the new mesh
-                                       block->mesh = r.mesh;
+                                       bool is_empty = true;
+                                       for (int l = 0; l < MAX_TILE_LAYERS; l++)
+                                               if (r.mesh->getMesh(l)->getMeshBufferCount() != 0)
+                                                       is_empty = false;
+
+                                       if (is_empty)
+                                               delete r.mesh;
+                                       else
+                                               // Replace with the new mesh
+                                               block->mesh = r.mesh;
                                }
                        } else {
                                delete r.mesh;
index ef01d2a1fbaf8832cf14a2f52900296a9c28a9fa..c6ebee00621bff812345fff1c9147fb16b66ce2d 100644 (file)
@@ -194,19 +194,22 @@ struct FrameSpec
        video::ITexture *flags_texture;
 };
 
-struct TileSpec
+#define MAX_TILE_LAYERS 2
+
+//! Defines a layer of a tile.
+struct TileLayer
 {
-       TileSpec():
+       TileLayer():
                texture(NULL),
                texture_id(0),
                color(),
                material_type(TILE_MATERIAL_BASIC),
                material_flags(
                        //0 // <- DEBUG, Use the one below
-                       MATERIAL_FLAG_BACKFACE_CULLING
+                       MATERIAL_FLAG_BACKFACE_CULLING |
+                       MATERIAL_FLAG_TILEABLE_HORIZONTAL|
+                       MATERIAL_FLAG_TILEABLE_VERTICAL
                ),
-               rotation(0),
-               emissive_light(0),
                shader_id(0),
                normal_texture(NULL),
                flags_texture(NULL),
@@ -217,49 +220,41 @@ struct TileSpec
        }
 
        /*!
-        * Two tiles are equal if they can be appended to
-        * the same mesh buffer.
+        * Two layers are equal if they can be merged.
         */
-       bool operator==(const TileSpec &other) const
+       bool operator==(const TileLayer &other) const
        {
-               return (
+               return
                        texture_id == other.texture_id &&
                        material_type == other.material_type &&
                        material_flags == other.material_flags &&
-                       rotation == other.rotation
-               );
+                       color == other.color;
        }
 
        /*!
-        * Two tiles are not equal if they must be in different mesh buffers.
+        * Two tiles are not equal if they must have different vertices.
         */
-       bool operator!=(const TileSpec &other) const
+       bool operator!=(const TileLayer &other) const
        {
                return !(*this == other);
        }
-       
+
        // Sets everything else except the texture in the material
        void applyMaterialOptions(video::SMaterial &material) const
        {
                switch (material_type) {
                case TILE_MATERIAL_BASIC:
+               case TILE_MATERIAL_WAVING_LEAVES:
+               case TILE_MATERIAL_WAVING_PLANTS:
                        material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
                        break;
                case TILE_MATERIAL_ALPHA:
-                       material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
-                       break;
                case TILE_MATERIAL_LIQUID_TRANSPARENT:
                        material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
                        break;
                case TILE_MATERIAL_LIQUID_OPAQUE:
                        material.MaterialType = video::EMT_SOLID;
                        break;
-               case TILE_MATERIAL_WAVING_LEAVES:
-                       material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       break;
-               case TILE_MATERIAL_WAVING_PLANTS:
-                       material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
-                       break;
                }
                material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING)
                        ? true : false;
@@ -285,26 +280,26 @@ struct TileSpec
                }
        }
 
-       // ordered for performance! please do not reorder unless you pahole it first.
+       bool isTileable() const
+       {
+               return (material_flags & MATERIAL_FLAG_TILEABLE_HORIZONTAL)
+                       && (material_flags & MATERIAL_FLAG_TILEABLE_VERTICAL);
+       }
+
        video::ITexture *texture;
        u32 texture_id;
-       // The color of the tile, or if the tile does not own
-       // a color then the color of the node owning this tile.
+       /*!
+        * The color of the tile, or if the tile does not own
+        * a color then the color of the node owning this tile.
+        */
        video::SColor color;
        // Material parameters
        u8 material_type;
        u8 material_flags;
-
-       u8 rotation;
-       //! This much light does the tile emit.
-       u8 emissive_light;
-
        u32 shader_id;
-
        video::ITexture *normal_texture;
-       // cacheline (64)
-
        video::ITexture *flags_texture;
+
        // Animation parameters
        u16 animation_frame_length_ms;
        u8 animation_frame_count;
@@ -313,4 +308,39 @@ struct TileSpec
 
        std::vector<FrameSpec> frames;
 };
+
+/*!
+ * Defines a face of a node. May have up to two layers.
+ */
+struct TileSpec
+{
+       TileSpec():
+               rotation(0),
+               emissive_light(0)
+       {
+               for (int layer = 0; layer < MAX_TILE_LAYERS; layer++)
+                       layers[layer] = TileLayer();
+       }
+       
+       /*!
+        * Returns true if this tile can be merged with the other tile.
+        */
+       bool isTileable(const TileSpec &other) const {
+               for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+                       if (layers[layer] != other.layers[layer])
+                               return false;
+                       if (!layers[layer].isTileable())
+                               return false;
+               }
+               return rotation == 0
+                       && rotation == other.rotation
+                       && emissive_light == other.emissive_light;
+       }
+
+       u8 rotation;
+       //! This much light does the tile emit.
+       u8 emissive_light;
+       //! The first is base texture, the second is overlay.
+       TileLayer layers[MAX_TILE_LAYERS];
+};
 #endif
index 4cae03bf26d27f814332089b8a332a457cfa8361..6cd24ffc6e74fe11ce8453a3738105541ea9e02f 100644 (file)
@@ -290,6 +290,11 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
 
 struct MeshBufList
 {
+       /*!
+        * Specifies in which layer the list is.
+        * All lists which are in a lower layer are rendered before this list.
+        */
+       u8 layer;
        video::SMaterial m;
        std::vector<scene::IMeshBuffer*> bufs;
 };
@@ -303,7 +308,7 @@ struct MeshBufListList
                lists.clear();
        }
 
-       void add(scene::IMeshBuffer *buf)
+       void add(scene::IMeshBuffer *buf, u8 layer)
        {
                const video::SMaterial &m = buf->getMaterial();
                for(std::vector<MeshBufList>::iterator i = lists.begin();
@@ -315,12 +320,16 @@ struct MeshBufListList
                        if (l.m.TextureLayer[0].Texture != m.TextureLayer[0].Texture)
                                continue;
 
+                       if(l.layer != layer)
+                               continue;
+
                        if (l.m == m) {
                                l.bufs.push_back(buf);
                                return;
                        }
                }
                MeshBufList l;
+               l.layer = layer;
                l.m = m;
                l.bufs.push_back(buf);
                lists.push_back(l);
@@ -434,29 +443,34 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                        MapBlockMesh *mapBlockMesh = block->mesh;
                        assert(mapBlockMesh);
 
-                       scene::IMesh *mesh = mapBlockMesh->getMesh();
-                       assert(mesh);
+                       for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+                               scene::IMesh *mesh = mapBlockMesh->getMesh(layer);
+                               assert(mesh);
 
-                       u32 c = mesh->getMeshBufferCount();
-                       for (u32 i = 0; i < c; i++)
-                       {
-                               scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
+                               u32 c = mesh->getMeshBufferCount();
+                               for (u32 i = 0; i < c; i++) {
+                                       scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
 
-                               video::SMaterial& material = buf->getMaterial();
-                               video::IMaterialRenderer* rnd =
+                                       video::SMaterial& material = buf->getMaterial();
+                                       video::IMaterialRenderer* rnd =
                                                driver->getMaterialRenderer(material.MaterialType);
-                               bool transparent = (rnd && rnd->isTransparent());
-                               if (transparent == is_transparent_pass) {
-                                       if (buf->getVertexCount() == 0)
-                                               errorstream << "Block [" << analyze_block(block)
-                                                        << "] contains an empty meshbuf" << std::endl;
-
-                                       material.setFlag(video::EMF_TRILINEAR_FILTER, m_cache_trilinear_filter);
-                                       material.setFlag(video::EMF_BILINEAR_FILTER, m_cache_bilinear_filter);
-                                       material.setFlag(video::EMF_ANISOTROPIC_FILTER, m_cache_anistropic_filter);
-                                       material.setFlag(video::EMF_WIREFRAME, m_control.show_wireframe);
-
-                                       drawbufs.add(buf);
+                                       bool transparent = (rnd && rnd->isTransparent());
+                                       if (transparent == is_transparent_pass) {
+                                               if (buf->getVertexCount() == 0)
+                                                       errorstream << "Block [" << analyze_block(block)
+                                                               << "] contains an empty meshbuf" << std::endl;
+
+                                               material.setFlag(video::EMF_TRILINEAR_FILTER,
+                                                       m_cache_trilinear_filter);
+                                               material.setFlag(video::EMF_BILINEAR_FILTER,
+                                                       m_cache_bilinear_filter);
+                                               material.setFlag(video::EMF_ANISOTROPIC_FILTER,
+                                                       m_cache_anistropic_filter);
+                                               material.setFlag(video::EMF_WIREFRAME,
+                                                       m_control.show_wireframe);
+
+                                               drawbufs.add(buf, layer);
+                                       }
                                }
                        }
                }
index 153dacf42240346225b8b2a55e924859dfec3552..9f4223bac57b3f8298682b3a5642109d299c2fbd 100644 (file)
@@ -78,17 +78,19 @@ void MapblockMeshGenerator::useTile(int index, bool disable_backface_culling)
 {
        tile = getNodeTileN(n, p, index, data);
        if (!data->m_smooth_lighting)
-               color = encode_light_and_color(light, tile.color, f->light_source);
-       if (disable_backface_culling)
-               tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
-       tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
+               color = encode_light(light, f->light_source);
+       for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+               tile.layers[layer].material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
+               if (disable_backface_culling)
+                       tile.layers[layer].material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
+       }
 }
 
 void MapblockMeshGenerator::useDefaultTile(bool set_color)
 {
        tile = getNodeTile(n, p, v3s16(0, 0, 0), data);
        if (set_color && !data->m_smooth_lighting)
-               color = encode_light_and_color(light, tile.color, f->light_source);
+               color = encode_light(light, f->light_source);
 }
 
 TileSpec MapblockMeshGenerator::getTile(const v3s16& direction)
@@ -106,7 +108,7 @@ void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal)
                vertices[j].Pos = coords[j] + origin;
                vertices[j].Normal = normal2;
                if (data->m_smooth_lighting)
-                       vertices[j].Color = blendLight(coords[j], tile.color);
+                       vertices[j].Color = blendLight(coords[j]);
                else
                        vertices[j].Color = color;
                if (shade_face)
@@ -137,8 +139,7 @@ void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
        video::SColor colors[6];
        if (!data->m_smooth_lighting) {
                for (int face = 0; face != 6; ++face) {
-                       int tileindex = MYMIN(face, tilecount - 1);
-                       colors[face] = encode_light_and_color(light, tiles[tileindex].color, f->light_source);
+                       colors[face] = encode_light(light, f->light_source);
                }
                if (!f->light_source) {
                        applyFacesShading(colors[0], v3f(0, 1, 0));
@@ -240,9 +241,8 @@ void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
 
        if (data->m_smooth_lighting) {
                for (int j = 0; j < 24; ++j) {
-                       int tileindex = MYMIN(j / 4, tilecount - 1);
-                       vertices[j].Color = encode_light_and_color(lights[light_indices[j]],
-                               tiles[tileindex].color, f->light_source);
+                       vertices[j].Color = encode_light(lights[light_indices[j]],
+                               f->light_source);
                        if (!f->light_source)
                                applyFacesShading(vertices[j].Color, vertices[j].Normal);
                }
@@ -289,17 +289,16 @@ u16 MapblockMeshGenerator::blendLight(const v3f &vertex_pos)
 // Calculates vertex color to be used in mapblock mesh
 //  vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
 //  tile_color - node's tile color
-video::SColor MapblockMeshGenerator::blendLight(const v3f &vertex_pos,
-       video::SColor tile_color)
+video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos)
 {
        u16 light = blendLight(vertex_pos);
-       return encode_light_and_color(light, tile_color, f->light_source);
+       return encode_light(light, f->light_source);
 }
 
-video::SColor MapblockMeshGenerator::blendLight(const v3f &vertex_pos,
-       const v3f &vertex_normal, video::SColor tile_color)
+video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos,
+       const v3f &vertex_normal)
 {
-       video::SColor color = blendLight(vertex_pos, tile_color);
+       video::SColor color = blendLight(vertex_pos);
        if (!f->light_source)
                applyFacesShading(color, vertex_normal);
        return color;
@@ -367,8 +366,13 @@ static TileSpec getSpecialTile(const ContentFeatures &f,
        const MapNode &n, u8 i)
 {
        TileSpec copy = f.special_tiles[i];
-       if (!copy.has_color)
-               n.getColor(f, &copy.color);
+       for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
+               TileLayer *layer = &copy.layers[layernum];
+               if (layer->texture_id == 0)
+                       continue;
+               if (!layer->has_color)
+                       n.getColor(f, &(layer->color));
+       }
        return copy;
 }
 
@@ -395,8 +399,8 @@ void MapblockMeshGenerator::prepareLiquidNodeDrawing(bool flowing)
                light = getInteriorLight(ntop, 0, nodedef);
        }
 
-       color_liquid_top = encode_light_and_color(light, tile_liquid_top.color, f->light_source);
-       color = encode_light_and_color(light, tile_liquid.color, f->light_source);
+       color_liquid_top = encode_light(light, f->light_source);
+       color = encode_light(light, f->light_source);
 }
 
 void MapblockMeshGenerator::getLiquidNeighborhood(bool flowing)
@@ -547,7 +551,7 @@ void MapblockMeshGenerator::drawLiquidSides(bool flowing)
                        else
                                pos.Y =     !top_is_same_liquid ? corner_levels[base.Z][base.X] :  0.5 * BS;
                        if (data->m_smooth_lighting)
-                               color = blendLight(pos, tile_liquid.color);
+                               color = blendLightColor(pos);
                        pos += origin;
                        vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v);
                };
@@ -574,7 +578,7 @@ void MapblockMeshGenerator::drawLiquidTop(bool flowing)
                int w = corner_resolve[i][1];
                vertices[i].Pos.Y += corner_levels[w][u];
                if (data->m_smooth_lighting)
-                       vertices[i].Color = blendLight(vertices[i].Pos, tile_liquid_top.color);
+                       vertices[i].Color = blendLightColor(vertices[i].Pos);
                vertices[i].Pos += origin;
        }
 
@@ -659,7 +663,9 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()
                tiles[face] = getTile(g_6dirs[face]);
 
        TileSpec glass_tiles[6];
-       if (tiles[0].texture && tiles[3].texture && tiles[4].texture) {
+       if (tiles[1].layers[0].texture &&
+                       tiles[2].layers[0].texture &&
+                       tiles[3].layers[0].texture) {
                glass_tiles[0] = tiles[4];
                glass_tiles[1] = tiles[0];
                glass_tiles[2] = tiles[4];
@@ -763,7 +769,7 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()
        // Optionally render internal liquid level defined by param2
        // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
        if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
-                       f->special_tiles[0].texture) {
+                       f->special_tiles[0].layers[0].texture) {
                // Internal liquid level has param2 range 0 .. 63,
                // convert it to -0.5 .. 0.5
                float vlev = (param2 / 63.0) * 2.0 - 1.0;
@@ -998,7 +1004,8 @@ void MapblockMeshGenerator::drawFencelikeNode()
 {
        useDefaultTile(false);
        TileSpec tile_nocrack = tile;
-       tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
+       for (int layer = 0; layer < MAX_TILE_LAYERS; layer++)
+               tile_nocrack.layers[layer].material_flags &= ~MATERIAL_FLAG_CRACK;
 
        // Put wood the right way around in the posts
        TileSpec tile_rot = tile;
@@ -1253,7 +1260,7 @@ void MapblockMeshGenerator::drawMeshNode()
                        // vertex right here.
                        for (int k = 0; k < vertex_count; k++) {
                                video::S3DVertex &vertex = vertices[k];
-                               vertex.Color = blendLight(vertex.Pos, vertex.Normal, tile.color);
+                               vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
                                vertex.Pos += origin;
                        }
                        collector->append(tile, vertices, vertex_count,
index c8425024f11f0a2f7c2231290348085c07aa3145..6866a4498373ff777659a2862928532b373df826 100644 (file)
@@ -60,8 +60,8 @@ public:
 // lighting
        void getSmoothLightFrame();
        u16 blendLight(const v3f &vertex_pos);
-       video::SColor blendLight(const v3f &vertex_pos, video::SColor tile_color);
-       video::SColor blendLight(const v3f &vertex_pos, const v3f &vertex_normal, video::SColor tile_color);
+       video::SColor blendLightColor(const v3f &vertex_pos);
+       video::SColor blendLightColor(const v3f &vertex_pos, const v3f &vertex_normal);
 
        void useTile(int index, bool disable_backface_culling);
        void useDefaultTile(bool set_color = true);
index f558acf1ea24c62cc951d675d86a1b3621630a25..c482912e922643e2230ae39e2761d866e8b682d1 100644 (file)
@@ -687,8 +687,9 @@ void drawItemStack(video::IVideoDriver *driver,
                        assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
                        video::SColor c = basecolor;
                        if (imesh->buffer_colors.size() > j) {
-                               std::pair<bool, video::SColor> p = imesh->buffer_colors[j];
-                               c = p.first ? p.second : basecolor;
+                               ItemPartColor *p = &imesh->buffer_colors[j];
+                               if (p->override_base)
+                                       c = p->color;
                        }
                        colorizeMeshBuffer(buf, &c);
                        video::SMaterial &material = buf->getMaterial();
index 933dfc32aa8e5ce4eb9246c06e56123a9e499088..0bba644e6c5568846b435ba556d8ea570f5ae6d4 100644 (file)
@@ -323,7 +323,7 @@ void final_color_blend(video::SColor *result,
        video::SColorf dayLight;
        get_sunlight_color(&dayLight, daynight_ratio);
        final_color_blend(result,
-               encode_light_and_color(light, video::SColor(0xFFFFFFFF), 0), dayLight);
+               encode_light(light, 0), dayLight);
 }
 
 void final_color_blend(video::SColor *result,
@@ -422,12 +422,19 @@ static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
 
 struct FastFace
 {
-       TileSpec tile;
+       TileLayer layer;
        video::S3DVertex vertices[4]; // Precalculated vertices
+       /*!
+        * The face is divided into two triangles. If this is true,
+        * vertices 0 and 2 are connected, othervise vertices 1 and 3
+        * are connected.
+        */
+       bool vertex_0_2_connected;
+       u8 layernum;
 };
 
 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
-               v3f p, v3s16 dir, v3f scale, std::vector<FastFace> &dest)
+       v3f p, v3s16 dir, v3f scale, std::vector<FastFace> &dest)
 {
        // Position is at the center of the cube.
        v3f pos = p * BS;
@@ -577,27 +584,50 @@ static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
 
        v3f normal(dir.X, dir.Y, dir.Z);
 
-       dest.push_back(FastFace());
+       u16 li[4] = { li0, li1, li2, li3 };
+       u16 day[4];
+       u16 night[4];
 
-       FastFace& face = *dest.rbegin();
+       for (u8 i = 0; i < 4; i++) {
+               day[i] = li[i] >> 8;
+               night[i] = li[i] & 0xFF;
+       }
+
+       bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2])
+                       < abs(day[1] - day[3]) + abs(night[1] - night[3]);
 
-       u16 li[4] = { li0, li1, li2, li3 };
        v2f32 f[4] = {
                core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
                core::vector2d<f32>(x0, y0 + h),
                core::vector2d<f32>(x0, y0),
                core::vector2d<f32>(x0 + w * abs_scale, y0) };
 
-       for (u8 i = 0; i < 4; i++) {
-               video::SColor c = encode_light_and_color(li[i], tile.color,
-                       tile.emissive_light);
-               if (!tile.emissive_light)
-                       applyFacesShading(c, normal);
+       for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
+               TileLayer *layer = &tile.layers[layernum];
+               if (layer->texture_id == 0)
+                       continue;
 
-               face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
-       }
+               dest.push_back(FastFace());
+               FastFace& face = *dest.rbegin();
+
+               for (u8 i = 0; i < 4; i++) {
+                       video::SColor c = encode_light(li[i], tile.emissive_light);
+                       if (!tile.emissive_light)
+                               applyFacesShading(c, normal);
 
-       face.tile = tile;
+                       face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
+               }
+
+               /*
+                Revert triangles for nicer looking gradient if the
+                brightness of vertices 1 and 3 differ less than
+                the brightness of vertices 0 and 2.
+                */
+               face.vertex_0_2_connected = vertex_0_2_connected;
+
+               face.layer = *layer;
+               face.layernum = layernum;
+       }
 }
 
 /*
@@ -663,13 +693,20 @@ TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
 {
        INodeDefManager *ndef = data->m_client->ndef();
        const ContentFeatures &f = ndef->get(mn);
-       TileSpec spec = f.tiles[tileindex];
-       if (!spec.has_color)
-               mn.getColor(f, &spec.color);
+       TileSpec tile = f.tiles[tileindex];
+       TileLayer *top_layer = NULL;
+       for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
+               TileLayer *layer = &tile.layers[layernum];
+               if (layer->texture_id == 0)
+                       continue;
+               top_layer = layer;
+               if (!layer->has_color)
+                       mn.getColor(f, &(layer->color));
+       }
        // Apply temporary crack
        if (p == data->m_crack_pos_relative)
-               spec.material_flags |= MATERIAL_FLAG_CRACK;
-       return spec;
+               top_layer->material_flags |= MATERIAL_FLAG_CRACK;
+       return tile;
 }
 
 /*
@@ -732,10 +769,9 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *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_client->tsrc()->getTexture(spec.texture_id);
-       return spec;
+       TileSpec tile = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
+       tile.rotation = dir_to_tile[tile_index + 1];
+       return tile;
 }
 
 static void getTileInfo(
@@ -800,7 +836,9 @@ static void getTileInfo(
 
        // eg. water and glass
        if (equivalent)
-               tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
+               for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++)
+                       tile.layers[layernum].material_flags |=
+                               MATERIAL_FLAG_BACKFACE_CULLING;
 
        if (data->m_smooth_lighting == false)
        {
@@ -880,12 +918,7 @@ static void updateFastFaceRow(
                                        && next_lights[1] == lights[1]
                                        && next_lights[2] == lights[2]
                                        && next_lights[3] == lights[3]
-                                       && next_tile == tile
-                                       && tile.rotation == 0
-                                       && (tile.material_flags & MATERIAL_FLAG_TILEABLE_HORIZONTAL)
-                                       && (tile.material_flags & MATERIAL_FLAG_TILEABLE_VERTICAL)
-                                       && tile.color == next_tile.color
-                                       && tile.emissive_light == next_tile.emissive_light) {
+                                       && next_tile.isTileable(tile)) {
                                next_is_different = false;
                                continuous_tiles_count++;
                        }
@@ -988,7 +1021,6 @@ static void updateAllFastFaceRows(MeshMakeData *data,
 */
 
 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
-       m_mesh(new scene::SMesh()),
        m_minimap_mapblock(NULL),
        m_client(data->m_client),
        m_driver(m_client->tsrc()->getDevice()->getVideoDriver()),
@@ -1000,6 +1032,8 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
        m_last_daynight_ratio((u32) -1),
        m_daynight_diffs()
 {
+       for (int m = 0; m < MAX_TILE_LAYERS; m++)
+               m_mesh[m] = new scene::SMesh();
        m_enable_shaders = data->m_use_shaders;
        m_use_tangent_vertices = data->m_use_tangent_vertices;
        m_enable_vbo = g_settings->getBool("enable_vbo");
@@ -1048,23 +1082,14 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
                        const u16 indices[] = {0,1,2,2,3,0};
                        const u16 indices_alternate[] = {0,1,3,2,3,1};
 
-                       if(f.tile.texture == NULL)
+                       if (f.layer.texture == NULL)
                                continue;
 
-                       const u16 *indices_p = indices;
+                       const u16 *indices_p =
+                               f.vertex_0_2_connected ? indices : indices_alternate;
 
-                       /*
-                               Revert triangles for nicer looking gradient if the
-                               brightness of vertices 1 and 3 differ less than
-                               the brightness of vertices 0 and 2.
-                       */
-                       if (fabs(f.vertices[0].Color.getLuminance()
-                                       - f.vertices[2].Color.getLuminance())
-                                       > fabs(f.vertices[1].Color.getLuminance()
-                                       - f.vertices[3].Color.getLuminance()))
-                               indices_p = indices_alternate;
-
-                       collector.append(f.tile, f.vertices, 4, indices_p, 6);
+                       collector.append(f.layer, f.vertices, 4, indices_p, 6,
+                               f.layernum);
                }
        }
 
@@ -1081,146 +1106,151 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
                generator.generate();
        }
 
+       collector.applyTileColors();
+
        /*
                Convert MeshCollector to SMesh
        */
 
-       for(u32 i = 0; i < collector.prebuffers.size(); i++)
-       {
-               PreMeshBuffer &p = collector.prebuffers[i];
-
-               // Generate animation data
-               // - Cracks
-               if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
+       for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+               for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
                {
-                       // Find the texture name plus ^[crack:N:
-                       std::ostringstream os(std::ios::binary);
-                       os<<m_tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
-                       if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
-                               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 = m_tsrc->getTextureForMesh(
-                                       os.str()+"0",
-                                       &p.tile.texture_id);
-               }
-               // - Texture animation
-               if (p.tile.material_flags & MATERIAL_FLAG_ANIMATION) {
-                       // 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
-                       FrameSpec animation_frame = p.tile.frames[0];
-                       p.tile.texture = animation_frame.texture;
-               }
+                       PreMeshBuffer &p = collector.prebuffers[layer][i];
 
-               if (!m_enable_shaders) {
-                       // Extract colors for day-night animation
-                       // Dummy sunlight to handle non-sunlit areas
-                       video::SColorf sunlight;
-                       get_sunlight_color(&sunlight, 0);
-                       u32 vertex_count =
-                               m_use_tangent_vertices ?
-                                       p.tangent_vertices.size() : p.vertices.size();
-                       for (u32 j = 0; j < vertex_count; j++) {
-                               video::SColor *vc;
-                               if (m_use_tangent_vertices) {
-                                       vc = &p.tangent_vertices[j].Color;
+                       // Generate animation data
+                       // - Cracks
+                       if(p.layer.material_flags & MATERIAL_FLAG_CRACK)
+                       {
+                               // Find the texture name plus ^[crack:N:
+                               std::ostringstream os(std::ios::binary);
+                               os<<m_tsrc->getTextureName(p.layer.texture_id)<<"^[crack";
+                               if(p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
+                                       os<<"o";  // use ^[cracko
+                               os<<":"<<(u32)p.layer.animation_frame_count<<":";
+                               m_crack_materials.insert(std::make_pair(std::pair<u8, u32>(layer, i), os.str()));
+                               // Replace tile texture with the cracked one
+                               p.layer.texture = m_tsrc->getTextureForMesh(
+                                               os.str()+"0",
+                                               &p.layer.texture_id);
+                       }
+                       // - Texture animation
+                       if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
+                               // Add to MapBlockMesh in order to animate these tiles
+                               m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
+                               m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
+                               if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
+                                       // Get starting position from noise
+                                       m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 100000 * (2.0 + noise3d(
+                                                       data->m_blockpos.X, data->m_blockpos.Y,
+                                                       data->m_blockpos.Z, 0));
                                } else {
-                                       vc = &p.vertices[j].Color;
+                                       // Play all synchronized
+                                       m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
                                }
-                               video::SColor copy(*vc);
-                               if (vc->getAlpha() == 0) // No sunlight - no need to animate
-                                       final_color_blend(vc, copy, sunlight); // Finalize color
-                               else // Record color to animate
-                                       m_daynight_diffs[i][j] = copy;
-
-                               // The sunlight ratio has been stored,
-                               // delete alpha (for the final rendering).
-                               vc->setAlpha(255);
+                               // Replace tile texture with the first animation frame
+                               FrameSpec animation_frame = p.layer.frames[0];
+                               p.layer.texture = animation_frame.texture;
                        }
-               }
 
-               // Create material
-               video::SMaterial material;
-               material.setFlag(video::EMF_LIGHTING, false);
-               material.setFlag(video::EMF_BACK_FACE_CULLING, true);
-               material.setFlag(video::EMF_BILINEAR_FILTER, false);
-               material.setFlag(video::EMF_FOG_ENABLE, true);
-               material.setTexture(0, p.tile.texture);
+                       if (!m_enable_shaders) {
+                               // Extract colors for day-night animation
+                               // Dummy sunlight to handle non-sunlit areas
+                               video::SColorf sunlight;
+                               get_sunlight_color(&sunlight, 0);
+                               u32 vertex_count =
+                                       m_use_tangent_vertices ?
+                                               p.tangent_vertices.size() : p.vertices.size();
+                               for (u32 j = 0; j < vertex_count; j++) {
+                                       video::SColor *vc;
+                                       if (m_use_tangent_vertices) {
+                                               vc = &p.tangent_vertices[j].Color;
+                                       } else {
+                                               vc = &p.vertices[j].Color;
+                                       }
+                                       video::SColor copy(*vc);
+                                       if (vc->getAlpha() == 0) // No sunlight - no need to animate
+                                               final_color_blend(vc, copy, sunlight); // Finalize color
+                                       else // Record color to animate
+                                               m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
+
+                                       // The sunlight ratio has been stored,
+                                       // delete alpha (for the final rendering).
+                                       vc->setAlpha(255);
+                               }
+                       }
 
-               if (m_enable_shaders) {
-                       material.MaterialType = m_shdrsrc->getShaderInfo(p.tile.shader_id).material;
-                       p.tile.applyMaterialOptionsWithShaders(material);
-                       if (p.tile.normal_texture) {
-                               material.setTexture(1, p.tile.normal_texture);
+                       // Create material
+                       video::SMaterial material;
+                       material.setFlag(video::EMF_LIGHTING, false);
+                       material.setFlag(video::EMF_BACK_FACE_CULLING, true);
+                       material.setFlag(video::EMF_BILINEAR_FILTER, false);
+                       material.setFlag(video::EMF_FOG_ENABLE, true);
+                       material.setTexture(0, p.layer.texture);
+
+                       if (m_enable_shaders) {
+                               material.MaterialType = m_shdrsrc->getShaderInfo(p.layer.shader_id).material;
+                               p.layer.applyMaterialOptionsWithShaders(material);
+                               if (p.layer.normal_texture) {
+                                       material.setTexture(1, p.layer.normal_texture);
+                               }
+                               material.setTexture(2, p.layer.flags_texture);
+                       } else {
+                               p.layer.applyMaterialOptions(material);
+                       }
+
+                       scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
+
+                       // Create meshbuffer, add to mesh
+                       if (m_use_tangent_vertices) {
+                               scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
+                               // Set material
+                               buf->Material = material;
+                               // Add to mesh
+                               mesh->addMeshBuffer(buf);
+                               // Mesh grabbed it
+                               buf->drop();
+                               buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(),
+                                       &p.indices[0], p.indices.size());
+                       } else {
+                               scene::SMeshBuffer *buf = new scene::SMeshBuffer();
+                               // Set material
+                               buf->Material = material;
+                               // Add to mesh
+                               mesh->addMeshBuffer(buf);
+                               // Mesh grabbed it
+                               buf->drop();
+                               buf->append(&p.vertices[0], p.vertices.size(),
+                                       &p.indices[0], p.indices.size());
                        }
-                       material.setTexture(2, p.tile.flags_texture);
-               } else {
-                       p.tile.applyMaterialOptions(material);
                }
 
-               scene::SMesh *mesh = (scene::SMesh *)m_mesh;
 
-               // Create meshbuffer, add to mesh
+               /*
+                       Do some stuff to the mesh
+               */
+               m_camera_offset = camera_offset;
+               translateMesh(m_mesh[layer],
+                       intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
+
                if (m_use_tangent_vertices) {
-                       scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
-                       // Set material
-                       buf->Material = material;
-                       // Add to mesh
-                       mesh->addMeshBuffer(buf);
-                       // Mesh grabbed it
-                       buf->drop();
-                       buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(),
-                               &p.indices[0], p.indices.size());
-               } else {
-                       scene::SMeshBuffer *buf = new scene::SMeshBuffer();
-                       // Set material
-                       buf->Material = material;
-                       // Add to mesh
-                       mesh->addMeshBuffer(buf);
-                       // Mesh grabbed it
-                       buf->drop();
-                       buf->append(&p.vertices[0], p.vertices.size(),
-                               &p.indices[0], p.indices.size());
+                       scene::IMeshManipulator* meshmanip =
+                               m_client->getSceneManager()->getMeshManipulator();
+                       meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
                }
-       }
-
-       /*
-               Do some stuff to the mesh
-       */
-       m_camera_offset = camera_offset;
-       translateMesh(m_mesh,
-               intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
 
-       if (m_use_tangent_vertices) {
-               scene::IMeshManipulator* meshmanip =
-                       m_client->getSceneManager()->getMeshManipulator();
-               meshmanip->recalculateTangents(m_mesh, true, false, false);
-       }
-
-       if (m_mesh)
-       {
+               if (m_mesh[layer])
+               {
 #if 0
-               // Usually 1-700 faces and 1-7 materials
-               std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
-                               <<"and uses "<<m_mesh->getMeshBufferCount()
-                               <<" materials (meshbuffers)"<<std::endl;
+                       // Usually 1-700 faces and 1-7 materials
+                       std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
+                                       <<"and uses "<<m_mesh[layer]->getMeshBufferCount()
+                                       <<" materials (meshbuffers)"<<std::endl;
 #endif
 
-               // Use VBO for mesh (this just would set this for ever buffer)
-               if (m_enable_vbo) {
-                       m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
+                       // Use VBO for mesh (this just would set this for ever buffer)
+                       if (m_enable_vbo) {
+                               m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
+                       }
                }
        }
 
@@ -1235,14 +1265,15 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
 
 MapBlockMesh::~MapBlockMesh()
 {
-       if (m_enable_vbo && m_mesh) {
-               for (u32 i = 0; i < m_mesh->getMeshBufferCount(); i++) {
-                       scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i);
-                       m_driver->removeHardwareBuffer(buf);
-               }
+       for (int m = 0; m < MAX_TILE_LAYERS; m++) {
+               if (m_enable_vbo && m_mesh[m])
+                       for (u32 i = 0; i < m_mesh[m]->getMeshBufferCount(); i++) {
+                               scene::IMeshBuffer *buf = m_mesh[m]->getMeshBuffer(i);
+                               m_driver->removeHardwareBuffer(buf);
+                       }
+               m_mesh[m]->drop();
+               m_mesh[m] = NULL;
        }
-       m_mesh->drop();
-       m_mesh = NULL;
        delete m_minimap_mapblock;
 }
 
@@ -1259,9 +1290,10 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
        // Cracks
        if(crack != m_last_crack)
        {
-               for (UNORDERED_MAP<u32, std::string>::iterator i = m_crack_materials.begin();
-                               i != m_crack_materials.end(); ++i) {
-                       scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
+               for (std::map<std::pair<u8, u32>, std::string>::iterator i =
+                               m_crack_materials.begin(); i != m_crack_materials.end(); ++i) {
+                       scene::IMeshBuffer *buf = m_mesh[i->first.first]->
+                               getMeshBuffer(i->first.second);
                        std::string basename = i->second;
 
                        // Create new texture name from original
@@ -1274,10 +1306,10 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
 
                        // If the current material is also animated,
                        // update animation info
-                       UNORDERED_MAP<u32, TileSpec>::iterator anim_iter =
-                                       m_animation_tiles.find(i->first);
+                       std::map<std::pair<u8, u32>, TileLayer>::iterator anim_iter =
+                               m_animation_tiles.find(i->first);
                        if (anim_iter != m_animation_tiles.end()){
-                               TileSpec &tile = anim_iter->second;
+                               TileLayer &tile = anim_iter->second;
                                tile.texture = new_texture;
                                tile.texture_id = new_texture_id;
                                // force animation update
@@ -1289,9 +1321,9 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
        }
 
        // Texture animation
-       for (UNORDERED_MAP<u32, TileSpec>::iterator i = m_animation_tiles.begin();
-                       i != m_animation_tiles.end(); ++i) {
-               const TileSpec &tile = i->second;
+       for (std::map<std::pair<u8, u32>, TileLayer>::iterator i =
+                       m_animation_tiles.begin(); i != m_animation_tiles.end(); ++i) {
+               const TileLayer &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
@@ -1302,7 +1334,8 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
 
                m_animation_frames[i->first] = frame;
 
-               scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
+               scene::IMeshBuffer *buf = m_mesh[i->first.first]->
+                       getMeshBuffer(i->first.second);
 
                FrameSpec animation_frame = tile.frames[frame];
                buf->getMaterial().setTexture(0, animation_frame.texture);
@@ -1318,22 +1351,24 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
        if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
        {
                // Force reload mesh to VBO
-               if (m_enable_vbo) {
-                       m_mesh->setDirty();
-               }
+               if (m_enable_vbo)
+                       for (int m = 0; m < MAX_TILE_LAYERS; m++)
+                               m_mesh[m]->setDirty();
                video::SColorf day_color;
                get_sunlight_color(&day_color, daynight_ratio);
-               for(std::map<u32, std::map<u32, video::SColor > >::iterator
+               for(std::map<std::pair<u8, u32>, std::map<u32, video::SColor > >::iterator
                                i = m_daynight_diffs.begin();
                                i != m_daynight_diffs.end(); ++i)
                {
-                       scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
+                       scene::IMeshBuffer *buf = m_mesh[i->first.first]->
+                               getMeshBuffer(i->first.second);
                        video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
                        for(std::map<u32, video::SColor >::iterator
                                        j = i->second.begin();
                                        j != i->second.end(); ++j)
                        {
-                               final_color_blend(&(vertices[j->first].Color), j->second, day_color);
+                               final_color_blend(&(vertices[j->first].Color),
+                                       j->second, day_color);
                        }
                }
                m_last_daynight_ratio = daynight_ratio;
@@ -1345,9 +1380,12 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
 {
        if (camera_offset != m_camera_offset) {
-               translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
-               if (m_enable_vbo) {
-                       m_mesh->setDirty();
+               for (u8 layer = 0; layer < 2; layer++) {
+                       translateMesh(m_mesh[layer],
+                               intToFloat(m_camera_offset - camera_offset, BS));
+                       if (m_enable_vbo) {
+                               m_mesh[layer]->setDirty();
+                       }
                }
                m_camera_offset = camera_offset;
        }
@@ -1360,16 +1398,30 @@ void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
 void MeshCollector::append(const TileSpec &tile,
                const video::S3DVertex *vertices, u32 numVertices,
                const u16 *indices, u32 numIndices)
+{
+       for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
+               const TileLayer *layer = &tile.layers[layernum];
+               if (layer->texture_id == 0)
+                       continue;
+               append(*layer, vertices, numVertices, indices, numIndices,
+                       layernum);
+       }
+}
+
+void MeshCollector::append(const TileLayer &layer,
+               const video::S3DVertex *vertices, u32 numVertices,
+               const u16 *indices, u32 numIndices, u8 layernum)
 {
        if (numIndices > 65535) {
                dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
                return;
        }
+       std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
 
        PreMeshBuffer *p = NULL;
-       for (u32 i = 0; i < prebuffers.size(); i++) {
-               PreMeshBuffer &pp = prebuffers[i];
-               if (pp.tile != tile)
+       for (u32 i = 0; i < buffers->size(); i++) {
+               PreMeshBuffer &pp = (*buffers)[i];
+               if (pp.layer != layer)
                        continue;
                if (pp.indices.size() + numIndices > 65535)
                        continue;
@@ -1380,9 +1432,9 @@ void MeshCollector::append(const TileSpec &tile,
 
        if (p == NULL) {
                PreMeshBuffer pp;
-               pp.tile = tile;
-               prebuffers.push_back(pp);
-               p = &prebuffers[prebuffers.size() - 1];
+               pp.layer = layer;
+               buffers->push_back(pp);
+               p = &(*buffers)[buffers->size() - 1];
        }
 
        u32 vertex_count;
@@ -1416,16 +1468,31 @@ void MeshCollector::append(const TileSpec &tile,
                const video::S3DVertex *vertices, u32 numVertices,
                const u16 *indices, u32 numIndices,
                v3f pos, video::SColor c, u8 light_source)
+{
+       for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
+               const TileLayer *layer = &tile.layers[layernum];
+               if (layer->texture_id == 0)
+                       continue;
+               append(*layer, vertices, numVertices, indices, numIndices, pos,
+                       c, light_source, layernum);
+       }
+}
+
+void MeshCollector::append(const TileLayer &layer,
+               const video::S3DVertex *vertices, u32 numVertices,
+               const u16 *indices, u32 numIndices,
+               v3f pos, video::SColor c, u8 light_source, u8 layernum)
 {
        if (numIndices > 65535) {
                dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
                return;
        }
+       std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
 
        PreMeshBuffer *p = NULL;
-       for (u32 i = 0; i < prebuffers.size(); i++) {
-               PreMeshBuffer &pp = prebuffers[i];
-               if(pp.tile != tile)
+       for (u32 i = 0; i < buffers->size(); i++) {
+               PreMeshBuffer &pp = (*buffers)[i];
+               if(pp.layer != layer)
                        continue;
                if(pp.indices.size() + numIndices > 65535)
                        continue;
@@ -1436,9 +1503,9 @@ void MeshCollector::append(const TileSpec &tile,
 
        if (p == NULL) {
                PreMeshBuffer pp;
-               pp.tile = tile;
-               prebuffers.push_back(pp);
-               p = &prebuffers[prebuffers.size() - 1];
+               pp.layer = layer;
+               buffers->push_back(pp);
+               p = &(*buffers)[buffers->size() - 1];
        }
 
        video::SColor original_c = c;
@@ -1473,14 +1540,49 @@ void MeshCollector::append(const TileSpec &tile,
        }
 }
 
-video::SColor encode_light_and_color(u16 light, const video::SColor &color,
-       u8 emissive_light)
+void MeshCollector::applyTileColors()
+{
+       if (m_use_tangent_vertices)
+               for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+                       std::vector<PreMeshBuffer> *p = &prebuffers[layer];
+                       for (std::vector<PreMeshBuffer>::iterator it = p->begin();
+                                       it != p->end(); ++it) {
+                               video::SColor tc = it->layer.color;
+                               if (tc == video::SColor(0xFFFFFFFF))
+                                       continue;
+                               for (u32 index = 0; index < it->tangent_vertices.size(); index++) {
+                                       video::SColor *c = &it->tangent_vertices[index].Color;
+                                       c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
+                                               c->getGreen() * tc.getGreen() / 255,
+                                               c->getBlue() * tc.getBlue() / 255);
+                               }
+                       }
+               }
+       else
+               for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
+                       std::vector<PreMeshBuffer> *p = &prebuffers[layer];
+                       for (std::vector<PreMeshBuffer>::iterator it = p->begin();
+                                       it != p->end(); ++it) {
+                               video::SColor tc = it->layer.color;
+                               if (tc == video::SColor(0xFFFFFFFF))
+                                       continue;
+                               for (u32 index = 0; index < it->vertices.size(); index++) {
+                                       video::SColor *c = &it->vertices[index].Color;
+                                       c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
+                                               c->getGreen() * tc.getGreen() / 255,
+                                               c->getBlue() * tc.getBlue() / 255);
+                               }
+                       }
+               }
+}
+
+video::SColor encode_light(u16 light, u8 emissive_light)
 {
        // Get components
-       f32 day = (light & 0xff) / 255.0f;
-       f32 night = (light >> 8) / 255.0f;
+       u32 day = (light & 0xff);
+       u32 night = (light >> 8);
        // Add emissive light
-       night += emissive_light * 0.01f;
+       night += emissive_light * 2.5f;
        if (night > 255)
                night = 255;
        // Since we don't know if the day light is sunlight or
@@ -1490,15 +1592,14 @@ video::SColor encode_light_and_color(u16 light, const video::SColor &color,
                day = 0;
        else
                day = day - night;
-       f32 sum = day + night;
+       u32 sum = day + night;
        // Ratio of sunlight:
-       float r;
+       u32 r;
        if (sum > 0)
-               r = day / sum;
+               r = day * 255 / sum;
        else
                r = 0;
        // Average light:
        float b = (day + night) / 2;
-       return video::SColor(r * 255, b * color.getRed(), b * color.getGreen(),
-               b * color.getBlue());
+       return video::SColor(r, b, b, b);
 }
index 25c699e1cab24d2ba3520e13d0f698795d24ba5b..f32df39581b8428f7237a6325e65c255fcb3edb1 100644 (file)
@@ -108,7 +108,12 @@ public:
 
        scene::IMesh *getMesh()
        {
-               return m_mesh;
+               return m_mesh[0];
+       }
+
+       scene::IMesh *getMesh(u8 layer)
+       {
+               return m_mesh[layer];
        }
 
        MinimapMapblock *moveMinimapMapblock()
@@ -132,7 +137,7 @@ public:
        void updateCameraOffset(v3s16 camera_offset);
 
 private:
-       scene::IMesh *m_mesh;
+       scene::IMesh *m_mesh[MAX_TILE_LAYERS];
        MinimapMapblock *m_minimap_mapblock;
        Client *m_client;
        video::IVideoDriver *m_driver;
@@ -150,20 +155,23 @@ private:
        // Animation info: cracks
        // Last crack value passed to animate()
        int m_last_crack;
-       // Maps mesh buffer (i.e. material) indices to base texture names
-       UNORDERED_MAP<u32, std::string> m_crack_materials;
+       // Maps mesh and mesh buffer (i.e. material) indices to base texture names
+       std::map<std::pair<u8, u32>, std::string> m_crack_materials;
 
        // Animation info: texture animationi
-       // Maps meshbuffers to TileSpecs
-       UNORDERED_MAP<u32, TileSpec> m_animation_tiles;
-       UNORDERED_MAP<u32, int> m_animation_frames; // last animation frame
-       UNORDERED_MAP<u32, int> m_animation_frame_offsets;
+       // Maps mesh and mesh buffer indices to TileSpecs
+       // Keys are pairs of (mesh index, buffer index in the mesh)
+       std::map<std::pair<u8, u32>, TileLayer> m_animation_tiles;
+       std::map<std::pair<u8, u32>, int> m_animation_frames; // last animation frame
+       std::map<std::pair<u8, u32>, int> m_animation_frame_offsets;
 
        // Animation info: day/night transitions
        // Last daynight_ratio value passed to animate()
        u32 m_last_daynight_ratio;
-       // For each meshbuffer, stores pre-baked colors of sunlit vertices
-       std::map<u32, std::map<u32, video::SColor > > m_daynight_diffs;
+       // For each mesh and mesh buffer, stores pre-baked colors
+       // of sunlit vertices
+       // Keys are pairs of (mesh index, buffer index in the mesh)
+       std::map<std::pair<u8, u32>, std::map<u32, video::SColor > > m_daynight_diffs;
 
        // Camera offset info -> do we have to translate the mesh?
        v3s16 m_camera_offset;
@@ -176,7 +184,7 @@ private:
 */
 struct PreMeshBuffer
 {
-       TileSpec tile;
+       TileLayer layer;
        std::vector<u16> indices;
        std::vector<video::S3DVertex> vertices;
        std::vector<video::S3DVertexTangents> tangent_vertices;
@@ -184,7 +192,7 @@ struct PreMeshBuffer
 
 struct MeshCollector
 {
-       std::vector<PreMeshBuffer> prebuffers;
+       std::vector<PreMeshBuffer> prebuffers[MAX_TILE_LAYERS];
        bool m_use_tangent_vertices;
 
        MeshCollector(bool use_tangent_vertices):
@@ -193,27 +201,38 @@ struct MeshCollector
        }
 
        void append(const TileSpec &material,
+                               const video::S3DVertex *vertices, u32 numVertices,
+                               const u16 *indices, u32 numIndices);
+       void append(const TileLayer &material,
                        const video::S3DVertex *vertices, u32 numVertices,
-                       const u16 *indices, u32 numIndices);
+                       const u16 *indices, u32 numIndices, u8 layernum);
        void append(const TileSpec &material,
+                               const video::S3DVertex *vertices, u32 numVertices,
+                               const u16 *indices, u32 numIndices, v3f pos,
+                               video::SColor c, u8 light_source);
+       void append(const TileLayer &material,
                        const video::S3DVertex *vertices, u32 numVertices,
-                       const u16 *indices, u32 numIndices,
-                       v3f pos, video::SColor c, u8 light_source);
+                       const u16 *indices, u32 numIndices, v3f pos,
+                       video::SColor c, u8 light_source, u8 layernum);
+       /*!
+        * Colorizes all vertices in the collector.
+        */
+       void applyTileColors();
 };
 
 /*!
- * Encodes light and color of a node.
+ * Encodes light of a node.
  * The result is not the final color, but a
  * half-baked vertex color.
+ * You have to multiply the resulting color
+ * with the node's color.
  *
  * \param light the first 8 bits are day light,
  * the last 8 bits are night light
- * \param color the node's color
  * \param emissive_light amount of light the surface emits,
  * from 0 to LIGHT_SUN.
  */
-video::SColor encode_light_and_color(u16 light, const video::SColor &color,
-       u8 emissive_light);
+video::SColor encode_light(u16 light, u8 emissive_light);
 
 // Compute light at node
 u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef);
index a79264ef0d0d976f73dcb7120f6a3d3f1c1416c4..d776f61855834bfee120f7c9449306a8994e0568 100644 (file)
@@ -385,48 +385,50 @@ void recalculateBoundingBox(scene::IMesh *src_mesh)
        src_mesh->setBoundingBox(bbox);
 }
 
-scene::IMesh* cloneMesh(scene::IMesh *src_mesh)
+scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer)
+{
+       scene::IMeshBuffer *clone = NULL;
+       switch (mesh_buffer->getVertexType()) {
+       case video::EVT_STANDARD: {
+               video::S3DVertex *v = (video::S3DVertex *) mesh_buffer->getVertices();
+               u16 *indices = (u16*) mesh_buffer->getIndices();
+               scene::SMeshBuffer *temp_buf = new scene::SMeshBuffer();
+               temp_buf->append(v, mesh_buffer->getVertexCount(), indices,
+                       mesh_buffer->getIndexCount());
+               return temp_buf;
+               break;
+       }
+       case video::EVT_2TCOORDS: {
+               video::S3DVertex2TCoords *v =
+                       (video::S3DVertex2TCoords *) mesh_buffer->getVertices();
+               u16 *indices = (u16*) mesh_buffer->getIndices();
+               scene::SMeshBufferTangents *temp_buf = new scene::SMeshBufferTangents();
+               temp_buf->append(v, mesh_buffer->getVertexCount(), indices,
+                       mesh_buffer->getIndexCount());
+               break;
+       }
+       case video::EVT_TANGENTS: {
+               video::S3DVertexTangents *v =
+                       (video::S3DVertexTangents *) mesh_buffer->getVertices();
+               u16 *indices = (u16*) mesh_buffer->getIndices();
+               scene::SMeshBufferTangents *temp_buf = new scene::SMeshBufferTangents();
+               temp_buf->append(v, mesh_buffer->getVertexCount(), indices,
+                       mesh_buffer->getIndexCount());
+               break;
+       }
+       }
+       return clone;
+}
+
+scene::SMesh* cloneMesh(scene::IMesh *src_mesh)
 {
        scene::SMesh* dst_mesh = new scene::SMesh();
        for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
-               scene::IMeshBuffer *buf = src_mesh->getMeshBuffer(j);
-               switch (buf->getVertexType()) {
-                       case video::EVT_STANDARD: {
-                               video::S3DVertex *v =
-                                       (video::S3DVertex *) buf->getVertices();
-                               u16 *indices = (u16*)buf->getIndices();
-                               scene::SMeshBuffer *temp_buf = new scene::SMeshBuffer();
-                               temp_buf->append(v, buf->getVertexCount(),
-                                       indices, buf->getIndexCount());
-                               dst_mesh->addMeshBuffer(temp_buf);
-                               temp_buf->drop();
-                               break;
-                       }
-                       case video::EVT_2TCOORDS: {
-                               video::S3DVertex2TCoords *v =
-                                       (video::S3DVertex2TCoords *) buf->getVertices();
-                               u16 *indices = (u16*)buf->getIndices();
-                               scene::SMeshBufferTangents *temp_buf =
-                                       new scene::SMeshBufferTangents();
-                               temp_buf->append(v, buf->getVertexCount(),
-                                       indices, buf->getIndexCount());
-                               dst_mesh->addMeshBuffer(temp_buf);
-                               temp_buf->drop();
-                               break;
-                       }
-                       case video::EVT_TANGENTS: {
-                               video::S3DVertexTangents *v =
-                                       (video::S3DVertexTangents *) buf->getVertices();
-                               u16 *indices = (u16*)buf->getIndices();
-                               scene::SMeshBufferTangents *temp_buf =
-                                       new scene::SMeshBufferTangents();
-                               temp_buf->append(v, buf->getVertexCount(),
-                                       indices, buf->getIndexCount());
-                               dst_mesh->addMeshBuffer(temp_buf);
-                               temp_buf->drop();
-                               break;
-                       }
-               }
+               scene::IMeshBuffer *temp_buf = cloneMeshBuffer(
+                       src_mesh->getMeshBuffer(j));
+               dst_mesh->addMeshBuffer(temp_buf);
+               temp_buf->drop();
+
        }
        return dst_mesh;
 }
index bcf0d771c508e4ccce5050db0dffeefda8cc4f0f..0e946caaba96e3d8c9176be7ccf2a816d7de2f4e 100644 (file)
@@ -82,11 +82,16 @@ void rotateMeshBy6dFacedir(scene::IMesh *mesh, int facedir);
 void rotateMeshXYby (scene::IMesh *mesh, f64 degrees);
 void rotateMeshXZby (scene::IMesh *mesh, f64 degrees);
 void rotateMeshYZby (scene::IMesh *mesh, f64 degrees); 
+
+/*
+ *  Clone the mesh buffer.
+ */
+scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer);
  
 /*
        Clone the mesh.
 */
-scene::IMesh* cloneMesh(scene::IMesh *src_mesh);
+scene::SMesh* cloneMesh(scene::IMesh *src_mesh);
 
 /*
        Convert nodeboxes to mesh. Each tile goes into a different buffer.
index ea532d9e070b2d863bceca8e74eb03c0531341da..b586fa70be46b0136fb67dcfae2b96c60ac83d51 100644 (file)
@@ -148,9 +148,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
                Add node and tile color and palette
                Fix plantlike visual_scale being applied squared and add compatibility
                        with pre-30 clients by sending sqrt(visual_scale)
+       PROTOCOL VERSION 31:
+               Add tile overlay
 */
 
-#define LATEST_PROTOCOL_VERSION 30
+#define LATEST_PROTOCOL_VERSION 31
 
 // Server's supported network protocol range
 #define SERVER_PROTOCOL_VERSION_MIN 24
index ce3e378a08624e4156951a3fecb9dcb959455039..db28325aa38f5fccdaa5204a302d6f4db96250c0 100644 (file)
@@ -378,13 +378,13 @@ void ContentFeatures::reset()
 
 void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
 {
-       if (protocol_version < 30) {
+       if (protocol_version < 31) {
                serializeOld(os, protocol_version);
                return;
        }
 
        // version
-       writeU8(os, 9);
+       writeU8(os, 10);
 
        // general
        os << serializeString(name);
@@ -404,6 +404,8 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
        writeU8(os, 6);
        for (u32 i = 0; i < 6; i++)
                tiledef[i].serialize(os, protocol_version);
+       for (u32 i = 0; i < 6; i++)
+               tiledef_overlay[i].serialize(os, protocol_version);
        writeU8(os, CF_SPECIAL_COUNT);
        for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
                tiledef_special[i].serialize(os, protocol_version);
@@ -492,7 +494,7 @@ void ContentFeatures::deSerialize(std::istream &is)
        if (version < 9) {
                deSerializeOld(is, version);
                return;
-       } else if (version > 9) {
+       } else if (version > 10) {
                throw SerializationError("unsupported ContentFeatures version");
        }
 
@@ -516,6 +518,9 @@ void ContentFeatures::deSerialize(std::istream &is)
                throw SerializationError("unsupported tile count");
        for (u32 i = 0; i < 6; i++)
                tiledef[i].deSerialize(is, version, drawtype);
+       if (version >= 10)
+               for (u32 i = 0; i < 6; i++)
+                       tiledef_overlay[i].deSerialize(is, version, drawtype);
        if (readU8(is) != CF_SPECIAL_COUNT)
                throw SerializationError("unsupported CF_SPECIAL_COUNT");
        for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
@@ -581,7 +586,7 @@ void ContentFeatures::deSerialize(std::istream &is)
 }
 
 #ifndef SERVER
-void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
+void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileLayer *tile,
                TileDef *tiledef, u32 shader_id, bool use_normal_texture,
                bool backface_culling, u8 material_type)
 {
@@ -774,14 +779,18 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
 
        // Tiles (fill in f->tiles[])
        for (u16 j = 0; j < 6; j++) {
-               fillTileAttribs(tsrc, &tiles[j], &tdef[j], tile_shader[j],
+               fillTileAttribs(tsrc, &tiles[j].layers[0], &tdef[j], tile_shader[j],
                        tsettings.use_normal_texture,
                        tiledef[j].backface_culling, material_type);
+               if (tiledef_overlay[j].name!="")
+                       fillTileAttribs(tsrc, &tiles[j].layers[1], &tiledef_overlay[j],
+                               tile_shader[j], tsettings.use_normal_texture,
+                               tiledef[j].backface_culling, material_type);
        }
 
        // Special tiles (fill in f->special_tiles[])
        for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) {
-               fillTileAttribs(tsrc, &special_tiles[j], &tiledef_special[j],
+               fillTileAttribs(tsrc, &special_tiles[j].layers[0], &tiledef_special[j],
                        tile_shader[j], tsettings.use_normal_texture,
                        tiledef_special[j].backface_culling, material_type);
        }
@@ -1538,8 +1547,19 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
        if (protocol_version < 30 && drawtype == NDT_PLANTLIKE)
                compatible_visual_scale = sqrt(visual_scale);
 
+       TileDef compatible_tiles[6];
+       for (u8 i = 0; i < 6; i++) {
+               compatible_tiles[i] = tiledef[i];
+               if (tiledef_overlay[i].name != "") {
+                       std::stringstream s;
+                       s << "(" << tiledef[i].name << ")^(" << tiledef_overlay[i].name
+                               << ")";
+                       compatible_tiles[i].name = s.str();
+               }
+       }
+
        // Protocol >= 24
-       if (protocol_version < 30) {
+       if (protocol_version < 31) {
                writeU8(os, protocol_version < 27 ? 7 : 8);
 
                os << serializeString(name);
@@ -1553,7 +1573,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
                writeF1000(os, compatible_visual_scale);
                writeU8(os, 6);
                for (u32 i = 0; i < 6; i++)
-                       tiledef[i].serialize(os, protocol_version);
+                       compatible_tiles[i].serialize(os, protocol_version);
                writeU8(os, CF_SPECIAL_COUNT);
                for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
                        tiledef_special[i].serialize(os, protocol_version);
index 83968ce270d3a69afadb6d017516bce7484327cd..4d3bacc6c55cdd8ae92e744af7d0f34bc0cbba44 100644 (file)
@@ -280,6 +280,8 @@ struct ContentFeatures
 #endif
        float visual_scale; // Misc. scale parameter
        TileDef tiledef[6];
+       // These will be drawn over the base tiles.
+       TileDef tiledef_overlay[6];
        TileDef tiledef_special[CF_SPECIAL_COUNT]; // eg. flowing liquid
        // If 255, the node is opaque.
        // Otherwise it uses texture alpha.
@@ -405,7 +407,7 @@ struct ContentFeatures
        }
 
 #ifndef SERVER
-       void fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, TileDef *tiledef,
+       void fillTileAttribs(ITextureSource *tsrc, TileLayer *tile, TileDef *tiledef,
                u32 shader_id, bool use_normal_texture, bool backface_culling,
                u8 material_type);
        void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
index e1f292fb60cf567cb7270a90d8a576fd160cf8c3..7f406d874eb2a26030a3c3029e10d30bf661444e 100644 (file)
@@ -620,7 +620,7 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef,
 {
        // Texture
        u8 texid = myrand_range(0, 5);
-       const TileSpec &tile = f.tiles[texid];
+       const TileLayer &tile = f.tiles[texid].layers[0];
        video::ITexture *texture;
        struct TileAnimationParams anim;
        anim.type = TAT_NONE;
index 8dfb851e6e7fe43adfd71f7e0b845e39e5303582..573347b4c3bc95a88b5eae17bda5d72d4eda9cdd 100644 (file)
@@ -426,6 +426,34 @@ ContentFeatures read_content_features(lua_State *L, int index)
        }
        lua_pop(L, 1);
 
+       // overlay_tiles = {}
+       lua_getfield(L, index, "overlay_tiles");
+       if (lua_istable(L, -1)) {
+               int table = lua_gettop(L);
+               lua_pushnil(L);
+               int i = 0;
+               while (lua_next(L, table) != 0) {
+                       // Read tiledef from value
+                       f.tiledef_overlay[i] = read_tiledef(L, -1, f.drawtype);
+                       // removes value, keeps key for next iteration
+                       lua_pop(L, 1);
+                       i++;
+                       if (i == 6) {
+                               lua_pop(L, 1);
+                               break;
+                       }
+               }
+               // Copy last value to all remaining textures
+               if (i >= 1) {
+                       TileDef lasttile = f.tiledef_overlay[i - 1];
+                       while (i < 6) {
+                               f.tiledef_overlay[i] = lasttile;
+                               i++;
+                       }
+               }
+       }
+       lua_pop(L, 1);
+
        // special_tiles = {}
        lua_getfield(L, index, "special_tiles");
        // If nil, try the deprecated name "special_materials" instead
index 40af0be5f345616881c4ac81adbdbd5056cea633..2b23d9e023c43723181578d2f19259858df25746 100644 (file)
@@ -235,27 +235,16 @@ WieldMeshSceneNode::~WieldMeshSceneNode()
                g_extrusion_mesh_cache = NULL;
 }
 
-void WieldMeshSceneNode::setCube(const TileSpec tiles[6],
+void WieldMeshSceneNode::setCube(const ContentFeatures &f,
                        v3f wield_scale, ITextureSource *tsrc)
 {
        scene::IMesh *cubemesh = g_extrusion_mesh_cache->createCube();
-       changeToMesh(cubemesh);
+       scene::SMesh *copy = cloneMesh(cubemesh);
        cubemesh->drop();
-
+       postProcessNodeMesh(copy, f, false, true, &m_material_type, &m_colors);
+       changeToMesh(copy);
+       copy->drop();
        m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR);
-
-       // Customize materials
-       for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) {
-               assert(i < 6);
-               video::SMaterial &material = m_meshnode->getMaterial(i);
-               if (tiles[i].animation_frame_count == 1) {
-                       material.setTexture(0, tiles[i].texture);
-               } else {
-                       FrameSpec animation_frame = tiles[i].frames[0];
-                       material.setTexture(0, animation_frame.texture);
-               }
-               tiles[i].applyMaterialOptions(material);
-       }
 }
 
 void WieldMeshSceneNode::setExtruded(const std::string &imagename,
@@ -274,8 +263,10 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename,
                dim = core::dimension2d<u32>(dim.Width, frame_height);
        }
        scene::IMesh *mesh = g_extrusion_mesh_cache->create(dim);
-       changeToMesh(mesh);
+       scene::SMesh *copy = cloneMesh(mesh);
        mesh->drop();
+       changeToMesh(copy);
+       copy->drop();
 
        m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR_EXTRUDED);
 
@@ -321,12 +312,12 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
 
        // Color-related
        m_colors.clear();
-       video::SColor basecolor = idef->getItemstackColor(item, client);
+       m_base_color = idef->getItemstackColor(item, client);
 
        // If wield_image is defined, it overrides everything else
        if (def.wield_image != "") {
                setExtruded(def.wield_image, def.wield_scale, tsrc, 1);
-               m_colors.push_back(basecolor);
+               m_colors.push_back(ItemPartColor());
                return;
        }
        // Handle nodes
@@ -334,66 +325,50 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
        else if (def.type == ITEM_NODE) {
                if (f.mesh_ptr[0]) {
                        // e.g. mesh nodes and nodeboxes
-                       changeToMesh(f.mesh_ptr[0]);
-                       // mesh_ptr[0] is pre-scaled by BS * f->visual_scale
+                       scene::SMesh *mesh = cloneMesh(f.mesh_ptr[0]);
+                       postProcessNodeMesh(mesh, f, m_enable_shaders, true,
+                               &m_material_type, &m_colors);
+                       changeToMesh(mesh);
+                       mesh->drop();
+                       // mesh is pre-scaled by BS * f->visual_scale
                        m_meshnode->setScale(
                                        def.wield_scale * WIELD_SCALE_FACTOR
                                        / (BS * f.visual_scale));
                } else if (f.drawtype == NDT_AIRLIKE) {
                        changeToMesh(NULL);
                } else if (f.drawtype == NDT_PLANTLIKE) {
-                       setExtruded(tsrc->getTextureName(f.tiles[0].texture_id), def.wield_scale, tsrc, f.tiles[0].animation_frame_count);
+                       setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id),
+                               def.wield_scale, tsrc,
+                               f.tiles[0].layers[0].animation_frame_count);
                } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) {
-                       setCube(f.tiles, def.wield_scale, tsrc);
+                       setCube(f, def.wield_scale, tsrc);
                } else {
                        MeshMakeData mesh_make_data(client, false);
                        MapNode mesh_make_node(id, 255, 0);
                        mesh_make_data.fillSingleNode(&mesh_make_node);
                        MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
-                       changeToMesh(mapblock_mesh.getMesh());
-                       translateMesh(m_meshnode->getMesh(), v3f(-BS, -BS, -BS));
+                       scene::SMesh *mesh = cloneMesh(mapblock_mesh.getMesh());
+                       translateMesh(mesh, v3f(-BS, -BS, -BS));
+                       postProcessNodeMesh(mesh, f, m_enable_shaders, true,
+                               &m_material_type, &m_colors);
+                       changeToMesh(mesh);
+                       mesh->drop();
                        m_meshnode->setScale(
                                        def.wield_scale * WIELD_SCALE_FACTOR
                                        / (BS * f.visual_scale));
                }
                u32 material_count = m_meshnode->getMaterialCount();
-               if (material_count > 6) {
-                       errorstream << "WieldMeshSceneNode::setItem: Invalid material "
-                               "count " << material_count << ", truncating to 6" << std::endl;
-                       material_count = 6;
-               }
                for (u32 i = 0; i < material_count; ++i) {
-                       const TileSpec *tile = &(f.tiles[i]);
                        video::SMaterial &material = m_meshnode->getMaterial(i);
                        material.setFlag(video::EMF_BACK_FACE_CULLING, true);
                        material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter);
                        material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter);
-                       bool animated = (tile->animation_frame_count > 1);
-                       if (animated) {
-                               FrameSpec animation_frame = tile->frames[0];
-                               material.setTexture(0, animation_frame.texture);
-                       } else {
-                               material.setTexture(0, tile->texture);
-                       }
-                       m_colors.push_back(tile->has_color ? tile->color : basecolor);
-                       material.MaterialType = m_material_type;
-                       if (m_enable_shaders) {
-                               if (tile->normal_texture) {
-                                       if (animated) {
-                                               FrameSpec animation_frame = tile->frames[0];
-                                               material.setTexture(1, animation_frame.normal_texture);
-                                       } else {
-                                               material.setTexture(1, tile->normal_texture);
-                                       }
-                               }
-                               material.setTexture(2, tile->flags_texture);
-                       }
                }
                return;
        }
        else if (def.inventory_image != "") {
                setExtruded(def.inventory_image, def.wield_scale, tsrc, 1);
-               m_colors.push_back(basecolor);
+               m_colors.push_back(ItemPartColor());
                return;
        }
 
@@ -413,9 +388,9 @@ void WieldMeshSceneNode::setColor(video::SColor c)
        u8 blue = c.getBlue();
        u32 mc = mesh->getMeshBufferCount();
        for (u32 j = 0; j < mc; j++) {
-               video::SColor bc(0xFFFFFFFF);
-               if (m_colors.size() > j)
-                       bc = m_colors[j];
+               video::SColor bc(m_base_color);
+               if ((m_colors.size() > j) && (m_colors[j].override_base))
+                       bc = m_colors[j].color;
                video::SColor buffercolor(255,
                        bc.getRed() * red / 255,
                        bc.getGreen() * green / 255,
@@ -439,19 +414,7 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
                m_meshnode->setMesh(dummymesh);
                dummymesh->drop();  // m_meshnode grabbed it
        } else {
-               if (m_lighting) {
-                       m_meshnode->setMesh(mesh);
-               } else {
-                       /*
-                               Lighting is disabled, this means the caller can (and probably will)
-                               call setColor later. We therefore need to clone the mesh so that
-                               setColor will only modify this scene node's mesh, not others'.
-                       */
-                       scene::IMeshManipulator *meshmanip = SceneManager->getMeshManipulator();
-                       scene::IMesh *new_mesh = meshmanip->createMeshCopy(mesh);
-                       m_meshnode->setMesh(new_mesh);
-                       new_mesh->drop();  // m_meshnode grabbed it
-               }
+               m_meshnode->setMesh(mesh);
        }
 
        m_meshnode->setMaterialFlag(video::EMF_LIGHTING, m_lighting);
@@ -475,24 +438,24 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
                g_extrusion_mesh_cache->grab();
        }
 
-       scene::IMesh *mesh;
+       scene::SMesh *mesh;
 
        // If inventory_image is defined, it overrides everything else
        if (def.inventory_image != "") {
                mesh = getExtrudedMesh(tsrc, def.inventory_image);
-               result->mesh = mesh;
-               result->buffer_colors.push_back(
-                       std::pair<bool, video::SColor>(false, video::SColor(0xFFFFFFFF)));
+               result->buffer_colors.push_back(ItemPartColor());
        } else if (def.type == ITEM_NODE) {
                if (f.mesh_ptr[0]) {
                        mesh = cloneMesh(f.mesh_ptr[0]);
                        scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
                } else if (f.drawtype == NDT_PLANTLIKE) {
                        mesh = getExtrudedMesh(tsrc,
-                               tsrc->getTextureName(f.tiles[0].texture_id));
+                               tsrc->getTextureName(f.tiles[0].layers[0].texture_id));
                } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES
                        || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
-                       mesh = cloneMesh(g_extrusion_mesh_cache->createCube());
+                       scene::IMesh *cube = g_extrusion_mesh_cache->createCube();
+                       mesh = cloneMesh(cube);
+                       cube->drop();
                        scaleMesh(mesh, v3f(1.2, 1.2, 1.2));
                } else {
                        MeshMakeData mesh_make_data(client, false);
@@ -519,32 +482,27 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
 
                u32 mc = mesh->getMeshBufferCount();
                for (u32 i = 0; i < mc; ++i) {
-                       const TileSpec *tile = &(f.tiles[i]);
                        scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
-                       result->buffer_colors.push_back(
-                               std::pair<bool, video::SColor>(tile->has_color, tile->color));
-                       colorizeMeshBuffer(buf, &tile->color);
                        video::SMaterial &material = buf->getMaterial();
                        material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
                        material.setFlag(video::EMF_BILINEAR_FILTER, false);
                        material.setFlag(video::EMF_TRILINEAR_FILTER, false);
                        material.setFlag(video::EMF_BACK_FACE_CULLING, true);
                        material.setFlag(video::EMF_LIGHTING, false);
-                       if (tile->animation_frame_count > 1) {
-                               FrameSpec animation_frame = tile->frames[0];
-                               material.setTexture(0, animation_frame.texture);
-                       } else {
-                               material.setTexture(0, tile->texture);
-                       }
                }
 
                rotateMeshXZby(mesh, -45);
                rotateMeshYZby(mesh, -30);
-               result->mesh = mesh;
+
+               postProcessNodeMesh(mesh, f, false, false, NULL,
+                       &result->buffer_colors);
        }
+       result->mesh = mesh;
 }
 
-scene::IMesh * getExtrudedMesh(ITextureSource *tsrc,
+
+
+scene::SMesh * getExtrudedMesh(ITextureSource *tsrc,
                const std::string &imagename)
 {
        video::ITexture *texture = tsrc->getTextureForMesh(imagename);
@@ -553,7 +511,9 @@ scene::IMesh * getExtrudedMesh(ITextureSource *tsrc,
        }
 
        core::dimension2d<u32> dim = texture->getSize();
-       scene::IMesh *mesh = cloneMesh(g_extrusion_mesh_cache->create(dim));
+       scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
+       scene::SMesh *mesh = cloneMesh(original);
+       original->drop();
 
        // Customize material
        video::SMaterial &material = mesh->getMeshBuffer(0)->getMaterial();
@@ -569,3 +529,57 @@ scene::IMesh * getExtrudedMesh(ITextureSource *tsrc,
 
        return mesh;
 }
+
+void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f,
+       bool use_shaders, bool set_material, video::E_MATERIAL_TYPE *mattype,
+       std::vector<ItemPartColor> *colors)
+{
+       u32 mc = mesh->getMeshBufferCount();
+       // Allocate colors for existing buffers
+       colors->clear();
+       for (u32 i = 0; i < mc; ++i)
+               colors->push_back(ItemPartColor());
+
+       for (u32 i = 0; i < mc; ++i) {
+               const TileSpec *tile = &(f.tiles[i]);
+               scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
+               for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
+                       const TileLayer *layer = &tile->layers[layernum];
+                       if (layer->texture_id == 0)
+                               continue;
+                       if (layernum != 0) {
+                               scene::IMeshBuffer *copy = cloneMeshBuffer(buf);
+                               copy->getMaterial() = buf->getMaterial();
+                               mesh->addMeshBuffer(copy);
+                               copy->drop();
+                               buf = copy;
+                               colors->push_back(
+                                       ItemPartColor(layer->has_color, layer->color));
+                       } else {
+                               (*colors)[i] = ItemPartColor(layer->has_color, layer->color);
+                       }
+                       video::SMaterial &material = buf->getMaterial();
+                       if (set_material)
+                               layer->applyMaterialOptions(material);
+                       if (mattype) {
+                               material.MaterialType = *mattype;
+                       }
+                       if (layer->animation_frame_count > 1) {
+                               FrameSpec animation_frame = layer->frames[0];
+                               material.setTexture(0, animation_frame.texture);
+                       } else {
+                               material.setTexture(0, layer->texture);
+                       }
+                       if (use_shaders) {
+                               if (layer->normal_texture) {
+                                       if (layer->animation_frame_count > 1) {
+                                               FrameSpec animation_frame = layer->frames[0];
+                                               material.setTexture(1, animation_frame.normal_texture);
+                                       } else
+                                               material.setTexture(1, layer->normal_texture);
+                               }
+                               material.setTexture(2, layer->flags_texture);
+                       }
+               }
+       }
+}
index d3946b4e09f0f3420799aa1d312e8cfd46f3b0de..c98b469d99a1b700aed28bf938c0d257b2c213ad 100644 (file)
@@ -26,17 +26,41 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 struct ItemStack;
 class Client;
 class ITextureSource;
-struct TileSpec;
+struct ContentFeatures;
+
+/*!
+ * Holds color information of an item mesh's buffer.
+ */
+struct ItemPartColor {
+       /*!
+        * If this is false, the global base color of the item
+        * will be used instead of the specific color of the
+        * buffer.
+        */
+       bool override_base;
+       /*!
+        * The color of the buffer.
+        */
+       video::SColor color;
+
+       ItemPartColor():
+               override_base(false),
+               color(0)
+       {}
+
+       ItemPartColor(bool override, video::SColor color):
+               override_base(override),
+               color(color)
+       {}
+};
 
 struct ItemMesh
 {
        scene::IMesh *mesh;
        /*!
         * Stores the color of each mesh buffer.
-        * If the boolean is true, the color is fixed, else
-        * palettes can modify it.
         */
-       std::vector<std::pair<bool, video::SColor> > buffer_colors;
+       std::vector<ItemPartColor> buffer_colors;
 
        ItemMesh() : mesh(NULL), buffer_colors() {}
 };
@@ -51,7 +75,8 @@ public:
                        s32 id = -1, bool lighting = false);
        virtual ~WieldMeshSceneNode();
 
-       void setCube(const TileSpec tiles[6], v3f wield_scale, ITextureSource *tsrc);
+       void setCube(const ContentFeatures &f, v3f wield_scale,
+               ITextureSource *tsrc);
        void setExtruded(const std::string &imagename, v3f wield_scale,
                        ITextureSource *tsrc, u8 num_frames);
        void setItem(const ItemStack &item, Client *client);
@@ -84,7 +109,12 @@ private:
         * Stores the colors of the mesh's mesh buffers.
         * This does not include lighting.
         */
-       std::vector<video::SColor> m_colors;
+       std::vector<ItemPartColor> m_colors;
+       /*!
+        * The base color of this mesh. This is the default
+        * for all mesh buffers.
+        */
+       video::SColor m_base_color;
 
        // Bounding box culling is disabled for this type of scene node,
        // so this variable is just required so we can implement
@@ -94,5 +124,16 @@ private:
 
 void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result);
 
-scene::IMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename);
+scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename);
+
+/*!
+ * Applies overlays, textures and optionally materials to the given mesh and
+ * extracts tile colors for colorization.
+ * \param mattype overrides the buffer's material type, but can also
+ * be NULL to leave the original material.
+ * \param colors returns the colors of the mesh buffers in the mesh.
+ */
+void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f,
+       bool use_shaders, bool set_material, video::E_MATERIAL_TYPE *mattype,
+       std::vector<ItemPartColor> *colors);
 #endif