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}, --[[
^ 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 ]]

@ -487,11 +487,15 @@ 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) {
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 {
else
// Replace with the new mesh
block->mesh = r.mesh;
}

@ -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,23 +220,21 @@ 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);
}
@ -243,23 +244,17 @@ struct TileSpec
{
switch (material_type) {
case TILE_MATERIAL_BASIC:
case TILE_MATERIAL_WAVING_LEAVES:
case TILE_MATERIAL_WAVING_PLANTS:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
case TILE_MATERIAL_ALPHA:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_LIQUID_TRANSPARENT:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_LIQUID_OPAQUE:
material.MaterialType = video::EMT_SOLID;
break;
case TILE_MATERIAL_WAVING_LEAVES:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
case TILE_MATERIAL_WAVING_PLANTS:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
}
material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING)
? true : false;
@ -285,26 +280,26 @@ struct TileSpec
}
}
// ordered for performance! please do not reorder unless you pahole it first.
bool isTileable() const
{
return (material_flags & MATERIAL_FLAG_TILEABLE_HORIZONTAL)
&& (material_flags & MATERIAL_FLAG_TILEABLE_VERTICAL);
}
video::ITexture *texture;
u32 texture_id;
// The color of the tile, or if the tile does not own
// a color then the color of the node owning this tile.
/*!
* The color of the tile, or if the tile does not own
* a color then the color of the node owning this tile.
*/
video::SColor color;
// Material parameters
u8 material_type;
u8 material_flags;
u8 rotation;
//! This much light does the tile emit.
u8 emissive_light;
u32 shader_id;
video::ITexture *normal_texture;
// cacheline (64)
video::ITexture *flags_texture;
// Animation parameters
u16 animation_frame_length_ms;
u8 animation_frame_count;
@ -313,4 +308,39 @@ struct TileSpec
std::vector<FrameSpec> frames;
};
/*!
* Defines a face of a node. May have up to two layers.
*/
struct TileSpec
{
TileSpec():
rotation(0),
emissive_light(0)
{
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++)
layers[layer] = TileLayer();
}
/*!
* Returns true if this tile can be merged with the other tile.
*/
bool isTileable(const TileSpec &other) const {
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
if (layers[layer] != other.layers[layer])
return false;
if (!layers[layer].isTileable())
return false;
}
return rotation == 0
&& rotation == other.rotation
&& emissive_light == other.emissive_light;
}
u8 rotation;
//! This much light does the tile emit.
u8 emissive_light;
//! The first is base texture, the second is overlay.
TileLayer layers[MAX_TILE_LAYERS];
};
#endif

@ -290,6 +290,11 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
struct MeshBufList
{
/*!
* Specifies in which layer the list is.
* All lists which are in a lower layer are rendered before this list.
*/
u8 layer;
video::SMaterial m;
std::vector<scene::IMeshBuffer*> bufs;
};
@ -303,7 +308,7 @@ struct MeshBufListList
lists.clear();
}
void add(scene::IMeshBuffer *buf)
void add(scene::IMeshBuffer *buf, u8 layer)
{
const video::SMaterial &m = buf->getMaterial();
for(std::vector<MeshBufList>::iterator i = lists.begin();
@ -315,12 +320,16 @@ struct MeshBufListList
if (l.m.TextureLayer[0].Texture != m.TextureLayer[0].Texture)
continue;
if(l.layer != layer)
continue;
if (l.m == m) {
l.bufs.push_back(buf);
return;
}
}
MeshBufList l;
l.layer = layer;
l.m = m;
l.bufs.push_back(buf);
lists.push_back(l);
@ -434,12 +443,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
scene::IMesh *mesh = mapBlockMesh->getMesh();
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++)
{
for (u32 i = 0; i < c; i++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
video::SMaterial& material = buf->getMaterial();
@ -451,12 +460,17 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
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);
}
}
}
}

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

@ -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);

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

