Overlays for wield and inventory images (#6107)

* Overlays for wield and inventory images
master
Dániel Juhász 2017-08-25 11:20:53 +00:00 committed by Loïc Blot
parent 6761e21383
commit f6a33a1a7a
6 changed files with 135 additions and 51 deletions

View File

@ -588,7 +588,11 @@ other. This allows different hardware coloring, but also means that
tiles with overlays are drawn slower. Using too much overlays might tiles with overlays are drawn slower. Using too much overlays might
cause FPS loss. cause FPS loss.
To define an overlay, simply set the `overlay_tiles` field of the node For inventory and wield images you can specify overlays which
hardware coloring does not modify. You have to set `inventory_overlay`
and `wield_overlay` fields to an image name.
To define a node overlay, simply set the `overlay_tiles` field of the node
definition. These tiles are defined in the same way as plain tiles: definition. These tiles are defined in the same way as plain tiles:
they can have a texture name, color etc. they can have a texture name, color etc.
To skip one face, set that overlay tile to an empty string. To skip one face, set that overlay tile to an empty string.
@ -4146,7 +4150,9 @@ Definition tables
{bendy = 2, snappy = 1}, {bendy = 2, snappy = 1},
{hard = 1, metal = 1, spikes = 1} {hard = 1, metal = 1, spikes = 1}
inventory_image = "default_tool_steelaxe.png", inventory_image = "default_tool_steelaxe.png",
inventory_overlay = "overlay.png", -- an overlay which does not get colorized
wield_image = "", wield_image = "",
wield_overlay = "",
palette = "", palette = "",
--[[ --[[
^ An image file containing the palette of a node. ^ An image file containing the palette of a node.

View File

@ -67,7 +67,9 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
name = def.name; name = def.name;
description = def.description; description = def.description;
inventory_image = def.inventory_image; inventory_image = def.inventory_image;
inventory_overlay = def.inventory_overlay;
wield_image = def.wield_image; wield_image = def.wield_image;
wield_overlay = def.wield_overlay;
wield_scale = def.wield_scale; wield_scale = def.wield_scale;
stack_max = def.stack_max; stack_max = def.stack_max;
usable = def.usable; usable = def.usable;
@ -105,7 +107,9 @@ void ItemDefinition::reset()
name = ""; name = "";
description = ""; description = "";
inventory_image = ""; inventory_image = "";
inventory_overlay = "";
wield_image = ""; wield_image = "";
wield_overlay = "";
palette_image = ""; palette_image = "";
color = video::SColor(0xFFFFFFFF); color = video::SColor(0xFFFFFFFF);
wield_scale = v3f(1.0, 1.0, 1.0); wield_scale = v3f(1.0, 1.0, 1.0);
@ -159,6 +163,8 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
if (version >= 4) { if (version >= 4) {
writeF1000(os, sound_place.pitch); writeF1000(os, sound_place.pitch);
writeF1000(os, sound_place_failed.pitch); writeF1000(os, sound_place_failed.pitch);
os << serializeString(inventory_overlay);
os << serializeString(wield_overlay);
} }
} }
@ -222,6 +228,8 @@ void ItemDefinition::deSerialize(std::istream &is)
if (version >= 4) { if (version >= 4) {
sound_place.pitch = readF1000(is); sound_place.pitch = readF1000(is);
sound_place_failed.pitch = readF1000(is); sound_place_failed.pitch = readF1000(is);
inventory_overlay = deSerializeString(is);
wield_overlay = deSerializeString(is);
} }
} catch(SerializationError &e) {}; } catch(SerializationError &e) {};
} }

View File

@ -60,7 +60,9 @@ struct ItemDefinition
Visual properties Visual properties
*/ */
std::string inventory_image; // Optional for nodes, mandatory for tools/craftitems std::string inventory_image; // Optional for nodes, mandatory for tools/craftitems
std::string inventory_overlay; // Overlay of inventory_image.
std::string wield_image; // If empty, inventory_image or mesh (only nodes) is used std::string wield_image; // If empty, inventory_image or mesh (only nodes) is used
std::string wield_overlay; // Overlay of wield_image.
std::string palette_image; // If specified, the item will be colorized based on this std::string palette_image; // If specified, the item will be colorized based on this
video::SColor color; // The fallback color of the node. video::SColor color; // The fallback color of the node.
v3f wield_scale; v3f wield_scale;

View File

