Skip to content

Commit

Permalink
Soft node overlay (#5186)
Browse files Browse the repository at this point in the history
This commit adds node overlays, which are tiles that are drawn on top of
other tiles.
  • Loading branch information
juhdanad authored and Zeno- committed Apr 21, 2017
1 parent 2ad74a9 commit 1ffb180
Show file tree
Hide file tree
Showing 18 changed files with 763 additions and 467 deletions.
6 changes: 6 additions & 0 deletions doc/lua_api.txt
Expand Up @@ -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 ]]
Expand Down
16 changes: 10 additions & 6 deletions src/client.cpp
Expand Up @@ -487,13 +487,17 @@ void Client::step(float dtime)
minimap_mapblock = r.mesh->moveMinimapMapblock();
if (minimap_mapblock == NULL)
do_mapper_update = false;
}

if (r.mesh && r.mesh->getMesh()->getMeshBufferCount() == 0) {
delete r.mesh;
} else {
// Replace with the new mesh
block->mesh = r.mesh;
bool is_empty = true;
for (int l = 0; l < MAX_TILE_LAYERS; l++)
if (r.mesh->getMesh(l)->getMeshBufferCount() != 0)
is_empty = false;

if (is_empty)
delete r.mesh;
else
// Replace with the new mesh
block->mesh = r.mesh;
}
} else {
delete r.mesh;
Expand Down
96 changes: 63 additions & 33 deletions src/client/tile.h
Expand Up @@ -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),
Expand All @@ -217,49 +220,41 @@ struct TileSpec
}

/*!
* Two tiles are equal if they can be appended to
* the same mesh buffer.
* Two layers are equal if they can be merged.
*/
bool operator==(const TileSpec &other) const
bool operator==(const TileLayer &other) const
{
return (
return
texture_id == other.texture_id &&
material_type == other.material_type &&
material_flags == other.material_flags &&
rotation == other.rotation
);
color == other.color;
}

/*!
* Two tiles are not equal if they must be in different mesh buffers.
* Two tiles are not equal if they must have different vertices.
*/
bool operator!=(const TileSpec &other) const
bool operator!=(const TileLayer &other) const
{
return !(*this == other);
}

// Sets everything else except the texture in the material
void applyMaterialOptions(video::SMaterial &material) const
{
switch (material_type) {
case TILE_MATERIAL_BASIC:
case TILE_MATERIAL_WAVING_LEAVES:
case TILE_MATERIAL_WAVING_PLANTS:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
case TILE_MATERIAL_ALPHA:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_LIQUID_TRANSPARENT:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_LIQUID_OPAQUE:
material.MaterialType = video::EMT_SOLID;
break;
case TILE_MATERIAL_WAVING_LEAVES:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
case TILE_MATERIAL_WAVING_PLANTS:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
}
material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING)
? true : false;
Expand All @@ -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;
Expand All @@ -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
56 changes: 35 additions & 21 deletions src/clientmap.cpp
Expand Up @@ -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;
};
Expand All @@ -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();
Expand All @@ -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);
Expand Down Expand Up @@ -434,29 +443,34 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);

scene::IMesh *mesh = mapBlockMesh->getMesh();
assert(mesh);
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
scene::IMesh *mesh = mapBlockMesh->getMesh(layer);
assert(mesh);

u32 c = mesh->getMeshBufferCount();
for (u32 i = 0; i < c; i++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
u32 c = mesh->getMeshBufferCount();
for (u32 i = 0; i < c; i++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);

video::SMaterial& material = buf->getMaterial();
video::IMaterialRenderer* rnd =
video::SMaterial& material = buf->getMaterial();
video::IMaterialRenderer* rnd =
driver->getMaterialRenderer(material.MaterialType);
bool transparent = (rnd && rnd->isTransparent());
if (transparent == is_transparent_pass) {
if (buf->getVertexCount() == 0)
errorstream << "Block [" << analyze_block(block)
<< "] contains an empty meshbuf" << std::endl;

material.setFlag(video::EMF_TRILINEAR_FILTER, m_cache_trilinear_filter);
material.setFlag(video::EMF_BILINEAR_FILTER, m_cache_bilinear_filter);
material.setFlag(video::EMF_ANISOTROPIC_FILTER, m_cache_anistropic_filter);
material.setFlag(video::EMF_WIREFRAME, m_control.show_wireframe);

drawbufs.add(buf);
bool transparent = (rnd && rnd->isTransparent());
if (transparent == is_transparent_pass) {
if (buf->getVertexCount() == 0)
errorstream << "Block [" << analyze_block(block)
<< "] contains an empty meshbuf" << std::endl;

material.setFlag(video::EMF_TRILINEAR_FILTER,
m_cache_trilinear_filter);
material.setFlag(video::EMF_BILINEAR_FILTER,
m_cache_bilinear_filter);
material.setFlag(video::EMF_ANISOTROPIC_FILTER,
m_cache_anistropic_filter);
material.setFlag(video::EMF_WIREFRAME,
m_control.show_wireframe);

drawbufs.add(buf, layer);
}
}
}
}
Expand Down

1 comment on commit 1ffb180

@Fixer-007
Copy link
Contributor

@Fixer-007 Fixer-007 commented on 1ffb180 Apr 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/src/wieldmesh.cpp: In function ‘void getItemMesh(Client*, const ItemStack&, ItemMesh*)’:
/src/wieldmesh.cpp:500:21: warning: ‘mesh’ may be used uninitialized in this function [-Wmaybe-uninitialized]
  result->mesh = mesh;
                     ^

And probably introduced this bug #5631

Please sign in to comment.