@ -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,8 +422,15 @@ 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,
@ -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<f32>(x0 + w * abs_scale, y0 + h),
core::vector2d<f32>(x0, y0 + h),
core::vector2d<f32>(x0, y0),
core::vector2d<f32>(x0 + w * abs_scale, y0) };
for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
TileLayer *layer = &tile.layers[layernum];
if (layer->texture_id == 0)
continue;
dest.push_back(FastFace());
FastFace& face = *dest.rbegin();
for (u8 i = 0; i < 4; i++) {
video::SColor c = encode_light_and_color(li[i], tile.color,
tile.emissive_light);
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]);
}
face.tile = tile;
/*
Revert triangles for nicer looking gradient if the
brightness of vertices 1 and 3 differ less than
the brightness of vertices 0 and 2.
*/
face.vertex_0_2_connected = vertex_0_2_connected;
face.layer = *layer;
face.layernum = layernum;
}
}
/*
@ -663,13 +693,20 @@ TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
{
INodeDefManager *ndef = data->m_client->ndef();
const ContentFeatures &f = ndef->get(mn);
TileSpec spec = f.tiles[tileindex];
if (!spec.has_color)
mn.getColor(f, &spec.color);
TileSpec tile = f.tiles[tileindex];
TileLayer *top_layer = NULL;
for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
TileLayer *layer = &tile.layers[layernum];
if (layer->texture_id == 0)
continue;
top_layer = layer;
if (!layer->has_color)
mn.getColor(f, &(layer->color));
}
// Apply temporary crack
if (p == data->m_crack_pos_relative)
spec.material_flags |= MATERIAL_FLAG_CRACK;
return spec;
top_layer->material_flags |= MATERIAL_FLAG_CRACK;
return tile;
}
/*
@ -732,10 +769,9 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
};
u16 tile_index=facedir*16 + dir_i;
TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
spec.rotation=dir_to_tile[tile_index + 1];
spec.texture = data->m_client->tsrc()->getTexture(spec.texture_id);
return spec;
TileSpec tile = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
tile.rotation = dir_to_tile[tile_index + 1];
return tile;
}
static void getTileInfo(
@ -800,7 +836,9 @@ static void getTileInfo(
// eg. water and glass
if (equivalent)
tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++)
tile.layers[layernum].material_flags |=
MATERIAL_FLAG_BACKFACE_CULLING;
if (data->m_smooth_lighting == false)
{
@ -880,12 +918,7 @@ static void updateFastFaceRow(
&& next_lights[1] == lights[1]
&& next_lights[2] == lights[2]
&& next_lights[3] == lights[3]
&& next_tile == tile
&& tile.rotation == 0
&& (tile.material_flags & MATERIAL_FLAG_TILEABLE_HORIZONTAL)
&& (tile.material_flags & MATERIAL_FLAG_TILEABLE_VERTICAL)
&& tile.color == next_tile.color
&& tile.emissive_light == next_tile.emissive_light) {
&& next_tile.isTileable(tile)) {
next_is_different = false;
continuous_tiles_count++;
}
@ -988,7 +1021,6 @@ static void updateAllFastFaceRows(MeshMakeData *data,
*/
MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
m_mesh(new scene::SMesh()),
m_minimap_mapblock(NULL),
m_client(data->m_client),
m_driver(m_client->tsrc()->getDevice()->getVideoDriver()),
@ -1000,6 +1032,8 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
m_last_daynight_ratio((u32) -1),
m_daynight_diffs()
{
for (int m = 0; m < MAX_TILE_LAYERS; m++)
m_mesh[m] = new scene::SMesh();
m_enable_shaders = data->m_use_shaders;
m_use_tangent_vertices = data->m_use_tangent_vertices;
m_enable_vbo = g_settings->getBool("enable_vbo");
@ -1048,23 +1082,14 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
const u16 indices[] = {0,1,2,2,3,0};
const u16 indices_alternate[] = {0,1,3,2,3,1};
if(f.tile.texture == NULL)
if (f.layer.texture == NULL)
continue;
const u16 *indices_p = indices;
const u16 *indices_p =
f.vertex_0_2_connected ? indices : indices_alternate;
/*
Revert triangles for nicer looking gradient if the
brightness of vertices 1 and 3 differ less than
the brightness of vertices 0 and 2.
*/
if (fabs(f.vertices[0].Color.getLuminance()
- f.vertices[2].Color.getLuminance())
> fabs(f.vertices[1].Color.getLuminance()
- f.vertices[3].Color.getLuminance()))
indices_p = indices_alternate;
collector.append(f.tile, f.vertices, 4, indices_p, 6);
collector.append(f.layer, f.vertices, 4, indices_p, 6,
f.layernum);
}
}
@ -1081,47 +1106,50 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
generator.generate();
}
collector.applyTileColors();
/*
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];
PreMeshBuffer &p = collector.prebuffers[layer][i];
// Generate animation data
// - Cracks
if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
if(p.layer.material_flags & MATERIAL_FLAG_CRACK)
{
// Find the texture name plus ^[crack:N:
std::ostringstream os(std::ios::binary);
os<<m_tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
os<<m_tsrc->getTextureName(p.layer.texture_id)<<"^[crack";
if(p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
os<<"o"; // use ^[cracko
os<<":"<<(u32)p.tile.animation_frame_count<<":";
m_crack_materials.insert(std::make_pair(i, os.str()));
os<<":"<<(u32)p.layer.animation_frame_count<<":";
m_crack_materials.insert(std::make_pair(std::pair<u8, u32>(layer, i), os.str()));
// Replace tile texture with the cracked one
p.tile.texture = m_tsrc->getTextureForMesh(
p.layer.texture = m_tsrc->getTextureForMesh(
os.str()+"0",
&p.tile.texture_id);
&p.layer.texture_id);
}
// - Texture animation
if (p.tile.material_flags & MATERIAL_FLAG_ANIMATION) {
if (p.layer.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;
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[i] = 100000 * (2.0 + noise3d(
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 {
// Play all synchronized
m_animation_frame_offsets[i] = 0;
m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
}
// Replace tile texture with the first animation frame
FrameSpec animation_frame = p.tile.frames[0];
p.tile.texture = animation_frame.texture;
FrameSpec animation_frame = p.layer.frames[0];
p.layer.texture = animation_frame.texture;
}
if (!m_enable_shaders) {
@ -1143,7 +1171,7 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
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;
m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
// The sunlight ratio has been stored,
// delete alpha (for the final rendering).
@ -1157,20 +1185,20 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
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);
material.setTexture(0, p.layer.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.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.tile.flags_texture);
material.setTexture(2, p.layer.flags_texture);
} else {
p.tile.applyMaterialOptions(material);
p.layer.applyMaterialOptions(material);
}
scene::SMesh *mesh = (scene::SMesh *)m_mesh;
scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
// Create meshbuffer, add to mesh
if (m_use_tangent_vertices) {
@ -1196,31 +1224,33 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
}
}
/*
Do some stuff to the mesh
*/
m_camera_offset = camera_offset;
translateMesh(m_mesh,
translateMesh(m_mesh[layer],
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);
meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
}
if (m_mesh)
if (m_mesh[layer])
{
#if 0
// Usually 1-700 faces and 1-7 materials
std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
<<"and uses "<<m_mesh->getMeshBufferCount()
<<"and uses "<<m_mesh[layer]->getMeshBufferCount()
<<" materials (meshbuffers)"<<std::endl;
#endif
// Use VBO for mesh (this just would set this for ever buffer)
if (m_enable_vbo) {
m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
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);
for (int m = 0; m < MAX_TILE_LAYERS; m++) {
if (m_enable_vbo && m_mesh[m])
for (u32 i = 0; i < m_mesh[m]->getMeshBufferCount(); i++) {
scene::IMeshBuffer *buf = m_mesh[m]->getMeshBuffer(i);
m_driver->removeHardwareBuffer(buf);
}
m_mesh[m]->drop();
m_mesh[m] = NULL;
}
m_mesh->drop();
m_mesh = NULL;
delete m_minimap_mapblock;
}
@ -1259,9 +1290,10 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
// Cracks
if(crack != m_last_crack)
{
for (UNORDERED_MAP<u32, std::string>::iterator i = m_crack_materials.begin();
i != m_crack_materials.end(); ++i) {
scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
for (std::map<std::pair<u8, u32>, std::string>::iterator i =
m_crack_materials.begin(); i != m_crack_materials.end(); ++i) {
scene::IMeshBuffer *buf = m_mesh[i->first.first]->
getMeshBuffer(i->first.second);
std::string basename = i->second;
// Create new texture name from original
@ -1274,10 +1306,10 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
// If the current material is also animated,
// update animation info
UNORDERED_MAP<u32, TileSpec>::iterator anim_iter =
std::map<std::pair<u8, u32>, TileLayer>::iterator anim_iter =
m_animation_tiles.find(i->first);
if (anim_iter != m_animation_tiles.end()){
TileSpec &tile = anim_iter->second;
TileLayer &tile = anim_iter->second;
tile.texture = new_texture;
tile.texture_id = new_texture_id;
// force animation update
@ -1289,9 +1321,9 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
}
// Texture animation
for (UNORDERED_MAP<u32, TileSpec>::iterator i = m_animation_tiles.begin();
i != m_animation_tiles.end(); ++i) {
const TileSpec &tile = i->second;
for (std::map<std::pair<u8, u32>, TileLayer>::iterator i =
m_animation_tiles.begin(); i != m_animation_tiles.end(); ++i) {
const TileLayer &tile = i->second;
// Figure out current frame
int frameoffset = m_animation_frame_offsets[i->first];
int frame = (int)(time * 1000 / tile.animation_frame_length_ms
@ -1302,7 +1334,8 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
m_animation_frames[i->first] = frame;
scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
scene::IMeshBuffer *buf = m_mesh[i->first.first]->
getMeshBuffer(i->first.second);
FrameSpec animation_frame = tile.frames[frame];
buf->getMaterial().setTexture(0, animation_frame.texture);
@ -1318,22 +1351,24 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
{
// Force reload mesh to VBO
if (m_enable_vbo) {
m_mesh->setDirty();
}
if (m_enable_vbo)
for (int m = 0; m < MAX_TILE_LAYERS; m++)
m_mesh[m]->setDirty();
video::SColorf day_color;
get_sunlight_color(&day_color, daynight_ratio);
for(std::map<u32, std::map<u32, video::SColor > >::iterator
for(std::map<std::pair<u8, u32>, std::map<u32, video::SColor > >::iterator
i = m_daynight_diffs.begin();
i != m_daynight_diffs.end(); ++i)
{
scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
scene::IMeshBuffer *buf = m_mesh[i->first.first]->
getMeshBuffer(i->first.second);
video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
for(std::map<u32, video::SColor >::iterator
j = i->second.begin();
j != i->second.end(); ++j)
{
final_color_blend(&(vertices[j->first].Color), j->second, day_color);
final_color_blend(&(vertices[j->first].Color),
j->second, day_color);
}
}
m_last_daynight_ratio = daynight_ratio;
@ -1345,9 +1380,12 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
{
if (camera_offset != m_camera_offset) {
translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
for (u8 layer = 0; layer < 2; layer++) {
translateMesh(m_mesh[layer],
intToFloat(m_camera_offset - camera_offset, BS));
if (m_enable_vbo) {
m_mesh->setDirty();
m_mesh[layer]->setDirty();
}
}
m_camera_offset = camera_offset;
}
@ -1360,16 +1398,30 @@ void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
void MeshCollector::append(const TileSpec &tile,
const video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices)
{
for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
const TileLayer *layer = &tile.layers[layernum];
if (layer->texture_id == 0)
continue;
append(*layer, vertices, numVertices, indices, numIndices,
layernum);
}
}
void MeshCollector::append(const TileLayer &layer,
const video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices, u8 layernum)
{
if (numIndices > 65535) {
dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
return;
}
std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
PreMeshBuffer *p = NULL;
for (u32 i = 0; i < prebuffers.size(); i++) {
PreMeshBuffer &pp = prebuffers[i];
if (pp.tile != tile)
for (u32 i = 0; i < buffers->size(); i++) {
PreMeshBuffer &pp = (*buffers)[i];
if (pp.layer != layer)
continue;
if (pp.indices.size() + numIndices > 65535)
continue;
@ -1380,9 +1432,9 @@ void MeshCollector::append(const TileSpec &tile,
if (p == NULL) {
PreMeshBuffer pp;
pp.tile = tile;
prebuffers.push_back(pp);
p = &prebuffers[prebuffers.size() - 1];
pp.layer = layer;
buffers->push_back(pp);
p = &(*buffers)[buffers->size() - 1];
}
u32 vertex_count;
@ -1416,16 +1468,31 @@ void MeshCollector::append(const TileSpec &tile,
const video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices,
v3f pos, video::SColor c, u8 light_source)
{
for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
const TileLayer *layer = &tile.layers[layernum];
if (layer->texture_id == 0)
continue;
append(*layer, vertices, numVertices, indices, numIndices, pos,
c, light_source, layernum);
}
}
void MeshCollector::append(const TileLayer &layer,
const video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices,
v3f pos, video::SColor c, u8 light_source, u8 layernum)
{
if (numIndices > 65535) {
dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
return;
}
std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
PreMeshBuffer *p = NULL;
for (u32 i = 0; i < prebuffers.size(); i++) {
PreMeshBuffer &pp = prebuffers[i];
if(pp.tile != tile)
for (u32 i = 0; i < buffers->size(); i++) {
PreMeshBuffer &pp = (*buffers)[i];
if(pp.layer != layer)
continue;
if(pp.indices.size() + numIndices > 65535)
continue;
@ -1436,9 +1503,9 @@ void MeshCollector::append(const TileSpec &tile,
if (p == NULL) {
PreMeshBuffer pp;
pp.tile = tile;
prebuffers.push_back(pp);
p = &prebuffers[prebuffers.size() - 1];
pp.layer = layer;
buffers->push_back(pp);
p = &(*buffers)[buffers->size() - 1];
}
video::SColor original_c = c;
@ -1473,14 +1540,49 @@ void MeshCollector::append(const TileSpec &tile,
}
}
video::SColor encode_light_and_color(u16 light, const video::SColor &color,
u8 emissive_light)
void MeshCollector::applyTileColors()
{
if (m_use_tangent_vertices)
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
std::vector<PreMeshBuffer> *p = &prebuffers[layer];
for (std::vector<PreMeshBuffer>::iterator it = p->begin();
it != p->end(); ++it) {
video::SColor tc = it->layer.color;
if (tc == video::SColor(0xFFFFFFFF))
continue;
for (u32 index = 0; index < it->tangent_vertices.size(); index++) {
video::SColor *c = &it->tangent_vertices[index].Color;
c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
c->getGreen() * tc.getGreen() / 255,
c->getBlue() * tc.getBlue() / 255);
}
}
}
else
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
std::vector<PreMeshBuffer> *p = &prebuffers[layer];
for (std::vector<PreMeshBuffer>::iterator it = p->begin();
it != p->end(); ++it) {
video::SColor tc = it->layer.color;
if (tc == video::SColor(0xFFFFFFFF))
continue;
for (u32 index = 0; index < it->vertices.size(); index++) {
video::SColor *c = &it->vertices[index].Color;
c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
c->getGreen() * tc.getGreen() / 255,
c->getBlue() * tc.getBlue() / 255);
}
}
}
}
video::SColor encode_light(u16 light, u8 emissive_light)
{
// Get components
f32 day = (light & 0xff) / 255.0f;
f32 night = (light >> 8) / 255.0f;
u32 day = (light & 0xff);
u32 night = (light >> 8);
// Add emissive light
night += emissive_light * 0.01f;
night += emissive_light * 2.5f;
if (night > 255)
night = 255;
// Since we don't know if the day light is sunlight or
@ -1490,15 +1592,14 @@ video::SColor encode_light_and_color(u16 light, const video::SColor &color,
day = 0;
else
day = day - night;
f32 sum = day + night;
u32 sum = day + night;
// Ratio of sunlight:
float r;
u32 r;
if (sum > 0)
r = day / sum;
r = day * 255 / sum;
else
r = 0;
// Average light:
float b = (day + night) / 2;
return video::SColor(r * 255, b * color.getRed(), b * color.getGreen(),
b * color.getBlue());
return video::SColor(r, b, b, b);
}

@ -108,7 +108,12 @@ public:
scene::IMesh *getMesh()
{
return m_mesh;
return m_mesh[0];
}
scene::IMesh *getMesh(u8 layer)
{
return m_mesh[layer];
}
MinimapMapblock *moveMinimapMapblock()
@ -132,7 +137,7 @@ public:
void updateCameraOffset(v3s16 camera_offset);
private:
scene::IMesh *m_mesh;
scene::IMesh *m_mesh[MAX_TILE_LAYERS];
MinimapMapblock *m_minimap_mapblock;
Client *m_client;
video::IVideoDriver *m_driver;
@ -150,20 +155,23 @@ private:
// Animation info: cracks
// Last crack value passed to animate()
int m_last_crack;
// Maps mesh buffer (i.e. material) indices to base texture names
UNORDERED_MAP<u32, std::string> m_crack_materials;
// Maps mesh and mesh buffer (i.e. material) indices to base texture names
std::map<std::pair<u8, u32>, std::string> m_crack_materials;
// Animation info: texture animationi
// Maps meshbuffers to TileSpecs
UNORDERED_MAP<u32, TileSpec> m_animation_tiles;
UNORDERED_MAP<u32, int> m_animation_frames; // last animation frame
UNORDERED_MAP<u32, int> m_animation_frame_offsets;
// Maps mesh and mesh buffer indices to TileSpecs
// Keys are pairs of (mesh index, buffer index in the mesh)
std::map<std::pair<u8, u32>, TileLayer> m_animation_tiles;
std::map<std::pair<u8, u32>, int> m_animation_frames; // last animation frame
std::map<std::pair<u8, u32>, int> m_animation_frame_offsets;
// Animation info: day/night transitions
// Last daynight_ratio value passed to animate()
u32 m_last_daynight_ratio;
// For each meshbuffer, stores pre-baked colors of sunlit vertices
std::map<u32, std::map<u32, video::SColor > > m_daynight_diffs;
// For each mesh and mesh buffer, stores pre-baked colors
// of sunlit vertices
// Keys are pairs of (mesh index, buffer index in the mesh)
std::map<std::pair<u8, u32>, std::map<u32, video::SColor > > m_daynight_diffs;
// Camera offset info -> do we have to translate the mesh?
v3s16 m_camera_offset;
@ -176,7 +184,7 @@ private:
*/
struct PreMeshBuffer
{
TileSpec tile;
TileLayer layer;
std::vector<u16> indices;
std::vector<video::S3DVertex> vertices;
std::vector<video::S3DVertexTangents> tangent_vertices;
@ -184,7 +192,7 @@ struct PreMeshBuffer
struct MeshCollector
{
std::vector<PreMeshBuffer> prebuffers;
std::vector<PreMeshBuffer> prebuffers[MAX_TILE_LAYERS];
bool m_use_tangent_vertices;
MeshCollector(bool use_tangent_vertices):
@ -195,25 +203,36 @@ 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, 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);
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, 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);

@ -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::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()) {
scene::IMeshBuffer *clone = NULL;
switch (mesh_buffer->getVertexType()) {
case video::EVT_STANDARD: {
video::S3DVertex *v =
(video::S3DVertex *) buf->getVertices();
u16 *indices = (u16*)buf->getIndices();
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, buf->getVertexCount(),
indices, buf->getIndexCount());
dst_mesh->addMeshBuffer(temp_buf);
temp_buf->drop();
temp_buf->append(v, mesh_buffer->getVertexCount(), indices,
mesh_buffer->getIndexCount());
return temp_buf;
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();
(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 *) 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();
(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 *temp_buf = cloneMeshBuffer(
src_mesh->getMeshBuffer(j));
dst_mesh->addMeshBuffer(temp_buf);
temp_buf->drop();
}
return dst_mesh;
}

@ -83,10 +83,15 @@ 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.

@ -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

@ -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);

@ -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,

@ -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;

@ -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

@ -235,27 +235,16 @@ WieldMeshSceneNode::~WieldMeshSceneNode()
g_extrusion_mesh_cache = NULL;
}
void WieldMeshSceneNode::setCube(const TileSpec tiles[6],
void WieldMeshSceneNode::setCube(const ContentFeatures &f,
v3f wield_scale, ITextureSource *tsrc)
{
scene::IMesh *cubemesh = g_extrusion_mesh_cache->createCube();
changeToMesh(cubemesh);
scene::SMesh *copy = cloneMesh(cubemesh);
cubemesh->drop();
postProcessNodeMesh(copy, f, false, true, &m_material_type, &m_colors);
changeToMesh(copy);
copy->drop();
m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR);
// Customize materials
for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) {
assert(i < 6);
video::SMaterial &material = m_meshnode->getMaterial(i);
if (tiles[i].animation_frame_count == 1) {
material.setTexture(0, tiles[i].texture);
} else {
FrameSpec animation_frame = tiles[i].frames[0];
material.setTexture(0, animation_frame.texture);
}
tiles[i].applyMaterialOptions(material);
}
}
void WieldMeshSceneNode::setExtruded(const std::string &imagename,
@ -274,8 +263,10 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename,
dim = core::dimension2d<u32>(dim.Width, frame_height);
}
scene::IMesh *mesh = g_extrusion_mesh_cache->create(dim);
changeToMesh(mesh);
scene::SMesh *copy = cloneMesh(mesh);
mesh->drop();
changeToMesh(copy);
copy->drop();
m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR_EXTRUDED);
@ -321,12 +312,12 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
// Color-related
m_colors.clear();
video::SColor basecolor = idef->getItemstackColor(item, client);
m_base_color = idef->getItemstackColor(item, client);
// If wield_image is defined, it overrides everything else
if (def.wield_image != "") {
setExtruded(def.wield_image, def.wield_scale, tsrc, 1);
m_colors.push_back(basecolor);
m_colors.push_back(ItemPartColor());
return;
}
// Handle nodes
@ -334,66 +325,50 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
else if (def.type == ITEM_NODE) {
if (f.mesh_ptr[0]) {
// e.g. mesh nodes and nodeboxes
changeToMesh(f.mesh_ptr[0]);
// mesh_ptr[0] is pre-scaled by BS * f->visual_scale
scene::SMesh *mesh = cloneMesh(f.mesh_ptr[0]);
postProcessNodeMesh(mesh, f, m_enable_shaders, true,
&m_material_type, &m_colors);
changeToMesh(mesh);
mesh->drop();
// mesh is pre-scaled by BS * f->visual_scale
m_meshnode->setScale(
def.wield_scale * WIELD_SCALE_FACTOR
/ (BS * f.visual_scale));
} else if (f.drawtype == NDT_AIRLIKE) {
changeToMesh(NULL);
} else if (f.drawtype == NDT_PLANTLIKE) {
setExtruded(tsrc->getTextureName(f.tiles[0].texture_id), def.wield_scale, tsrc, f.tiles[0].animation_frame_count);
setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id),
def.wield_scale, tsrc,
f.tiles[0].layers[0].animation_frame_count);
} else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) {
setCube(f.tiles, def.wield_scale, tsrc);
setCube(f, def.wield_scale, tsrc);
} else {
MeshMakeData mesh_make_data(client, false);
MapNode mesh_make_node(id, 255, 0);
mesh_make_data.fillSingleNode(&mesh_make_node);
MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
changeToMesh(mapblock_mesh.getMesh());
translateMesh(m_meshnode->getMesh(), v3f(-BS, -BS, -BS));
scene::SMesh *mesh = cloneMesh(mapblock_mesh.getMesh());
translateMesh(mesh, v3f(-BS, -BS, -BS));
postProcessNodeMesh(mesh, f, m_enable_shaders, true,
&m_material_type, &m_colors);
changeToMesh(mesh);
mesh->drop();
m_meshnode->setScale(
def.wield_scale * WIELD_SCALE_FACTOR
/ (BS * f.visual_scale));
}
u32 material_count = m_meshnode->getMaterialCount();
if (material_count > 6) {
errorstream << "WieldMeshSceneNode::setItem: Invalid material "
"count " << material_count << ", truncating to 6" << std::endl;
material_count = 6;
}
for (u32 i = 0; i < material_count; ++i) {
const TileSpec *tile = &(f.tiles[i]);
video::SMaterial &material = m_meshnode->getMaterial(i);
material.setFlag(video::EMF_BACK_FACE_CULLING, true);
material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter);
material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter);
bool animated = (tile->animation_frame_count > 1);
if (animated) {
FrameSpec animation_frame = tile->frames[0];
material.setTexture(0, animation_frame.texture);
} else {
material.setTexture(0, tile->texture);
}
m_colors.push_back(tile->has_color ? tile->color : basecolor);
material.MaterialType = m_material_type;
if (m_enable_shaders) {
if (tile->normal_texture) {
if (animated) {
FrameSpec animation_frame = tile->frames[0];
material.setTexture(1, animation_frame.normal_texture);
} else {
material.setTexture(1, tile->normal_texture);
}
}
material.setTexture(2, tile->flags_texture);
}
}
return;
}
else if (def.inventory_image != "") {
setExtruded(def.inventory_image, def.wield_scale, tsrc, 1);
m_colors.push_back(basecolor);
m_colors.push_back(ItemPartColor());
return;
}
@ -413,9 +388,9 @@ void WieldMeshSceneNode::setColor(video::SColor c)
u8 blue = c.getBlue();
u32 mc = mesh->getMeshBufferCount();
for (u32 j = 0; j < mc; j++) {
video::SColor bc(0xFFFFFFFF);
if (m_colors.size() > j)
bc = m_colors[j];
video::SColor bc(m_base_color);
if ((m_colors.size() > j) && (m_colors[j].override_base))
bc = m_colors[j].color;
video::SColor buffercolor(255,
bc.getRed() * red / 255,
bc.getGreen() * green / 255,
@ -439,19 +414,7 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
m_meshnode->setMesh(dummymesh);
dummymesh->drop(); // m_meshnode grabbed it
} else {
if (m_lighting) {
m_meshnode->setMesh(mesh);
} else {
/*
Lighting is disabled, this means the caller can (and probably will)
call setColor later. We therefore need to clone the mesh so that
setColor will only modify this scene node's mesh, not others'.
*/
scene::IMeshManipulator *meshmanip = SceneManager->getMeshManipulator();
scene::IMesh *new_mesh = meshmanip->createMeshCopy(mesh);
m_meshnode->setMesh(new_mesh);
new_mesh->drop(); // m_meshnode grabbed it
}
}
m_meshnode->setMaterialFlag(video::EMF_LIGHTING, m_lighting);
@ -475,24 +438,24 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
g_extrusion_mesh_cache->grab();
}
scene::IMesh *mesh;
scene::SMesh *mesh;
// If inventory_image is defined, it overrides everything else
if (def.inventory_image != "") {
mesh = getExtrudedMesh(tsrc, def.inventory_image);
result->mesh = mesh;
result->buffer_colors.push_back(
std::pair<bool, video::SColor>(false, video::SColor(0xFFFFFFFF)));
result->buffer_colors.push_back(ItemPartColor());
} else if (def.type == ITEM_NODE) {
if (f.mesh_ptr[0]) {
mesh = cloneMesh(f.mesh_ptr[0]);
scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
} else if (f.drawtype == NDT_PLANTLIKE) {
mesh = getExtrudedMesh(tsrc,
tsrc->getTextureName(f.tiles[0].texture_id));
tsrc->getTextureName(f.tiles[0].layers[0].texture_id));
} else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES
|| f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
mesh = cloneMesh(g_extrusion_mesh_cache->createCube());
scene::IMesh *cube = g_extrusion_mesh_cache->createCube();
mesh = cloneMesh(cube);
cube->drop();
scaleMesh(mesh, v3f(1.2, 1.2, 1.2));
} else {
MeshMakeData mesh_make_data(client, false);
@ -519,32 +482,27 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
u32 mc = mesh->getMeshBufferCount();
for (u32 i = 0; i < mc; ++i) {
const TileSpec *tile = &(f.tiles[i]);
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
result->buffer_colors.push_back(
std::pair<bool, video::SColor>(tile->has_color, tile->color));
colorizeMeshBuffer(buf, &tile->color);
video::SMaterial &material = buf->getMaterial();
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
material.setFlag(video::EMF_BILINEAR_FILTER, false);
material.setFlag(video::EMF_TRILINEAR_FILTER, false);
material.setFlag(video::EMF_BACK_FACE_CULLING, true);
material.setFlag(video::EMF_LIGHTING, false);
if (tile->animation_frame_count > 1) {
FrameSpec animation_frame = tile->frames[0];
material.setTexture(0, animation_frame.texture);
} else {
material.setTexture(0, tile->texture);
}
}
rotateMeshXZby(mesh, -45);
rotateMeshYZby(mesh, -30);
postProcessNodeMesh(mesh, f, false, false, NULL,
&result->buffer_colors);
}
result->mesh = mesh;
}
}
scene::IMesh * getExtrudedMesh(ITextureSource *tsrc,
scene::SMesh * getExtrudedMesh(ITextureSource *tsrc,
const std::string &imagename)
{
video::ITexture *texture = tsrc->getTextureForMesh(imagename);
@ -553,7 +511,9 @@ scene::IMesh * getExtrudedMesh(ITextureSource *tsrc,
}
core::dimension2d<u32> dim = texture->getSize();
scene::IMesh *mesh = cloneMesh(g_extrusion_mesh_cache->create(dim));
scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
scene::SMesh *mesh = cloneMesh(original);
original->drop();
// Customize material
video::SMaterial &material = mesh->getMeshBuffer(0)->getMaterial();
@ -569,3 +529,57 @@ scene::IMesh * getExtrudedMesh(ITextureSource *tsrc,
return mesh;
}
void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f,
bool use_shaders, bool set_material, video::E_MATERIAL_TYPE *mattype,
std::vector<ItemPartColor> *colors)
{
u32 mc = mesh->getMeshBufferCount();
// Allocate colors for existing buffers
colors->clear();
for (u32 i = 0; i < mc; ++i)
colors->push_back(ItemPartColor());
for (u32 i = 0; i < mc; ++i) {
const TileSpec *tile = &(f.tiles[i]);
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
const TileLayer *layer = &tile->layers[layernum];
if (layer->texture_id == 0)
continue;
if (layernum != 0) {
scene::IMeshBuffer *copy = cloneMeshBuffer(buf);
copy->getMaterial() = buf->getMaterial();
mesh->addMeshBuffer(copy);
copy->drop();
buf = copy;
colors->push_back(
ItemPartColor(layer->has_color, layer->color));
} else {
(*colors)[i] = ItemPartColor(layer->has_color, layer->color);
}
video::SMaterial &material = buf->getMaterial();
if (set_material)
layer->applyMaterialOptions(material);
if (mattype) {
material.MaterialType = *mattype;
}
if (layer->animation_frame_count > 1) {
FrameSpec animation_frame = layer->frames[0];
material.setTexture(0, animation_frame.texture);
} else {
material.setTexture(0, layer->texture);
}
if (use_shaders) {
if (layer->normal_texture) {
if (layer->animation_frame_count > 1) {
FrameSpec animation_frame = layer->frames[0];
material.setTexture(1, animation_frame.normal_texture);
} else
material.setTexture(1, layer->normal_texture);
}
material.setTexture(2, layer->flags_texture);
}
}
}
}

