Soft node overlay (#5186)

This commit adds node overlays, which are tiles that are drawn on top of
other tiles.
This commit is contained in:
Dániel Juhász 2017-04-21 15:34:59 +02:00 committed by Zeno-
parent 2ad74a9e8b
commit 1ffb180868
18 changed files with 763 additions and 467 deletions

@ -3908,6 +3908,12 @@ Definition tables
tiles = {tile definition 1, def2, def3, def4, def5, def6}, --[[ tiles = {tile definition 1, def2, def3, def4, def5, def6}, --[[
^ Textures of node; +Y, -Y, +X, -X, +Z, -Z (old field name: tile_images) ^ Textures of node; +Y, -Y, +X, -X, +Z, -Z (old field name: tile_images)
^ List can be shortened to needed length ]] ^ 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_tiles = {tile definition 1, Tile definition 2}, --[[
^ Special textures of node; used rarely (old field name: special_materials) ^ Special textures of node; used rarely (old field name: special_materials)
^ List can be shortened to needed length ]] ^ List can be shortened to needed length ]]

@ -487,13 +487,17 @@ void Client::step(float dtime)
minimap_mapblock = r.mesh->moveMinimapMapblock(); minimap_mapblock = r.mesh->moveMinimapMapblock();
if (minimap_mapblock == NULL) if (minimap_mapblock == NULL)
do_mapper_update = false; do_mapper_update = false;
}
if (r.mesh && r.mesh->getMesh()->getMeshBufferCount() == 0) { bool is_empty = true;
delete r.mesh; for (int l = 0; l < MAX_TILE_LAYERS; l++)
} else { if (r.mesh->getMesh(l)->getMeshBufferCount() != 0)
// Replace with the new mesh is_empty = false;
block->mesh = r.mesh;
if (is_empty)
delete r.mesh;
else
// Replace with the new mesh
block->mesh = r.mesh;
} }
} else { } else {
delete r.mesh; delete r.mesh;

@ -194,19 +194,22 @@ struct FrameSpec
video::ITexture *flags_texture; video::ITexture *flags_texture;
}; };
struct TileSpec #define MAX_TILE_LAYERS 2
//! Defines a layer of a tile.
struct TileLayer
{ {
TileSpec(): TileLayer():
texture(NULL), texture(NULL),
texture_id(0), texture_id(0),
color(), color(),
material_type(TILE_MATERIAL_BASIC), material_type(TILE_MATERIAL_BASIC),
material_flags( material_flags(
//0 // <- DEBUG, Use the one below //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), shader_id(0),
normal_texture(NULL), normal_texture(NULL),
flags_texture(NULL), flags_texture(NULL),
@ -217,23 +220,21 @@ struct TileSpec
} }
/*! /*!
* Two tiles are equal if they can be appended to * Two layers are equal if they can be merged.
* the same mesh buffer.
*/ */
bool operator==(const TileSpec &other) const bool operator==(const TileLayer &other) const
{ {
return ( return
texture_id == other.texture_id && texture_id == other.texture_id &&
material_type == other.material_type && material_type == other.material_type &&
material_flags == other.material_flags && 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); return !(*this == other);
} }
@ -243,23 +244,17 @@ struct TileSpec
{ {
switch (material_type) { switch (material_type) {
case TILE_MATERIAL_BASIC: case TILE_MATERIAL_BASIC:
case TILE_MATERIAL_WAVING_LEAVES:
case TILE_MATERIAL_WAVING_PLANTS:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break; break;
case TILE_MATERIAL_ALPHA: case TILE_MATERIAL_ALPHA:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_LIQUID_TRANSPARENT: case TILE_MATERIAL_LIQUID_TRANSPARENT:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break; break;
case TILE_MATERIAL_LIQUID_OPAQUE: case TILE_MATERIAL_LIQUID_OPAQUE:
material.MaterialType = video::EMT_SOLID; material.MaterialType = video::EMT_SOLID;
break; 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) material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING)
? true : false; ? 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; video::ITexture *texture;
u32 texture_id; 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; video::SColor color;
// Material parameters // Material parameters
u8 material_type; u8 material_type;
u8 material_flags; u8 material_flags;
u8 rotation;
//! This much light does the tile emit.
u8 emissive_light;
u32 shader_id; u32 shader_id;
video::ITexture *normal_texture; video::ITexture *normal_texture;
// cacheline (64)
video::ITexture *flags_texture; video::ITexture *flags_texture;
// Animation parameters // Animation parameters
u16 animation_frame_length_ms; u16 animation_frame_length_ms;
u8 animation_frame_count; u8 animation_frame_count;
@ -313,4 +308,39 @@ struct TileSpec
std::vector<FrameSpec> frames; 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 #endif

@ -290,6 +290,11 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
struct MeshBufList 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; video::SMaterial m;
std::vector<scene::IMeshBuffer*> bufs; std::vector<scene::IMeshBuffer*> bufs;
}; };
@ -303,7 +308,7 @@ struct MeshBufListList
lists.clear(); lists.clear();
} }
void add(scene::IMeshBuffer *buf) void add(scene::IMeshBuffer *buf, u8 layer)
{ {
const video::SMaterial &m = buf->getMaterial(); const video::SMaterial &m = buf->getMaterial();
for(std::vector<MeshBufList>::iterator i = lists.begin(); for(std::vector<MeshBufList>::iterator i = lists.begin();
@ -315,12 +320,16 @@ struct MeshBufListList
if (l.m.TextureLayer[0].Texture != m.TextureLayer[0].Texture) if (l.m.TextureLayer[0].Texture != m.TextureLayer[0].Texture)
continue; continue;
if(l.layer != layer)
continue;
if (l.m == m) { if (l.m == m) {
l.bufs.push_back(buf); l.bufs.push_back(buf);
return; return;
} }
} }
MeshBufList l; MeshBufList l;
l.layer = layer;
l.m = m; l.m = m;
l.bufs.push_back(buf); l.bufs.push_back(buf);
lists.push_back(l); lists.push_back(l);
@ -434,29 +443,34 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
MapBlockMesh *mapBlockMesh = block->mesh; MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh); assert(mapBlockMesh);
scene::IMesh *mesh = mapBlockMesh->getMesh(); for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
assert(mesh); scene::IMesh *mesh = mapBlockMesh->getMesh(layer);
assert(mesh);
u32 c = mesh->getMeshBufferCount(); u32 c = mesh->getMeshBufferCount();
for (u32 i = 0; i < c; i++) for (u32 i = 0; i < c; i++) {
{ scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
video::SMaterial& material = buf->getMaterial(); video::SMaterial& material = buf->getMaterial();
video::IMaterialRenderer* rnd = video::IMaterialRenderer* rnd =
driver->getMaterialRenderer(material.MaterialType); driver->getMaterialRenderer(material.MaterialType);
bool transparent = (rnd && rnd->isTransparent()); bool transparent = (rnd && rnd->isTransparent());
if (transparent == is_transparent_pass) { if (transparent == is_transparent_pass) {
if (buf->getVertexCount() == 0) if (buf->getVertexCount() == 0)
errorstream << "Block [" << analyze_block(block) errorstream << "Block [" << analyze_block(block)
<< "] contains an empty meshbuf" << std::endl; << "] contains an empty meshbuf" << std::endl;
material.setFlag(video::EMF_TRILINEAR_FILTER, m_cache_trilinear_filter); material.setFlag(video::EMF_TRILINEAR_FILTER,
material.setFlag(video::EMF_BILINEAR_FILTER, m_cache_bilinear_filter); m_cache_trilinear_filter);
material.setFlag(video::EMF_ANISOTROPIC_FILTER, m_cache_anistropic_filter); material.setFlag(video::EMF_BILINEAR_FILTER,
material.setFlag(video::EMF_WIREFRAME, m_control.show_wireframe); 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);
}
} }
} }
} }

