From 1ffb180868ffcec6812cd3aac8f56ffefb91c8bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= Date: Fri, 21 Apr 2017 15:34:59 +0200 Subject: [PATCH] Soft node overlay (#5186) This commit adds node overlays, which are tiles that are drawn on top of other tiles. --- doc/lua_api.txt | 6 + src/client.cpp | 16 +- src/client/tile.h | 96 ++++-- src/clientmap.cpp | 52 ++-- src/content_mapblock.cpp | 61 ++-- src/content_mapblock.h | 4 +- src/hud.cpp | 5 +- src/mapblock_mesh.cpp | 529 +++++++++++++++++++------------- src/mapblock_mesh.h | 57 ++-- src/mesh.cpp | 80 ++--- src/mesh.h | 7 +- src/network/networkprotocol.h | 4 +- src/nodedef.cpp | 36 ++- src/nodedef.h | 4 +- src/particles.cpp | 2 +- src/script/common/c_content.cpp | 28 ++ src/wieldmesh.cpp | 188 ++++++------ src/wieldmesh.h | 55 +++- 18 files changed, 763 insertions(+), 467 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 16e662e0c..4e328ac76 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -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 ]] diff --git a/src/client.cpp b/src/client.cpp index 3cea4fbf4..ce42d025e 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -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; diff --git a/src/client/tile.h b/src/client/tile.h index ef01d2a1f..c6ebee006 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -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 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 diff --git a/src/clientmap.cpp b/src/clientmap.cpp index 4cae03bf2..6cd24ffc6 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -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 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::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; + 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); + 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); + drawbufs.add(buf, layer); + } } } } diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 153dacf42..9f4223bac 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -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, ©.color); + for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) { + TileLayer *layer = ©.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, diff --git a/src/content_mapblock.h b/src/content_mapblock.h index c8425024f..6866a4498 100644 --- a/src/content_mapblock.h +++ b/src/content_mapblock.h @@ -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); diff --git a/src/hud.cpp b/src/hud.cpp index f558acf1e..c482912e9 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -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 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(); diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 933dfc32a..0bba644e6 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -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 &dest) + v3f p, v3s16 dir, v3f scale, std::vector &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()); - - FastFace& face = *dest.rbegin(); - u16 li[4] = { li0, li1, li2, li3 }; + u16 day[4]; + u16 night[4]; + + 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]); + v2f32 f[4] = { core::vector2d(x0 + w * abs_scale, y0 + h), core::vector2d(x0, y0 + h), core::vector2d(x0, y0), core::vector2d(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.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; } - - face.tile = tile; } /* @@ -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<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<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(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(layer, i)] = p.layer; + m_animation_frames[std::pair(layer, i)] = 0; + if(g_settings->getBool("desynchronize_mapblock_texture_animation")){ + // Get starting position from noise + m_animation_frame_offsets[std::pair(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(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; + // Replace tile texture with the first animation frame + FrameSpec animation_frame = p.layer.frames[0]; + p.layer.texture = animation_frame.texture; + } - // The sunlight ratio has been stored, - // delete alpha (for the final rendering). - vc->setAlpha(255); + 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(layer, i)][j] = copy; + + // The sunlight ratio has been stored, + // delete alpha (for the final rendering). + vc->setAlpha(255); + } + } + + // 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()); } } - // 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) { - 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); - } - material.setTexture(2, p.tile.flags_texture); - } else { - p.tile.applyMaterialOptions(material); - } + /* + Do some stuff to the mesh + */ + m_camera_offset = camera_offset; + translateMesh(m_mesh[layer], + intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS)); - scene::SMesh *mesh = (scene::SMesh *)m_mesh; - - // 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()); + 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 "<getMeshBufferCount() - <<" materials (meshbuffers)"<getMeshBufferCount() + <<" materials (meshbuffers)"<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::iterator i = m_crack_materials.begin(); - i != m_crack_materials.end(); ++i) { - scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); + for (std::map, 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::iterator anim_iter = - m_animation_tiles.find(i->first); + std::map, 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::iterator i = m_animation_tiles.begin(); - i != m_animation_tiles.end(); ++i) { - const TileSpec &tile = i->second; + for (std::map, 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 >::iterator + for(std::map, std::map >::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::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="< *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="< *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 *p = &prebuffers[layer]; + for (std::vector::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 *p = &prebuffers[layer]; + for (std::vector::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); } diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 25c699e1c..f32df3958 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -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 m_crack_materials; + // Maps mesh and mesh buffer (i.e. material) indices to base texture names + std::map, std::string> m_crack_materials; // Animation info: texture animationi - // Maps meshbuffers to TileSpecs - UNORDERED_MAP m_animation_tiles; - UNORDERED_MAP m_animation_frames; // last animation frame - UNORDERED_MAP 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, TileLayer> m_animation_tiles; + std::map, int> m_animation_frames; // last animation frame + std::map, 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 > 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::map > 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 indices; std::vector vertices; std::vector tangent_vertices; @@ -184,7 +192,7 @@ struct PreMeshBuffer struct MeshCollector { - std::vector prebuffers; + std::vector 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); diff --git a/src/mesh.cpp b/src/mesh.cpp index a79264ef0..d776f6185 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -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; } diff --git a/src/mesh.h b/src/mesh.h index bcf0d771c..0e946caab 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -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. diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index ea532d9e0..b586fa70b 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -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 diff --git a/src/nodedef.cpp b/src/nodedef.cpp index ce3e378a0..db28325aa 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -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); diff --git a/src/nodedef.h b/src/nodedef.h index 83968ce27..4d3bacc6c 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -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, diff --git a/src/particles.cpp b/src/particles.cpp index e1f292fb6..7f406d874 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -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; diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 8dfb851e6..573347b4c 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -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 diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp index 40af0be5f..2b23d9e02 100644 --- a/src/wieldmesh.cpp +++ b/src/wieldmesh.cpp @@ -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(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(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(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 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 *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); + } + } + } +} diff --git a/src/wieldmesh.h b/src/wieldmesh.h index d3946b4e0..c98b469d9 100644 --- a/src/wieldmesh.h +++ b/src/wieldmesh.h @@ -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 > buffer_colors; + std::vector 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 m_colors; + std::vector 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 *colors); #endif