From f6a33a1a7a298cb7d3fb18818bae97bd1b89d633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= Date: Fri, 25 Aug 2017 11:20:53 +0000 Subject: [PATCH] Overlays for wield and inventory images (#6107) * Overlays for wield and inventory images --- doc/lua_api.txt | 8 +- src/itemdef.cpp | 8 ++ src/itemdef.h | 2 + src/script/common/c_content.cpp | 6 ++ src/wieldmesh.cpp | 155 ++++++++++++++++++++++---------- src/wieldmesh.h | 7 +- 6 files changed, 135 insertions(+), 51 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 20c3e5a92..5b071d626 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -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 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: they can have a texture name, color etc. To skip one face, set that overlay tile to an empty string. @@ -4146,7 +4150,9 @@ Definition tables {bendy = 2, snappy = 1}, {hard = 1, metal = 1, spikes = 1} inventory_image = "default_tool_steelaxe.png", + inventory_overlay = "overlay.png", -- an overlay which does not get colorized wield_image = "", + wield_overlay = "", palette = "", --[[ ^ An image file containing the palette of a node. diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 5ad976450..3d8116bf6 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -67,7 +67,9 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def) name = def.name; description = def.description; inventory_image = def.inventory_image; + inventory_overlay = def.inventory_overlay; wield_image = def.wield_image; + wield_overlay = def.wield_overlay; wield_scale = def.wield_scale; stack_max = def.stack_max; usable = def.usable; @@ -105,7 +107,9 @@ void ItemDefinition::reset() name = ""; description = ""; inventory_image = ""; + inventory_overlay = ""; wield_image = ""; + wield_overlay = ""; palette_image = ""; color = video::SColor(0xFFFFFFFF); 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) { writeF1000(os, sound_place.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) { sound_place.pitch = readF1000(is); sound_place_failed.pitch = readF1000(is); + inventory_overlay = deSerializeString(is); + wield_overlay = deSerializeString(is); } } catch(SerializationError &e) {}; } diff --git a/src/itemdef.h b/src/itemdef.h index bca922d3a..45cff582a 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -60,7 +60,9 @@ struct ItemDefinition Visual properties */ 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_overlay; // Overlay of wield_image. std::string palette_image; // If specified, the item will be colorized based on this video::SColor color; // The fallback color of the node. v3f wield_scale; diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 4360f63bb..f746ce766 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -56,7 +56,9 @@ void read_item_definition(lua_State* L, int index, getstringfield(L, index, "name", def.name); getstringfield(L, index, "description", def.description); 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_overlay", def.wield_overlay); getstringfield(L, index, "palette", def.palette_image); // Read item color. @@ -142,8 +144,12 @@ void push_item_definition_full(lua_State *L, const ItemDefinition &i) lua_setfield(L, -2, "type"); lua_pushstring(L, i.inventory_image.c_str()); 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_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_setfield(L, -2, "palette_image"); push_ARGB8(L, i.color); diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp index df23dc433..0394cc9ea 100644 --- a/src/wieldmesh.cpp +++ b/src/wieldmesh.cpp @@ -240,13 +240,16 @@ void WieldMeshSceneNode::setCube(const ContentFeatures &f, } 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); if (!texture) { changeToMesh(nullptr); return; } + video::ITexture *overlay_texture = + overlay_name.empty() ? NULL : tsrc->getTexture(overlay_name); core::dimension2d dim = texture->getSize(); // 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; dim = core::dimension2d(dim.Width, frame_height); } - scene::IMesh *mesh = g_extrusion_mesh_cache->create(dim); - scene::SMesh *copy = cloneMesh(mesh); + scene::IMesh *original = g_extrusion_mesh_cache->create(dim); + 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(); - changeToMesh(copy); - copy->drop(); m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR_EXTRUDED); - // Customize material - video::SMaterial &material = m_meshnode->getMaterial(0); - material.setTexture(0, tsrc->getTextureForMesh(imagename)); - material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE; - material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; - material.MaterialType = m_material_type; - material.setFlag(video::EMF_BACK_FACE_CULLING, true); - // Enable bi/trilinear filtering only for high resolution textures - if (dim.Width > 32) { - material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter); - material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter); - } else { - material.setFlag(video::EMF_BILINEAR_FILTER, false); - material.setFlag(video::EMF_TRILINEAR_FILTER, false); - } - material.setFlag(video::EMF_ANISOTROPIC_FILTER, m_anisotropic_filter); - // mipmaps cause "thin black line" artifacts + // Customize materials + for (u32 layer = 0; layer < m_meshnode->getMaterialCount(); layer++) { + video::SMaterial &material = m_meshnode->getMaterial(layer); + material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE; + material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; + material.MaterialType = m_material_type; + material.setFlag(video::EMF_BACK_FACE_CULLING, true); + // Enable bi/trilinear filtering only for high resolution textures + if (dim.Width > 32) { + material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter); + material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter); + } else { + material.setFlag(video::EMF_BILINEAR_FILTER, false); + material.setFlag(video::EMF_TRILINEAR_FILTER, false); + } + material.setFlag(video::EMF_ANISOTROPIC_FILTER, m_anisotropic_filter); + // mipmaps cause "thin black line" artifacts #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 - if (m_enable_shaders) { - material.setTexture(2, tsrc->getShaderFlagsTexture(false)); + if (m_enable_shaders) { + 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 (!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(); + // overlay is white, if present + m_colors.emplace_back(true, video::SColor(0xFFFFFFFF)); return; } @@ -335,14 +351,23 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) } case NDT_PLANTLIKE: { setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id), + tsrc->getTextureName(f.tiles[0].layers[1].texture_id), def.wield_scale, tsrc, 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; } case NDT_PLANTLIKE_ROOTED: { 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); + // Add color + const TileLayer &l0 = f.special_tiles[0].layers[0]; + m_colors.emplace_back(l0.has_color, l0.color); break; } case NDT_NORMAL: @@ -376,10 +401,12 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) } return; } - - if (!def.inventory_image.empty()) { - setExtruded(def.inventory_image, def.wield_scale, tsrc, 1); + else if (!def.inventory_image.empty()) { + setExtruded(def.inventory_image, def.inventory_overlay, def.wield_scale, + tsrc, 1); m_colors.emplace_back(); + // overlay is white, if present + m_colors.emplace_back(true, video::SColor(0xFFFFFFFF)); return; } @@ -456,24 +483,38 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) // If inventory_image is defined, it overrides everything else 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(); + // overlay is white, if present + result->buffer_colors.emplace_back(true, video::SColor(0xFFFFFFFF)); // Items with inventory images do not need shading result->needs_shading = false; } 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)); + postProcessNodeMesh(mesh, f, false, false, nullptr, + &result->buffer_colors); } else { switch (f.drawtype) { case NDT_PLANTLIKE: { 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; } case NDT_PLANTLIKE_ROOTED: { 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; } case NDT_NORMAL: @@ -484,6 +525,9 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) mesh = cloneMesh(cube); cube->drop(); scaleMesh(mesh, v3f(1.2, 1.2, 1.2)); + // add overlays + postProcessNodeMesh(mesh, f, false, false, nullptr, + &result->buffer_colors); break; } default: { @@ -507,6 +551,10 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) material1.setTexture(3, material2.getTexture(3)); 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); rotateMeshYZby(mesh, -30); - - postProcessNodeMesh(mesh, f, false, false, nullptr, &result->buffer_colors); } 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); if (!texture) { - return nullptr; + return NULL; } + video::ITexture *overlay_texture = + (overlay_name.empty()) ? NULL : tsrc->getTexture(overlay_name); + // get mesh core::dimension2d dim = texture->getSize(); 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(); - material.setTexture(0, tsrc->getTexture(imagename)); - 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; + //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(); + } + // 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)); return mesh; diff --git a/src/wieldmesh.h b/src/wieldmesh.h index 828625075..23e3abf29 100644 --- a/src/wieldmesh.h +++ b/src/wieldmesh.h @@ -78,8 +78,8 @@ public: virtual ~WieldMeshSceneNode(); void setCube(const ContentFeatures &f, v3f wield_scale); - void setExtruded(const std::string &imagename, v3f wield_scale, - ITextureSource *tsrc, u8 num_frames); + void setExtruded(const std::string &imagename, const std::string &overlay_image, + v3f wield_scale, ITextureSource *tsrc, u8 num_frames); void setItem(const ItemStack &item, Client *client); // Sets the vertex color of the wield mesh. @@ -125,7 +125,8 @@ private: 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