@ -78,17 +78,19 @@ void MapblockMeshGenerator::useTile(int index, bool disable_backface_culling)
{ {
tile = getNodeTileN(n, p, index, data); tile = getNodeTileN(n, p, index, data);
if (!data->m_smooth_lighting) if (!data->m_smooth_lighting)
color = encode_light_and_color(light, tile.color, f->light_source); color = encode_light(light, f->light_source);
if (disable_backface_culling) for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING; tile.layers[layer].material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
tile.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) void MapblockMeshGenerator::useDefaultTile(bool set_color)
{ {
tile = getNodeTile(n, p, v3s16(0, 0, 0), data); tile = getNodeTile(n, p, v3s16(0, 0, 0), data);
if (set_color && !data->m_smooth_lighting) 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) 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].Pos = coords[j] + origin;
vertices[j].Normal = normal2; vertices[j].Normal = normal2;
if (data->m_smooth_lighting) if (data->m_smooth_lighting)
vertices[j].Color = blendLight(coords[j], tile.color); vertices[j].Color = blendLight(coords[j]);
else else
vertices[j].Color = color; vertices[j].Color = color;
if (shade_face) if (shade_face)
@ -137,8 +139,7 @@ void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
video::SColor colors[6]; video::SColor colors[6];
if (!data->m_smooth_lighting) { if (!data->m_smooth_lighting) {
for (int face = 0; face != 6; ++face) { for (int face = 0; face != 6; ++face) {
int tileindex = MYMIN(face, tilecount - 1); colors[face] = encode_light(light, f->light_source);
colors[face] = encode_light_and_color(light, tiles[tileindex].color, f->light_source);
} }
if (!f->light_source) { if (!f->light_source) {
applyFacesShading(colors[0], v3f(0, 1, 0)); applyFacesShading(colors[0], v3f(0, 1, 0));
@ -240,9 +241,8 @@ void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
if (data->m_smooth_lighting) { if (data->m_smooth_lighting) {
for (int j = 0; j < 24; ++j) { for (int j = 0; j < 24; ++j) {
int tileindex = MYMIN(j / 4, tilecount - 1); vertices[j].Color = encode_light(lights[light_indices[j]],
vertices[j].Color = encode_light_and_color(lights[light_indices[j]], f->light_source);
tiles[tileindex].color, f->light_source);
if (!f->light_source) if (!f->light_source)
applyFacesShading(vertices[j].Color, vertices[j].Normal); 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 // 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) // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
// tile_color - node's tile color // tile_color - node's tile color
video::SColor MapblockMeshGenerator::blendLight(const v3f &vertex_pos, video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos)
video::SColor tile_color)
{ {
u16 light = blendLight(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, video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos,
const v3f &vertex_normal, video::SColor tile_color) const v3f &vertex_normal)
{ {
video::SColor color = blendLight(vertex_pos, tile_color); video::SColor color = blendLight(vertex_pos);
if (!f->light_source) if (!f->light_source)
applyFacesShading(color, vertex_normal); applyFacesShading(color, vertex_normal);
return color; return color;
@ -367,8 +366,13 @@ static TileSpec getSpecialTile(const ContentFeatures &f,
const MapNode &n, u8 i) const MapNode &n, u8 i)
{ {
TileSpec copy = f.special_tiles[i]; TileSpec copy = f.special_tiles[i];
if (!copy.has_color) for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
n.getColor(f, &copy.color); TileLayer *layer = &copy.layers[layernum];
if (layer->texture_id == 0)
continue;
if (!layer->has_color)
n.getColor(f, &(layer->color));
}
return copy; return copy;
} }
@ -395,8 +399,8 @@ void MapblockMeshGenerator::prepareLiquidNodeDrawing(bool flowing)
light = getInteriorLight(ntop, 0, nodedef); light = getInteriorLight(ntop, 0, nodedef);
} }
color_liquid_top = encode_light_and_color(light, tile_liquid_top.color, f->light_source); color_liquid_top = encode_light(light, f->light_source);
color = encode_light_and_color(light, tile_liquid.color, f->light_source); color = encode_light(light, f->light_source);
} }
void MapblockMeshGenerator::getLiquidNeighborhood(bool flowing) void MapblockMeshGenerator::getLiquidNeighborhood(bool flowing)
@ -547,7 +551,7 @@ void MapblockMeshGenerator::drawLiquidSides(bool flowing)
else else
pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS; pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS;
if (data->m_smooth_lighting) if (data->m_smooth_lighting)
color = blendLight(pos, tile_liquid.color); color = blendLightColor(pos);
pos += origin; pos += origin;
vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v); 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]; int w = corner_resolve[i][1];
vertices[i].Pos.Y += corner_levels[w][u]; vertices[i].Pos.Y += corner_levels[w][u];
if (data->m_smooth_lighting) 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; vertices[i].Pos += origin;
} }
@ -659,7 +663,9 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()
tiles[face] = getTile(g_6dirs[face]); tiles[face] = getTile(g_6dirs[face]);
TileSpec glass_tiles[6]; 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[0] = tiles[4];
glass_tiles[1] = tiles[0]; glass_tiles[1] = tiles[0];
glass_tiles[2] = tiles[4]; glass_tiles[2] = tiles[4];
@ -763,7 +769,7 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()
// Optionally render internal liquid level defined by param2 // Optionally render internal liquid level defined by param2
// Liquid is textured with 1 tile defined in nodedef 'special_tiles' // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL && 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, // Internal liquid level has param2 range 0 .. 63,
// convert it to -0.5 .. 0.5 // convert it to -0.5 .. 0.5
float vlev = (param2 / 63.0) * 2.0 - 1.0; float vlev = (param2 / 63.0) * 2.0 - 1.0;
@ -998,7 +1004,8 @@ void MapblockMeshGenerator::drawFencelikeNode()
{ {
useDefaultTile(false); useDefaultTile(false);
TileSpec tile_nocrack = tile; 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 // Put wood the right way around in the posts
TileSpec tile_rot = tile; TileSpec tile_rot = tile;
@ -1253,7 +1260,7 @@ void MapblockMeshGenerator::drawMeshNode()
// vertex right here. // vertex right here.
for (int k = 0; k < vertex_count; k++) { for (int k = 0; k < vertex_count; k++) {
video::S3DVertex &vertex = vertices[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; vertex.Pos += origin;
} }
collector->append(tile, vertices, vertex_count, collector->append(tile, vertices, vertex_count,

@ -60,8 +60,8 @@ public:
// lighting // lighting
void getSmoothLightFrame(); void getSmoothLightFrame();
u16 blendLight(const v3f &vertex_pos); u16 blendLight(const v3f &vertex_pos);
video::SColor blendLight(const v3f &vertex_pos, video::SColor tile_color); video::SColor blendLightColor(const v3f &vertex_pos);
video::SColor blendLight(const v3f &vertex_pos, const v3f &vertex_normal, video::SColor tile_color); video::SColor blendLightColor(const v3f &vertex_pos, const v3f &vertex_normal);
void useTile(int index, bool disable_backface_culling); void useTile(int index, bool disable_backface_culling);
void useDefaultTile(bool set_color = true); void useDefaultTile(bool set_color = true);

@ -687,8 +687,9 @@ void drawItemStack(video::IVideoDriver *driver,
assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER); assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
video::SColor c = basecolor; video::SColor c = basecolor;
if (imesh->buffer_colors.size() > j) { if (imesh->buffer_colors.size() > j) {
std::pair<bool, video::SColor> p = imesh->buffer_colors[j]; ItemPartColor *p = &imesh->buffer_colors[j];
c = p.first ? p.second : basecolor; if (p->override_base)
c = p->color;
} }
colorizeMeshBuffer(buf, &c); colorizeMeshBuffer(buf, &c);
video::SMaterial &material = buf->getMaterial(); video::SMaterial &material = buf->getMaterial();

@ -323,7 +323,7 @@ void final_color_blend(video::SColor *result,
video::SColorf dayLight; video::SColorf dayLight;
get_sunlight_color(&dayLight, daynight_ratio); get_sunlight_color(&dayLight, daynight_ratio);
final_color_blend(result, 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, void final_color_blend(video::SColor *result,
@ -422,12 +422,19 @@ static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
struct FastFace struct FastFace
{ {
TileSpec tile; TileLayer layer;
video::S3DVertex vertices[4]; // Precalculated vertices 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, 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. // Position is at the center of the cube.
v3f pos = p * BS; 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); v3f normal(dir.X, dir.Y, dir.Z);
dest.push_back(FastFace());
FastFace& face = *dest.rbegin();
u16 li[4] = { li0, li1, li2, li3 }; 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] = { v2f32 f[4] = {
core::vector2d<f32>(x0 + w * abs_scale, y0 + h), core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
core::vector2d<f32>(x0, y0 + h), core::vector2d<f32>(x0, y0 + h),
core::vector2d<f32>(x0, y0), core::vector2d<f32>(x0, y0),
core::vector2d<f32>(x0 + w * abs_scale, y0) }; core::vector2d<f32>(x0 + w * abs_scale, y0) };
for (u8 i = 0; i < 4; i++) { for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
video::SColor c = encode_light_and_color(li[i], tile.color, TileLayer *layer = &tile.layers[layernum];
tile.emissive_light); if (layer->texture_id == 0)
if (!tile.emissive_light) continue;
applyFacesShading(c, normal);
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(); INodeDefManager *ndef = data->m_client->ndef();
const ContentFeatures &f = ndef->get(mn); const ContentFeatures &f = ndef->get(mn);
TileSpec spec = f.tiles[tileindex]; TileSpec tile = f.tiles[tileindex];
if (!spec.has_color) TileLayer *top_layer = NULL;
mn.getColor(f, &spec.color); 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 // Apply temporary crack
if (p == data->m_crack_pos_relative) if (p == data->m_crack_pos_relative)
spec.material_flags |= MATERIAL_FLAG_CRACK; top_layer->material_flags |= MATERIAL_FLAG_CRACK;
return spec; return tile;
} }
/* /*
@ -732,10 +769,9 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
}; };
u16 tile_index=facedir*16 + dir_i; u16 tile_index=facedir*16 + dir_i;
TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data); TileSpec tile = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
spec.rotation=dir_to_tile[tile_index + 1]; tile.rotation = dir_to_tile[tile_index + 1];
spec.texture = data->m_client->tsrc()->getTexture(spec.texture_id); return tile;
return spec;
} }
static void getTileInfo( static void getTileInfo(
@ -800,7 +836,9 @@ static void getTileInfo(
// eg. water and glass // eg. water and glass
if (equivalent) 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) if (data->m_smooth_lighting == false)
{ {
@ -880,12 +918,7 @@ static void updateFastFaceRow(
&& next_lights[1] == lights[1] && next_lights[1] == lights[1]
&& next_lights[2] == lights[2] && next_lights[2] == lights[2]
&& next_lights[3] == lights[3] && next_lights[3] == lights[3]
&& next_tile == tile && next_tile.isTileable(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_is_different = false; next_is_different = false;
continuous_tiles_count++; continuous_tiles_count++;
} }
@ -988,7 +1021,6 @@ static void updateAllFastFaceRows(MeshMakeData *data,
*/ */
MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
m_mesh(new scene::SMesh()),
m_minimap_mapblock(NULL), m_minimap_mapblock(NULL),
m_client(data->m_client), m_client(data->m_client),
m_driver(m_client->tsrc()->getDevice()->getVideoDriver()), 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_last_daynight_ratio((u32) -1),
m_daynight_diffs() 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_enable_shaders = data->m_use_shaders;
m_use_tangent_vertices = data->m_use_tangent_vertices; m_use_tangent_vertices = data->m_use_tangent_vertices;
m_enable_vbo = g_settings->getBool("enable_vbo"); 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[] = {0,1,2,2,3,0};
const u16 indices_alternate[] = {0,1,3,2,3,1}; const u16 indices_alternate[] = {0,1,3,2,3,1};
if(f.tile.texture == NULL) if (f.layer.texture == NULL)
continue; continue;
const u16 *indices_p = indices; const u16 *indices_p =
f.vertex_0_2_connected ? indices : indices_alternate;
/* collector.append(f.layer, f.vertices, 4, indices_p, 6,
Revert triangles for nicer looking gradient if the f.layernum);
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);
} }
} }
@ -1081,146 +1106,151 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
generator.generate(); generator.generate();
} }
collector.applyTileColors();
/* /*
Convert MeshCollector to SMesh Convert MeshCollector to SMesh
*/ */
for(u32 i = 0; i < collector.prebuffers.size(); i++) for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
{ for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
PreMeshBuffer &p = collector.prebuffers[i];
// Generate animation data
// - Cracks
if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
{ {
// Find the texture name plus ^[crack:N: PreMeshBuffer &p = collector.prebuffers[layer][i];
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;
}
if (!m_enable_shaders) { // Generate animation data
// Extract colors for day-night animation // - Cracks
// Dummy sunlight to handle non-sunlit areas if(p.layer.material_flags & MATERIAL_FLAG_CRACK)
video::SColorf sunlight; {
get_sunlight_color(&sunlight, 0); // Find the texture name plus ^[crack:N:
u32 vertex_count = std::ostringstream os(std::ios::binary);
m_use_tangent_vertices ? os<<m_tsrc->getTextureName(p.layer.texture_id)<<"^[crack";
p.tangent_vertices.size() : p.vertices.size(); if(p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
for (u32 j = 0; j < vertex_count; j++) { os<<"o"; // use ^[cracko
video::SColor *vc; os<<":"<<(u32)p.layer.animation_frame_count<<":";
if (m_use_tangent_vertices) { m_crack_materials.insert(std::make_pair(std::pair<u8, u32>(layer, i), os.str()));
vc = &p.tangent_vertices[j].Color; // 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 { } else {
vc = &p.vertices[j].Color; // Play all synchronized
m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
} }
video::SColor copy(*vc); // Replace tile texture with the first animation frame
if (vc->getAlpha() == 0) // No sunlight - no need to animate FrameSpec animation_frame = p.layer.frames[0];
final_color_blend(vc, copy, sunlight); // Finalize color p.layer.texture = animation_frame.texture;
else // Record color to animate }
m_daynight_diffs[i][j] = copy;
// The sunlight ratio has been stored, if (!m_enable_shaders) {
// delete alpha (for the final rendering). // Extract colors for day-night animation
vc->setAlpha(255); // 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);
}
}
// 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; Do some stuff to the mesh
p.tile.applyMaterialOptionsWithShaders(material); */
if (p.tile.normal_texture) { m_camera_offset = camera_offset;
material.setTexture(1, p.tile.normal_texture); translateMesh(m_mesh[layer],
} intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
material.setTexture(2, p.tile.flags_texture);
} else {
p.tile.applyMaterialOptions(material);
}
scene::SMesh *mesh = (scene::SMesh *)m_mesh;
// Create meshbuffer, add to mesh
if (m_use_tangent_vertices) { if (m_use_tangent_vertices) {
scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents(); scene::IMeshManipulator* meshmanip =
// Set material m_client->getSceneManager()->getMeshManipulator();
buf->Material = material; meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
// 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());
} }
}
/* if (m_mesh[layer])
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 0 #if 0
// Usually 1-700 faces and 1-7 materials // Usually 1-700 faces and 1-7 materials
std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces " std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
<<"and uses "<<m_mesh->getMeshBufferCount() <<"and uses "<<m_mesh[layer]->getMeshBufferCount()
<<" materials (meshbuffers)"<<std::endl; <<" materials (meshbuffers)"<<std::endl;
#endif #endif
// Use VBO for mesh (this just would set this for ever buffer) // Use VBO for mesh (this just would set this for ever buffer)
if (m_enable_vbo) { if (m_enable_vbo) {
m_mesh->setHardwareMappingHint(scene::EHM_STATIC); m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
}
} }
} }
@ -1235,14 +1265,15 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
MapBlockMesh::~MapBlockMesh() MapBlockMesh::~MapBlockMesh()
{ {
if (m_enable_vbo && m_mesh) { for (int m = 0; m < MAX_TILE_LAYERS; m++) {
for (u32 i = 0; i < m_mesh->getMeshBufferCount(); i++) { if (m_enable_vbo && m_mesh[m])
scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i); for (u32 i = 0; i < m_mesh[m]->getMeshBufferCount(); i++) {
m_driver->removeHardwareBuffer(buf); 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; delete m_minimap_mapblock;
} }
@ -1259,9 +1290,10 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
// Cracks // Cracks
if(crack != m_last_crack) if(crack != m_last_crack)
{ {
for (UNORDERED_MAP<u32, std::string>::iterator i = m_crack_materials.begin(); for (std::map<std::pair<u8, u32>, std::string>::iterator i =
i != m_crack_materials.end(); ++i) { m_crack_materials.begin(); i != m_crack_materials.end(); ++i) {
scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); scene::IMeshBuffer *buf = m_mesh[i->first.first]->
getMeshBuffer(i->first.second);
std::string basename = i->second; std::string basename = i->second;
// Create new texture name from original // 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, // If the current material is also animated,
// update animation info // update animation info
UNORDERED_MAP<u32, TileSpec>::iterator anim_iter = std::map<std::pair<u8, u32>, TileLayer>::iterator anim_iter =
m_animation_tiles.find(i->first); m_animation_tiles.find(i->first);
if (anim_iter != m_animation_tiles.end()){ if (anim_iter != m_animation_tiles.end()){
TileSpec &tile = anim_iter->second; TileLayer &tile = anim_iter->second;
tile.texture = new_texture; tile.texture = new_texture;
tile.texture_id = new_texture_id; tile.texture_id = new_texture_id;
// force animation update // force animation update
@ -1289,9 +1321,9 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
} }
// Texture animation // Texture animation
for (UNORDERED_MAP<u32, TileSpec>::iterator i = m_animation_tiles.begin(); for (std::map<std::pair<u8, u32>, TileLayer>::iterator i =
i != m_animation_tiles.end(); ++i) { m_animation_tiles.begin(); i != m_animation_tiles.end(); ++i) {
const TileSpec &tile = i->second; const TileLayer &tile = i->second;
// Figure out current frame // Figure out current frame
int frameoffset = m_animation_frame_offsets[i->first]; int frameoffset = m_animation_frame_offsets[i->first];
int frame = (int)(time * 1000 / tile.animation_frame_length_ms 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; 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]; FrameSpec animation_frame = tile.frames[frame];
buf->getMaterial().setTexture(0, animation_frame.texture); 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)) if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
{ {
// Force reload mesh to VBO // Force reload mesh to VBO
if (m_enable_vbo) { if (m_enable_vbo)
m_mesh->setDirty(); for (int m = 0; m < MAX_TILE_LAYERS; m++)
} m_mesh[m]->setDirty();
video::SColorf day_color; video::SColorf day_color;
get_sunlight_color(&day_color, daynight_ratio); 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.begin();
i != m_daynight_diffs.end(); ++i) 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(); video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
for(std::map<u32, video::SColor >::iterator for(std::map<u32, video::SColor >::iterator
j = i->second.begin(); j = i->second.begin();
j != i->second.end(); ++j) 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; 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) void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
{ {
if (camera_offset != m_camera_offset) { if (camera_offset != m_camera_offset) {
translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS)); for (u8 layer = 0; layer < 2; layer++) {
if (m_enable_vbo) { translateMesh(m_mesh[layer],
m_mesh->setDirty(); intToFloat(m_camera_offset - camera_offset, BS));
if (m_enable_vbo) {
m_mesh[layer]->setDirty();
}
} }
m_camera_offset = camera_offset; m_camera_offset = camera_offset;
} }
@ -1360,16 +1398,30 @@ void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
void MeshCollector::append(const TileSpec &tile, void MeshCollector::append(const TileSpec &tile,
const video::S3DVertex *vertices, u32 numVertices, const video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices) 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) { if (numIndices > 65535) {
dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl; dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
return; return;
} }
std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
PreMeshBuffer *p = NULL; PreMeshBuffer *p = NULL;
for (u32 i = 0; i < prebuffers.size(); i++) { for (u32 i = 0; i < buffers->size(); i++) {
PreMeshBuffer &pp = prebuffers[i]; PreMeshBuffer &pp = (*buffers)[i];
if (pp.tile != tile) if (pp.layer != layer)
continue; continue;
if (pp.indices.size() + numIndices > 65535) if (pp.indices.size() + numIndices > 65535)
continue; continue;
@ -1380,9 +1432,9 @@ void MeshCollector::append(const TileSpec &tile,
if (p == NULL) { if (p == NULL) {
PreMeshBuffer pp; PreMeshBuffer pp;
pp.tile = tile; pp.layer = layer;
prebuffers.push_back(pp); buffers->push_back(pp);
p = &prebuffers[prebuffers.size() - 1]; p = &(*buffers)[buffers->size() - 1];
} }
u32 vertex_count; u32 vertex_count;
@ -1416,16 +1468,31 @@ void MeshCollector::append(const TileSpec &tile,
const video::S3DVertex *vertices, u32 numVertices, const video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices, const u16 *indices, u32 numIndices,
v3f pos, video::SColor c, u8 light_source) 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) { if (numIndices > 65535) {
dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl; dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
return; return;
} }
std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
PreMeshBuffer *p = NULL; PreMeshBuffer *p = NULL;
for (u32 i = 0; i < prebuffers.size(); i++) { for (u32 i = 0; i < buffers->size(); i++) {
PreMeshBuffer &pp = prebuffers[i]; PreMeshBuffer &pp = (*buffers)[i];
if(pp.tile != tile) if(pp.layer != layer)
continue; continue;
if(pp.indices.size() + numIndices > 65535) if(pp.indices.size() + numIndices > 65535)
continue; continue;
@ -1436,9 +1503,9 @@ void MeshCollector::append(const TileSpec &tile,
if (p == NULL) { if (p == NULL) {
PreMeshBuffer pp; PreMeshBuffer pp;
pp.tile = tile; pp.layer = layer;
prebuffers.push_back(pp); buffers->push_back(pp);
p = &prebuffers[prebuffers.size() - 1]; p = &(*buffers)[buffers->size() - 1];
} }
video::SColor original_c = c; 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, void MeshCollector::applyTileColors()
u8 emissive_light) {
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 // Get components
f32 day = (light & 0xff) / 255.0f; u32 day = (light & 0xff);
f32 night = (light >> 8) / 255.0f; u32 night = (light >> 8);
// Add emissive light // Add emissive light
night += emissive_light * 0.01f; night += emissive_light * 2.5f;
if (night > 255) if (night > 255)
night = 255; night = 255;
// Since we don't know if the day light is sunlight or // 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; day = 0;
else else
day = day - night; day = day - night;
f32 sum = day + night; u32 sum = day + night;
// Ratio of sunlight: // Ratio of sunlight:
float r; u32 r;
if (sum > 0) if (sum > 0)
r = day / sum; r = day * 255 / sum;
else else
r = 0; r = 0;
// Average light: // Average light:
float b = (day + night) / 2; float b = (day + night) / 2;
return video::SColor(r * 255, b * color.getRed(), b * color.getGreen(), return video::SColor(r, b, b, b);
b * color.getBlue());
} }

@ -108,7 +108,12 @@ public:
scene::IMesh *getMesh() scene::IMesh *getMesh()
{ {
return m_mesh; return m_mesh[0];
}
scene::IMesh *getMesh(u8 layer)
{
return m_mesh[layer];
} }
MinimapMapblock *moveMinimapMapblock() MinimapMapblock *moveMinimapMapblock()
@ -132,7 +137,7 @@ public:
void updateCameraOffset(v3s16 camera_offset); void updateCameraOffset(v3s16 camera_offset);
private: private:
scene::IMesh *m_mesh; scene::IMesh *m_mesh[MAX_TILE_LAYERS];
MinimapMapblock *m_minimap_mapblock; MinimapMapblock *m_minimap_mapblock;
Client *m_client; Client *m_client;
video::IVideoDriver *m_driver; video::IVideoDriver *m_driver;
@ -150,20 +155,23 @@ private:
// Animation info: cracks // Animation info: cracks
// Last crack value passed to animate() // Last crack value passed to animate()
int m_last_crack; int m_last_crack;
// Maps mesh buffer (i.e. material) indices to base texture names // Maps mesh and mesh buffer (i.e. material) indices to base texture names
UNORDERED_MAP<u32, std::string> m_crack_materials; std::map<std::pair<u8, u32>, std::string> m_crack_materials;
// Animation info: texture animationi // Animation info: texture animationi
// Maps meshbuffers to TileSpecs // Maps mesh and mesh buffer indices to TileSpecs
UNORDERED_MAP<u32, TileSpec> m_animation_tiles; // Keys are pairs of (mesh index, buffer index in the mesh)
UNORDERED_MAP<u32, int> m_animation_frames; // last animation frame std::map<std::pair<u8, u32>, TileLayer> m_animation_tiles;
UNORDERED_MAP<u32, int> m_animation_frame_offsets; 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 // Animation info: day/night transitions
// Last daynight_ratio value passed to animate() // Last daynight_ratio value passed to animate()
u32 m_last_daynight_ratio; u32 m_last_daynight_ratio;
// For each meshbuffer, stores pre-baked colors of sunlit vertices // For each mesh and mesh buffer, stores pre-baked colors
std::map<u32, std::map<u32, video::SColor > > m_daynight_diffs; // 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? // Camera offset info -> do we have to translate the mesh?
v3s16 m_camera_offset; v3s16 m_camera_offset;
@ -176,7 +184,7 @@ private:
*/ */
struct PreMeshBuffer struct PreMeshBuffer
{ {
TileSpec tile; TileLayer layer;
std::vector<u16> indices; std::vector<u16> indices;
std::vector<video::S3DVertex> vertices; std::vector<video::S3DVertex> vertices;
std::vector<video::S3DVertexTangents> tangent_vertices; std::vector<video::S3DVertexTangents> tangent_vertices;
@ -184,7 +192,7 @@ struct PreMeshBuffer
struct MeshCollector struct MeshCollector
{ {
std::vector<PreMeshBuffer> prebuffers; std::vector<PreMeshBuffer> prebuffers[MAX_TILE_LAYERS];
bool m_use_tangent_vertices; bool m_use_tangent_vertices;
MeshCollector(bool use_tangent_vertices): MeshCollector(bool use_tangent_vertices):
@ -193,27 +201,38 @@ struct MeshCollector
} }
void append(const TileSpec &material, 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 video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices); const u16 *indices, u32 numIndices, u8 layernum);
void append(const TileSpec &material, 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 video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices, const u16 *indices, u32 numIndices, v3f pos,
v3f pos, video::SColor c, u8 light_source); 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 * The result is not the final color, but a
* half-baked vertex color. * 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, * \param light the first 8 bits are day light,
* the last 8 bits are night light * the last 8 bits are night light
* \param color the node's color
* \param emissive_light amount of light the surface emits, * \param emissive_light amount of light the surface emits,
* from 0 to LIGHT_SUN. * from 0 to LIGHT_SUN.
*/ */
video::SColor encode_light_and_color(u16 light, const video::SColor &color, video::SColor encode_light(u16 light, u8 emissive_light);
u8 emissive_light);
// Compute light at node // Compute light at node
u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef); u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef);

@ -385,48 +385,50 @@ void recalculateBoundingBox(scene::IMesh *src_mesh)
src_mesh->setBoundingBox(bbox); 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(); scene::SMesh* dst_mesh = new scene::SMesh();
for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) { for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
scene::IMeshBuffer *buf = src_mesh->getMeshBuffer(j); scene::IMeshBuffer *temp_buf = cloneMeshBuffer(
switch (buf->getVertexType()) { src_mesh->getMeshBuffer(j));
case video::EVT_STANDARD: { dst_mesh->addMeshBuffer(temp_buf);
video::S3DVertex *v = temp_buf->drop();
(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;
}
}
} }
return dst_mesh; return dst_mesh;
} }

@ -83,10 +83,15 @@ void rotateMeshXYby (scene::IMesh *mesh, f64 degrees);
void rotateMeshXZby (scene::IMesh *mesh, f64 degrees); void rotateMeshXZby (scene::IMesh *mesh, f64 degrees);
void rotateMeshYZby (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. 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. Convert nodeboxes to mesh. Each tile goes into a different buffer.

@ -148,9 +148,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
Add node and tile color and palette Add node and tile color and palette
Fix plantlike visual_scale being applied squared and add compatibility Fix plantlike visual_scale being applied squared and add compatibility
with pre-30 clients by sending sqrt(visual_scale) 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 // Server's supported network protocol range
#define SERVER_PROTOCOL_VERSION_MIN 24 #define SERVER_PROTOCOL_VERSION_MIN 24

@ -378,13 +378,13 @@ void ContentFeatures::reset()
void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
{ {
if (protocol_version < 30) { if (protocol_version < 31) {
serializeOld(os, protocol_version); serializeOld(os, protocol_version);
return; return;
} }
// version // version
writeU8(os, 9); writeU8(os, 10);
// general // general
os << serializeString(name); os << serializeString(name);
@ -404,6 +404,8 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
writeU8(os, 6); writeU8(os, 6);
for (u32 i = 0; i < 6; i++) for (u32 i = 0; i < 6; i++)
tiledef[i].serialize(os, protocol_version); 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); writeU8(os, CF_SPECIAL_COUNT);
for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) { for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) {
tiledef_special[i].serialize(os, protocol_version); tiledef_special[i].serialize(os, protocol_version);
@ -492,7 +494,7 @@ void ContentFeatures::deSerialize(std::istream &is)
if (version < 9) { if (version < 9) {
deSerializeOld(is, version); deSerializeOld(is, version);
return; return;
} else if (version > 9) { } else if (version > 10) {
throw SerializationError("unsupported ContentFeatures version"); throw SerializationError("unsupported ContentFeatures version");
} }
@ -516,6 +518,9 @@ void ContentFeatures::deSerialize(std::istream &is)
throw SerializationError("unsupported tile count"); throw SerializationError("unsupported tile count");
for (u32 i = 0; i < 6; i++) for (u32 i = 0; i < 6; i++)
tiledef[i].deSerialize(is, version, drawtype); 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) if (readU8(is) != CF_SPECIAL_COUNT)
throw SerializationError("unsupported CF_SPECIAL_COUNT"); throw SerializationError("unsupported CF_SPECIAL_COUNT");
for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
@ -581,7 +586,7 @@ void ContentFeatures::deSerialize(std::istream &is)
} }
#ifndef SERVER #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, TileDef *tiledef, u32 shader_id, bool use_normal_texture,
bool backface_culling, u8 material_type) bool backface_culling, u8 material_type)
{ {
@ -774,14 +779,18 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
// Tiles (fill in f->tiles[]) // Tiles (fill in f->tiles[])
for (u16 j = 0; j < 6; j++) { 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, tsettings.use_normal_texture,
tiledef[j].backface_culling, material_type); 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[]) // Special tiles (fill in f->special_tiles[])
for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) { 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, tile_shader[j], tsettings.use_normal_texture,
tiledef_special[j].backface_culling, material_type); 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) if (protocol_version < 30 && drawtype == NDT_PLANTLIKE)
compatible_visual_scale = sqrt(visual_scale); 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 // Protocol >= 24
if (protocol_version < 30) { if (protocol_version < 31) {
writeU8(os, protocol_version < 27 ? 7 : 8); writeU8(os, protocol_version < 27 ? 7 : 8);
os << serializeString(name); os << serializeString(name);
@ -1553,7 +1573,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
writeF1000(os, compatible_visual_scale); writeF1000(os, compatible_visual_scale);
writeU8(os, 6); writeU8(os, 6);
for (u32 i = 0; i < 6; i++) 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); writeU8(os, CF_SPECIAL_COUNT);
for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) for (u32 i = 0; i < CF_SPECIAL_COUNT; i++)
tiledef_special[i].serialize(os, protocol_version); tiledef_special[i].serialize(os, protocol_version);

@ -280,6 +280,8 @@ struct ContentFeatures
#endif #endif
float visual_scale; // Misc. scale parameter float visual_scale; // Misc. scale parameter
TileDef tiledef[6]; TileDef tiledef[6];
// These will be drawn over the base tiles.
TileDef tiledef_overlay[6];
TileDef tiledef_special[CF_SPECIAL_COUNT]; // eg. flowing liquid TileDef tiledef_special[CF_SPECIAL_COUNT]; // eg. flowing liquid
// If 255, the node is opaque. // If 255, the node is opaque.
// Otherwise it uses texture alpha. // Otherwise it uses texture alpha.
@ -405,7 +407,7 @@ struct ContentFeatures
} }
#ifndef SERVER #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, u32 shader_id, bool use_normal_texture, bool backface_culling,
u8 material_type); u8 material_type);
void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,