@ -26,17 +26,41 @@ with this program; if not, write to the Free Software Foundation, Inc.,
struct ItemStack;
class Client;
class ITextureSource;
struct TileSpec;
struct ContentFeatures;
/*!
* Holds color information of an item mesh's buffer.
*/
struct ItemPartColor {
/*!
* If this is false, the global base color of the item
* will be used instead of the specific color of the
* buffer.
*/
bool override_base;
/*!
* The color of the buffer.
*/
video::SColor color;
ItemPartColor():
override_base(false),
color(0)
{}
ItemPartColor(bool override, video::SColor color):
override_base(override),
color(color)
{}
};
struct ItemMesh
{
scene::IMesh *mesh;
/*!
* Stores the color of each mesh buffer.
* If the boolean is true, the color is fixed, else
* palettes can modify it.
*/
std::vector<std::pair<bool, video::SColor> > buffer_colors;
std::vector<ItemPartColor> buffer_colors;
ItemMesh() : mesh(NULL), buffer_colors() {}
};
@ -51,7 +75,8 @@ public:
s32 id = -1, bool lighting = false);
virtual ~WieldMeshSceneNode();
void setCube(const TileSpec tiles[6], v3f wield_scale, ITextureSource *tsrc);
void setCube(const ContentFeatures &f, v3f wield_scale,
ITextureSource *tsrc);
void setExtruded(const std::string &imagename, v3f wield_scale,
ITextureSource *tsrc, u8 num_frames);
void setItem(const ItemStack &item, Client *client);
@ -84,7 +109,12 @@ private:
* Stores the colors of the mesh's mesh buffers.
* This does not include lighting.
*/
std::vector<video::SColor> m_colors;
std::vector<ItemPartColor> m_colors;
/*!
* The base color of this mesh. This is the default
* for all mesh buffers.
*/
video::SColor m_base_color;
// Bounding box culling is disabled for this type of scene node,
// so this variable is just required so we can implement
@ -94,5 +124,16 @@ private:
void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result);
scene::IMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename);
scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename);
/*!
* Applies overlays, textures and optionally materials to the given mesh and
* extracts tile colors for colorization.
* \param mattype overrides the buffer's material type, but can also
* be NULL to leave the original material.
* \param colors returns the colors of the mesh buffers in the mesh.
*/
void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f,
bool use_shaders, bool set_material, video::E_MATERIAL_TYPE *mattype,
std::vector<ItemPartColor> *colors);
#endif