Browse Source

Overlays for wield and inventory images (#6107)

* Overlays for wield and inventory images
Dániel Juhász 6 years ago
parent
commit
f6a33a1a7a
6 changed files with 135 additions and 51 deletions
  1. 7 1
      doc/lua_api.txt
  2. 8 0
      src/itemdef.cpp
  3. 2 0
      src/itemdef.h
  4. 6 0
      src/script/common/c_content.cpp
  5. 108 47
      src/wieldmesh.cpp
  6. 4 3
      src/wieldmesh.h

+ 7 - 1
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.

+ 8 - 0
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) {};
 }

+ 2 - 0
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;

+ 6 - 0
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);

+ 108 - 47
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<u32> 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<u32>(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<u32> 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;

+ 4 - 3
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