@ -620,7 +620,7 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef,
{ {
// Texture // Texture
u8 texid = myrand_range(0, 5); u8 texid = myrand_range(0, 5);
const TileSpec &tile = f.tiles[texid]; const TileLayer &tile = f.tiles[texid].layers[0];
video::ITexture *texture; video::ITexture *texture;
struct TileAnimationParams anim; struct TileAnimationParams anim;
anim.type = TAT_NONE; anim.type = TAT_NONE;

@ -426,6 +426,34 @@ ContentFeatures read_content_features(lua_State *L, int index)
} }
lua_pop(L, 1); 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 = {} // special_tiles = {}
lua_getfield(L, index, "special_tiles"); lua_getfield(L, index, "special_tiles");
// If nil, try the deprecated name "special_materials" instead // If nil, try the deprecated name "special_materials" instead

@ -235,27 +235,16 @@ WieldMeshSceneNode::~WieldMeshSceneNode()
g_extrusion_mesh_cache = NULL; g_extrusion_mesh_cache = NULL;
} }
void WieldMeshSceneNode::setCube(const TileSpec tiles[6], void WieldMeshSceneNode::setCube(const ContentFeatures &f,
v3f wield_scale, ITextureSource *tsrc) v3f wield_scale, ITextureSource *tsrc)
{ {
scene::IMesh *cubemesh = g_extrusion_mesh_cache->createCube(); scene::IMesh *cubemesh = g_extrusion_mesh_cache->createCube();
changeToMesh(cubemesh); scene::SMesh *copy = cloneMesh(cubemesh);
cubemesh->drop(); cubemesh->drop();
postProcessNodeMesh(copy, f, false, true, &m_material_type, &m_colors);
changeToMesh(copy);
copy->drop();
m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR); 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, 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); dim = core::dimension2d<u32>(dim.Width, frame_height);
} }
scene::IMesh *mesh = g_extrusion_mesh_cache->create(dim); scene::IMesh *mesh = g_extrusion_mesh_cache->create(dim);
changeToMesh(mesh); scene::SMesh *copy = cloneMesh(mesh);
mesh->drop(); mesh->drop();
changeToMesh(copy);
copy->drop();
m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR_EXTRUDED); m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR_EXTRUDED);
@ -321,12 +312,12 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
// Color-related // Color-related
m_colors.clear(); 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 wield_image is defined, it overrides everything else
if (def.wield_image != "") { if (def.wield_image != "") {
setExtruded(def.wield_image, def.wield_scale, tsrc, 1); setExtruded(def.wield_image, def.wield_scale, tsrc, 1);
m_colors.push_back(basecolor); m_colors.push_back(ItemPartColor());
return; return;
} }
// Handle nodes // Handle nodes
@ -334,66 +325,50 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
else if (def.type == ITEM_NODE) { else if (def.type == ITEM_NODE) {
if (f.mesh_ptr[0]) { if (f.mesh_ptr[0]) {
// e.g. mesh nodes and nodeboxes // e.g. mesh nodes and nodeboxes
changeToMesh(f.mesh_ptr[0]); scene::SMesh *mesh = cloneMesh(f.mesh_ptr[0]);
// mesh_ptr[0] is pre-scaled by BS * f->visual_scale 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( m_meshnode->setScale(
def.wield_scale * WIELD_SCALE_FACTOR def.wield_scale * WIELD_SCALE_FACTOR
/ (BS * f.visual_scale)); / (BS * f.visual_scale));
} else if (f.drawtype == NDT_AIRLIKE) { } else if (f.drawtype == NDT_AIRLIKE) {
changeToMesh(NULL); changeToMesh(NULL);
} else if (f.drawtype == NDT_PLANTLIKE) { } 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) { } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) {
setCube(f.tiles, def.wield_scale, tsrc); setCube(f, def.wield_scale, tsrc);
} else { } else {
MeshMakeData mesh_make_data(client, false); MeshMakeData mesh_make_data(client, false);
MapNode mesh_make_node(id, 255, 0); MapNode mesh_make_node(id, 255, 0);
mesh_make_data.fillSingleNode(&mesh_make_node); mesh_make_data.fillSingleNode(&mesh_make_node);
MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0)); MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
changeToMesh(mapblock_mesh.getMesh()); scene::SMesh *mesh = cloneMesh(mapblock_mesh.getMesh());
translateMesh(m_meshnode->getMesh(), v3f(-BS, -BS, -BS)); 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( m_meshnode->setScale(
def.wield_scale * WIELD_SCALE_FACTOR def.wield_scale * WIELD_SCALE_FACTOR
/ (BS * f.visual_scale)); / (BS * f.visual_scale));
} }
u32 material_count = m_meshnode->getMaterialCount(); 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) { for (u32 i = 0; i < material_count; ++i) {
const TileSpec *tile = &(f.tiles[i]);
video::SMaterial &material = m_meshnode->getMaterial(i); video::SMaterial &material = m_meshnode->getMaterial(i);
material.setFlag(video::EMF_BACK_FACE_CULLING, true); material.setFlag(video::EMF_BACK_FACE_CULLING, true);
material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter); material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter);
material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_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; return;
} }
else if (def.inventory_image != "") { else if (def.inventory_image != "") {
setExtruded(def.inventory_image, def.wield_scale, tsrc, 1); setExtruded(def.inventory_image, def.wield_scale, tsrc, 1);
m_colors.push_back(basecolor); m_colors.push_back(ItemPartColor());
return; return;
} }
@ -413,9 +388,9 @@ void WieldMeshSceneNode::setColor(video::SColor c)
u8 blue = c.getBlue(); u8 blue = c.getBlue();
u32 mc = mesh->getMeshBufferCount(); u32 mc = mesh->getMeshBufferCount();
for (u32 j = 0; j < mc; j++) { for (u32 j = 0; j < mc; j++) {
video::SColor bc(0xFFFFFFFF); video::SColor bc(m_base_color);
if (m_colors.size() > j) if ((m_colors.size() > j) && (m_colors[j].override_base))
bc = m_colors[j]; bc = m_colors[j].color;
video::SColor buffercolor(255, video::SColor buffercolor(255,
bc.getRed() * red / 255, bc.getRed() * red / 255,
bc.getGreen() * green / 255, bc.getGreen() * green / 255,
@ -439,19 +414,7 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
m_meshnode->setMesh(dummymesh); m_meshnode->setMesh(dummymesh);
dummymesh->drop(); // m_meshnode grabbed it dummymesh->drop(); // m_meshnode grabbed it
} else { } else {
if (m_lighting) { m_meshnode->setMesh(mesh);
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->setMaterialFlag(video::EMF_LIGHTING, m_lighting); 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(); g_extrusion_mesh_cache->grab();
} }
scene::IMesh *mesh; scene::SMesh *mesh;
// If inventory_image is defined, it overrides everything else // If inventory_image is defined, it overrides everything else
if (def.inventory_image != "") { if (def.inventory_image != "") {
mesh = getExtrudedMesh(tsrc, def.inventory_image); mesh = getExtrudedMesh(tsrc, def.inventory_image);
result->mesh = mesh; result->buffer_colors.push_back(ItemPartColor());
result->buffer_colors.push_back(
std::pair<bool, video::SColor>(false, video::SColor(0xFFFFFFFF)));
} else if (def.type == ITEM_NODE) { } else if (def.type == ITEM_NODE) {
if (f.mesh_ptr[0]) { if (f.mesh_ptr[0]) {
mesh = cloneMesh(f.mesh_ptr[0]); mesh = cloneMesh(f.mesh_ptr[0]);
scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
} else if (f.drawtype == NDT_PLANTLIKE) { } else if (f.drawtype == NDT_PLANTLIKE) {
mesh = getExtrudedMesh(tsrc, 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 } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES
|| f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) { || 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)); scaleMesh(mesh, v3f(1.2, 1.2, 1.2));
} else { } else {
MeshMakeData mesh_make_data(client, false); MeshMakeData mesh_make_data(client, false);
@ -519,32 +482,27 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
u32 mc = mesh->getMeshBufferCount(); u32 mc = mesh->getMeshBufferCount();
for (u32 i = 0; i < mc; ++i) { for (u32 i = 0; i < mc; ++i) {
const TileSpec *tile = &(f.tiles[i]);
scene::IMeshBuffer *buf = mesh->getMeshBuffer(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(); video::SMaterial &material = buf->getMaterial();
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
material.setFlag(video::EMF_BILINEAR_FILTER, false); material.setFlag(video::EMF_BILINEAR_FILTER, false);
material.setFlag(video::EMF_TRILINEAR_FILTER, false); material.setFlag(video::EMF_TRILINEAR_FILTER, false);
material.setFlag(video::EMF_BACK_FACE_CULLING, true); material.setFlag(video::EMF_BACK_FACE_CULLING, true);
material.setFlag(video::EMF_LIGHTING, false); 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); rotateMeshXZby(mesh, -45);
rotateMeshYZby(mesh, -30); 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) const std::string &imagename)
{ {
video::ITexture *texture = tsrc->getTextureForMesh(imagename); video::ITexture *texture = tsrc->getTextureForMesh(imagename);
@ -553,7 +511,9 @@ scene::IMesh * getExtrudedMesh(ITextureSource *tsrc,
} }
core::dimension2d<u32> dim = texture->getSize(); 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 // Customize material
video::SMaterial &material = mesh->getMeshBuffer(0)->getMaterial(); video::SMaterial &material = mesh->getMeshBuffer(0)->getMaterial();
@ -569,3 +529,57 @@ scene::IMesh * getExtrudedMesh(ITextureSource *tsrc,
return mesh; 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);
}
}
}
}

@ -26,17 +26,41 @@ with this program; if not, write to the Free Software Foundation, Inc.,
struct ItemStack; struct ItemStack;
class Client; class Client;
class ITextureSource; 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 struct ItemMesh
{ {
scene::IMesh *mesh; scene::IMesh *mesh;
/*! /*!
* Stores the color of each mesh buffer. * 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() {} ItemMesh() : mesh(NULL), buffer_colors() {}
}; };
@ -51,7 +75,8 @@ public:
s32 id = -1, bool lighting = false); s32 id = -1, bool lighting = false);
virtual ~WieldMeshSceneNode(); 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, void setExtruded(const std::string &imagename, v3f wield_scale,
ITextureSource *tsrc, u8 num_frames); ITextureSource *tsrc, u8 num_frames);
void setItem(const ItemStack &item, Client *client); void setItem(const ItemStack &item, Client *client);
@ -84,7 +109,12 @@ private:
* Stores the colors of the mesh's mesh buffers. * Stores the colors of the mesh's mesh buffers.
* This does not include lighting. * 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, // Bounding box culling is disabled for this type of scene node,
// so this variable is just required so we can implement // so this variable is just required so we can implement
@ -94,5 +124,16 @@ private:
void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result); 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 #endif