@ -56,7 +56,9 @@ void read_item_definition(lua_State* L, int index,
getstringfield(L, index, "name", def.name); getstringfield(L, index, "name", def.name);
getstringfield(L, index, "description", def.description); getstringfield(L, index, "description", def.description);
getstringfield(L, index, "inventory_image", def.inventory_image); getstringfield(L, index, "inventory_image", def.inventory_image);
getstringfield(L, index, "inventory_overlay", def.inventory_overlay);
getstringfield(L, index, "wield_image", def.wield_image); getstringfield(L, index, "wield_image", def.wield_image);
getstringfield(L, index, "wield_overlay", def.wield_overlay);
getstringfield(L, index, "palette", def.palette_image); getstringfield(L, index, "palette", def.palette_image);
// Read item color. // Read item color.
@ -142,8 +144,12 @@ void push_item_definition_full(lua_State *L, const ItemDefinition &i)
lua_setfield(L, -2, "type"); lua_setfield(L, -2, "type");
lua_pushstring(L, i.inventory_image.c_str()); lua_pushstring(L, i.inventory_image.c_str());
lua_setfield(L, -2, "inventory_image"); lua_setfield(L, -2, "inventory_image");
lua_pushstring(L, i.inventory_overlay.c_str());
lua_setfield(L, -2, "inventory_overlay");
lua_pushstring(L, i.wield_image.c_str()); lua_pushstring(L, i.wield_image.c_str());
lua_setfield(L, -2, "wield_image"); lua_setfield(L, -2, "wield_image");
lua_pushstring(L, i.wield_overlay.c_str());
lua_setfield(L, -2, "wield_overlay");
lua_pushstring(L, i.palette_image.c_str()); lua_pushstring(L, i.palette_image.c_str());
lua_setfield(L, -2, "palette_image"); lua_setfield(L, -2, "palette_image");
push_ARGB8(L, i.color); push_ARGB8(L, i.color);

View File

@ -240,13 +240,16 @@ void WieldMeshSceneNode::setCube(const ContentFeatures &f,
} }
void WieldMeshSceneNode::setExtruded(const std::string &imagename, void WieldMeshSceneNode::setExtruded(const std::string &imagename,
v3f wield_scale, ITextureSource *tsrc, u8 num_frames) const std::string &overlay_name, v3f wield_scale, ITextureSource *tsrc,
u8 num_frames)
{ {
video::ITexture *texture = tsrc->getTexture(imagename); video::ITexture *texture = tsrc->getTexture(imagename);
if (!texture) { if (!texture) {
changeToMesh(nullptr); changeToMesh(nullptr);
return; return;
} }
video::ITexture *overlay_texture =
overlay_name.empty() ? NULL : tsrc->getTexture(overlay_name);
core::dimension2d<u32> dim = texture->getSize(); core::dimension2d<u32> dim = texture->getSize();
// Detect animation texture and pull off top frame instead of using entire thing // Detect animation texture and pull off top frame instead of using entire thing
@ -254,36 +257,46 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename,
u32 frame_height = dim.Height / num_frames; u32 frame_height = dim.Height / num_frames;
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 *original = g_extrusion_mesh_cache->create(dim);
scene::SMesh *copy = cloneMesh(mesh); scene::SMesh *mesh = cloneMesh(original);
original->drop();
//set texture
mesh->getMeshBuffer(0)->getMaterial().setTexture(0,
tsrc->getTexture(imagename));
if (overlay_texture) {
scene::IMeshBuffer *copy = cloneMeshBuffer(mesh->getMeshBuffer(0));
copy->getMaterial().setTexture(0, overlay_texture);
mesh->addMeshBuffer(copy);
copy->drop();
}
changeToMesh(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);
// Customize material // Customize materials
video::SMaterial &material = m_meshnode->getMaterial(0); for (u32 layer = 0; layer < m_meshnode->getMaterialCount(); layer++) {
material.setTexture(0, tsrc->getTextureForMesh(imagename)); video::SMaterial &material = m_meshnode->getMaterial(layer);
material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE; material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
material.MaterialType = m_material_type; material.MaterialType = m_material_type;
material.setFlag(video::EMF_BACK_FACE_CULLING, true); material.setFlag(video::EMF_BACK_FACE_CULLING, true);
// Enable bi/trilinear filtering only for high resolution textures // Enable bi/trilinear filtering only for high resolution textures
if (dim.Width > 32) { if (dim.Width > 32) {
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);
} else { } else {
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_ANISOTROPIC_FILTER, m_anisotropic_filter); material.setFlag(video::EMF_ANISOTROPIC_FILTER, m_anisotropic_filter);
// mipmaps cause "thin black line" artifacts // mipmaps cause "thin black line" artifacts
#if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2 #if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
material.setFlag(video::EMF_USE_MIP_MAPS, false); material.setFlag(video::EMF_USE_MIP_MAPS, false);
#endif #endif
if (m_enable_shaders) { if (m_enable_shaders) {
material.setTexture(2, tsrc->getShaderFlagsTexture(false)); material.setTexture(2, tsrc->getShaderFlagsTexture(false));
}
} }
} }
@ -308,8 +321,11 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
// If wield_image is defined, it overrides everything else // If wield_image is defined, it overrides everything else
if (!def.wield_image.empty()) { if (!def.wield_image.empty()) {
setExtruded(def.wield_image, def.wield_scale, tsrc, 1); setExtruded(def.wield_image, def.wield_overlay, def.wield_scale, tsrc,
1);
m_colors.emplace_back(); m_colors.emplace_back();
// overlay is white, if present
m_colors.emplace_back(true, video::SColor(0xFFFFFFFF));
return; return;
} }
@ -335,14 +351,23 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
} }
case NDT_PLANTLIKE: { case NDT_PLANTLIKE: {
setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id), setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id),
tsrc->getTextureName(f.tiles[0].layers[1].texture_id),
def.wield_scale, tsrc, def.wield_scale, tsrc,
f.tiles[0].layers[0].animation_frame_count); f.tiles[0].layers[0].animation_frame_count);
// Add color
const TileLayer &l0 = f.tiles[0].layers[0];
m_colors.emplace_back(l0.has_color, l0.color);
const TileLayer &l1 = f.tiles[0].layers[1];
m_colors.emplace_back(l1.has_color, l1.color);
break; break;
} }
case NDT_PLANTLIKE_ROOTED: { case NDT_PLANTLIKE_ROOTED: {
setExtruded(tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), setExtruded(tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id),
def.wield_scale, tsrc, "", def.wield_scale, tsrc,
f.special_tiles[0].layers[0].animation_frame_count); f.special_tiles[0].layers[0].animation_frame_count);
// Add color
const TileLayer &l0 = f.special_tiles[0].layers[0];
m_colors.emplace_back(l0.has_color, l0.color);
break; break;
} }
case NDT_NORMAL: case NDT_NORMAL:
@ -376,10 +401,12 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client)
} }
return; return;
} }
else if (!def.inventory_image.empty()) {
if (!def.inventory_image.empty()) { setExtruded(def.inventory_image, def.inventory_overlay, def.wield_scale,
setExtruded(def.inventory_image, def.wield_scale, tsrc, 1); tsrc, 1);
m_colors.emplace_back(); m_colors.emplace_back();
// overlay is white, if present
m_colors.emplace_back(true, video::SColor(0xFFFFFFFF));
return; return;
} }
@ -456,24 +483,38 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
// If inventory_image is defined, it overrides everything else // If inventory_image is defined, it overrides everything else
if (!def.inventory_image.empty()) { if (!def.inventory_image.empty()) {
mesh = getExtrudedMesh(tsrc, def.inventory_image); mesh = getExtrudedMesh(tsrc, def.inventory_image,
def.inventory_overlay);
result->buffer_colors.emplace_back(); result->buffer_colors.emplace_back();
// overlay is white, if present
result->buffer_colors.emplace_back(true, video::SColor(0xFFFFFFFF));
// Items with inventory images do not need shading // Items with inventory images do not need shading
result->needs_shading = false; result->needs_shading = false;
} 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));
postProcessNodeMesh(mesh, f, false, false, nullptr,
&result->buffer_colors);
} else { } else {
switch (f.drawtype) { switch (f.drawtype) {
case NDT_PLANTLIKE: { case NDT_PLANTLIKE: {
mesh = getExtrudedMesh(tsrc, mesh = getExtrudedMesh(tsrc,
tsrc->getTextureName(f.tiles[0].layers[0].texture_id)); tsrc->getTextureName(f.tiles[0].layers[0].texture_id),
tsrc->getTextureName(f.tiles[0].layers[1].texture_id));
// Add color
const TileLayer &l0 = f.tiles[0].layers[0];
result->buffer_colors.emplace_back(l0.has_color, l0.color);
const TileLayer &l1 = f.tiles[0].layers[1];
result->buffer_colors.emplace_back(l1.has_color, l1.color);
break; break;
} }
case NDT_PLANTLIKE_ROOTED: { case NDT_PLANTLIKE_ROOTED: {
mesh = getExtrudedMesh(tsrc, mesh = getExtrudedMesh(tsrc,
tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id)); tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), "");
// Add color
const TileLayer &l0 = f.special_tiles[0].layers[0];
result->buffer_colors.emplace_back(l0.has_color, l0.color);
break; break;
} }
case NDT_NORMAL: case NDT_NORMAL:
@ -484,6 +525,9 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
mesh = cloneMesh(cube); mesh = cloneMesh(cube);
cube->drop(); cube->drop();
scaleMesh(mesh, v3f(1.2, 1.2, 1.2)); scaleMesh(mesh, v3f(1.2, 1.2, 1.2));
// add overlays
postProcessNodeMesh(mesh, f, false, false, nullptr,
&result->buffer_colors);
break; break;
} }
default: { default: {
@ -507,6 +551,10 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
material1.setTexture(3, material2.getTexture(3)); material1.setTexture(3, material2.getTexture(3));
material1.MaterialType = material2.MaterialType; material1.MaterialType = material2.MaterialType;
} }
// add overlays (since getMesh() returns
// the base layer only)
postProcessNodeMesh(mesh, f, false, false, nullptr,
&result->buffer_colors);
} }
} }
} }
@ -524,36 +572,49 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
rotateMeshXZby(mesh, -45); rotateMeshXZby(mesh, -45);
rotateMeshYZby(mesh, -30); rotateMeshYZby(mesh, -30);
postProcessNodeMesh(mesh, f, false, false, nullptr, &result->buffer_colors);
} }
result->mesh = mesh; result->mesh = mesh;
} }
scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename) scene::SMesh *getExtrudedMesh(ITextureSource *tsrc,
const std::string &imagename, const std::string &overlay_name)
{ {
// check textures
video::ITexture *texture = tsrc->getTextureForMesh(imagename); video::ITexture *texture = tsrc->getTextureForMesh(imagename);
if (!texture) { if (!texture) {
return nullptr; return NULL;
} }
video::ITexture *overlay_texture =
(overlay_name.empty()) ? NULL : tsrc->getTexture(overlay_name);
// get mesh
core::dimension2d<u32> dim = texture->getSize(); core::dimension2d<u32> dim = texture->getSize();
scene::IMesh *original = g_extrusion_mesh_cache->create(dim); scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
scene::SMesh *mesh = cloneMesh(original); scene::SMesh *mesh = cloneMesh(original);
original->drop(); original->drop();
// Customize material //set texture
video::SMaterial &material = mesh->getMeshBuffer(0)->getMaterial(); mesh->getMeshBuffer(0)->getMaterial().setTexture(0,
material.setTexture(0, tsrc->getTexture(imagename)); tsrc->getTexture(imagename));
material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE; if (overlay_texture) {
material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; scene::IMeshBuffer *copy = cloneMeshBuffer(mesh->getMeshBuffer(0));
material.setFlag(video::EMF_BILINEAR_FILTER, false); copy->getMaterial().setTexture(0, overlay_texture);
material.setFlag(video::EMF_TRILINEAR_FILTER, false); mesh->addMeshBuffer(copy);
material.setFlag(video::EMF_BACK_FACE_CULLING, true); copy->drop();
material.setFlag(video::EMF_LIGHTING, false); }
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; // Customize materials
for (u32 layer = 0; layer < mesh->getMeshBufferCount(); layer++) {
video::SMaterial &material = mesh->getMeshBuffer(layer)->getMaterial();
material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
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);
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
}
scaleMesh(mesh, v3f(2.0, 2.0, 2.0)); scaleMesh(mesh, v3f(2.0, 2.0, 2.0));
return mesh; return mesh;

View File

@ -78,8 +78,8 @@ public:
virtual ~WieldMeshSceneNode(); virtual ~WieldMeshSceneNode();
void setCube(const ContentFeatures &f, v3f wield_scale); void setCube(const ContentFeatures &f, v3f wield_scale);
void setExtruded(const std::string &imagename, v3f wield_scale, void setExtruded(const std::string &imagename, const std::string &overlay_image,
ITextureSource *tsrc, u8 num_frames); v3f wield_scale, ITextureSource *tsrc, u8 num_frames);
void setItem(const ItemStack &item, Client *client); void setItem(const ItemStack &item, Client *client);
// Sets the vertex color of the wield mesh. // Sets the vertex color of the wield mesh.
@ -125,7 +125,8 @@ private:
void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result); void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result);
scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename); scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename,
const std::string &overlay_name);
/*! /*!
* Applies overlays, textures and optionally materials to the given mesh and * Applies overlays, textures and optionally materials to the given mesh and