diff --git a/doc/lua_api.md b/doc/lua_api.md index 2a4f92ab7..0ca836ba9 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -2271,6 +2271,11 @@ Some of the values in the key-value store are handled specially: See also: `get_description` in [`ItemStack`] * `short_description`: Set the item stack's short description. See also: `get_short_description` in [`ItemStack`] +* `inventory_image`: Override inventory_image +* `inventory_overlay`: Override inventory_overlay +* `wield_image`: Override wield_image +* `wield_overlay`: Override wield_overlay +* `wield_scale`: Override wield_scale, use vector.to_string * `color`: A `ColorString`, which sets the stack's color. * `palette_index`: If the item has a palette, this is used to get the current color from the palette. diff --git a/games/devtest/mods/testitems/init.lua b/games/devtest/mods/testitems/init.lua index fd6d4cfaf..ec080a7c5 100644 --- a/games/devtest/mods/testitems/init.lua +++ b/games/devtest/mods/testitems/init.lua @@ -40,6 +40,7 @@ minetest.register_craftitem("testitems:overlay_meta", { on_place = overlay_on_place, on_secondary_use = overlay_on_place, }) + minetest.register_craftitem("testitems:overlay_global", { description = S("Texture Overlay Test Item, Global Color") .. "\n" .. S("Image must be an orange square with rainbow cross (inventory and wield)"), @@ -51,3 +52,41 @@ minetest.register_craftitem("testitems:overlay_global", { wield_overlay = "testitems_overlay_overlay.png", color = GLOBAL_COLOR_ARG, }) + +minetest.register_craftitem("testitems:image_meta", { + description = S("Image Override Meta Test Item"), + inventory_image = "default_apple.png", + wield_image = "basetools_icesword.png", + + on_use = function(itemstack, player) + local meta = itemstack:get_meta() + local state = meta:get_int("state") + state = (state + 1) % 5 + meta:set_int("state", state) + minetest.chat_send_player(player:get_player_name(), "State " .. state) + + if state == 0 then + meta:set_string("inventory_image", "") + meta:set_string("wield_image", "") + meta:set_string("inventory_overlay", "") + meta:set_string("wield_overlay", "") + meta:set_string("wield_scale", "") + elseif state == 1 then + meta:set_string("inventory_image", "default_tree.png") + meta:set_string("wield_image", "basetools_firesword.png") + elseif state == 2 then + meta:set_string("inventory_image", "default_apple.png^testitems_overridden.png") + meta:set_string("wield_image", "basetools_icesword.png^testitems_overridden.png") + elseif state == 3 then + meta:set_string("inventory_image", "default_tree.png") + meta:set_string("wield_image", "basetools_firesword.png") + meta:set_string("inventory_overlay", "default_apple.png") + meta:set_string("wield_overlay", "default_apple.png") + elseif state == 4 then + local scale = vector.new(0.5, 0.5, 0.5) + meta:set_string("wield_scale", scale:to_string()) + end + + return itemstack + end, +}) diff --git a/games/devtest/mods/testitems/textures/testitems_overridden.png b/games/devtest/mods/testitems/textures/testitems_overridden.png new file mode 100644 index 000000000..5156bf7a6 Binary files /dev/null and b/games/devtest/mods/testitems/textures/testitems_overridden.png differ diff --git a/src/client/game.cpp b/src/client/game.cpp index fde4592b0..5d630a01d 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1840,7 +1840,6 @@ inline bool Game::handleCallbacks() void Game::processQueues() { texture_src->processQueue(); - itemdef_manager->processQueue(client); shader_src->processQueue(); } diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 58f5ec5f1..42b8a2e09 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -1008,10 +1008,14 @@ void drawItemStack( const static thread_local bool enable_animations = g_settings->getBool("inventory_items_animations"); - const ItemDefinition &def = item.getDefinition(client->idef()); + auto *idef = client->idef(); + const ItemDefinition &def = item.getDefinition(idef); bool draw_overlay = false; + const std::string inventory_image = item.getInventoryImage(idef); + const std::string inventory_overlay = item.getInventoryOverlay(idef); + bool has_mesh = false; ItemMesh *imesh; @@ -1020,8 +1024,8 @@ void drawItemStack( viewrect.clipAgainst(*clip); // Render as mesh if animated or no inventory image - if ((enable_animations && rotation_kind < IT_ROT_NONE) || def.inventory_image.empty()) { - imesh = client->idef()->getWieldMesh(def.name, client); + if ((enable_animations && rotation_kind < IT_ROT_NONE) || inventory_image.empty()) { + imesh = idef->getWieldMesh(item, client); has_mesh = imesh && imesh->mesh; } if (has_mesh) { @@ -1110,9 +1114,9 @@ void drawItemStack( driver->setTransform(video::ETS_PROJECTION, oldProjMat); driver->setViewPort(oldViewPort); - draw_overlay = def.type == ITEM_NODE && def.inventory_image.empty(); + draw_overlay = def.type == ITEM_NODE && inventory_image.empty(); } else { // Otherwise just draw as 2D - video::ITexture *texture = client->idef()->getInventoryTexture(def.name, client); + video::ITexture *texture = client->idef()->getInventoryTexture(item, client); video::SColor color; if (texture) { color = client->idef()->getItemstackColor(item, client); @@ -1134,9 +1138,9 @@ void drawItemStack( } // draw the inventory_overlay - if (!def.inventory_overlay.empty() && draw_overlay) { + if (!inventory_overlay.empty() && draw_overlay) { ITextureSource *tsrc = client->getTextureSource(); - video::ITexture *overlay_texture = tsrc->getTexture(def.inventory_overlay); + video::ITexture *overlay_texture = tsrc->getTexture(inventory_overlay); core::dimension2d dimens = overlay_texture->getOriginalSize(); core::rect srcrect(0, 0, dimens.Width, dimens.Height); draw2DImageFilterScaled(driver, overlay_texture, rect, srcrect, clip, 0, true); diff --git a/src/client/wieldmesh.cpp b/src/client/wieldmesh.cpp index 5030ea575..7788273f6 100644 --- a/src/client/wieldmesh.cpp +++ b/src/client/wieldmesh.cpp @@ -384,9 +384,13 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che m_colors.clear(); m_base_color = idef->getItemstackColor(item, client); + const std::string wield_image = item.getWieldImage(idef); + const std::string wield_overlay = item.getWieldOverlay(idef); + const v3f wield_scale = item.getWieldScale(idef); + // If wield_image needs to be checked and is defined, it overrides everything else - if (!def.wield_image.empty() && check_wield_image) { - setExtruded(def.wield_image, def.wield_overlay, def.wield_scale, tsrc, + if (!wield_image.empty() && check_wield_image) { + setExtruded(wield_image, wield_overlay, wield_scale, tsrc, 1); m_colors.emplace_back(); // overlay is white, if present @@ -413,7 +417,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che case NDT_RAILLIKE: case NDT_PLANTLIKE: case NDT_FLOWINGLIQUID: { - v3f wscale = def.wield_scale; + v3f wscale = wield_scale; if (f.drawtype == NDT_FLOWINGLIQUID) wscale.Z *= 0.1f; setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id), @@ -429,7 +433,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che } case NDT_PLANTLIKE_ROOTED: { setExtruded(tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), - "", def.wield_scale, tsrc, + "", wield_scale, tsrc, f.special_tiles[0].layers[0].animation_frame_count); // Add color const TileLayer &l0 = f.special_tiles[0].layers[0]; @@ -439,7 +443,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che case NDT_NORMAL: case NDT_ALLFACES: case NDT_LIQUID: - setCube(f, def.wield_scale); + setCube(f, wield_scale); break; default: { // Render non-trivial drawtypes like the actual node @@ -450,7 +454,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che changeToMesh(mesh); mesh->drop(); m_meshnode->setScale( - def.wield_scale * WIELD_SCALE_FACTOR + wield_scale * WIELD_SCALE_FACTOR / (BS * f.visual_scale)); break; } @@ -471,9 +475,10 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che setColor(video::SColor(0xFFFFFFFF)); return; } else { - if (!def.inventory_image.empty()) { - setExtruded(def.inventory_image, def.inventory_overlay, def.wield_scale, - tsrc, 1); + const std::string inventory_image = item.getInventoryImage(idef); + if (!inventory_image.empty()) { + const std::string inventory_overlay = item.getInventoryOverlay(idef); + setExtruded(inventory_image, inventory_overlay, def.wield_scale, tsrc, 1); } else { setExtruded("no_texture.png", "", def.wield_scale, tsrc, 1); } @@ -578,9 +583,10 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) bool cull_backface = f.needsBackfaceCulling(); // If inventory_image is defined, it overrides everything else - if (!def.inventory_image.empty()) { - mesh = getExtrudedMesh(tsrc, def.inventory_image, - def.inventory_overlay); + const std::string inventory_image = item.getInventoryImage(idef); + const std::string inventory_overlay = item.getInventoryOverlay(idef); + if (!inventory_image.empty()) { + mesh = getExtrudedMesh(tsrc, inventory_image, inventory_overlay); result->buffer_colors.emplace_back(); // overlay is white, if present result->buffer_colors.emplace_back(true, video::SColor(0xFFFFFFFF)); @@ -588,8 +594,7 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) result->needs_shading = false; } else if (def.type == ITEM_NODE && f.drawtype == NDT_AIRLIKE) { // Fallback image for airlike node - mesh = getExtrudedMesh(tsrc, "no_texture_airlike.png", - def.inventory_overlay); + mesh = getExtrudedMesh(tsrc, "no_texture_airlike.png", inventory_overlay); result->needs_shading = false; } else if (def.type == ITEM_NODE) { switch (f.drawtype) { diff --git a/src/inventory.cpp b/src/inventory.cpp index eaf7ba198..0f12ad579 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -272,6 +272,50 @@ std::string ItemStack::getShortDescription(const IItemDefManager *itemdef) const return desc; } +std::string ItemStack::getInventoryImage(const IItemDefManager *itemdef) const +{ + std::string texture = metadata.getString("inventory_image"); + if (texture.empty()) + texture = getDefinition(itemdef).inventory_image; + + return texture; +} + +std::string ItemStack::getInventoryOverlay(const IItemDefManager *itemdef) const +{ + std::string texture = metadata.getString("inventory_overlay"); + if (texture.empty()) + texture = getDefinition(itemdef).inventory_overlay; + + return texture; +} + +std::string ItemStack::getWieldImage(const IItemDefManager *itemdef) const +{ + std::string texture = metadata.getString("wield_image"); + if (texture.empty()) + texture = getDefinition(itemdef).wield_image; + + return texture; +} + +std::string ItemStack::getWieldOverlay(const IItemDefManager *itemdef) const +{ + std::string texture = metadata.getString("wield_overlay"); + if (texture.empty()) + texture = getDefinition(itemdef).wield_overlay; + + return texture; +} + +v3f ItemStack::getWieldScale(const IItemDefManager *itemdef) const +{ + std::string scale = metadata.getString("wield_scale"); + if (scale.empty()) + return getDefinition(itemdef).wield_scale; + + return str_to_v3f(scale); +} ItemStack ItemStack::addItem(ItemStack newitem, IItemDefManager *itemdef) { diff --git a/src/inventory.h b/src/inventory.h index 0f024792a..6d2a54e43 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -51,6 +51,12 @@ struct ItemStack std::string getDescription(const IItemDefManager *itemdef) const; std::string getShortDescription(const IItemDefManager *itemdef) const; + std::string getInventoryImage(const IItemDefManager *itemdef) const; + std::string getInventoryOverlay(const IItemDefManager *itemdef) const; + std::string getWieldImage(const IItemDefManager *itemdef) const; + std::string getWieldOverlay(const IItemDefManager *itemdef) const; + v3f getWieldScale(const IItemDefManager *itemdef) const; + /* Quantity methods */ diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 4d6950835..571b17a02 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -253,6 +253,13 @@ class CItemDefManager: public IWritableItemDefManager inventory_texture(NULL), palette(NULL) {} + + ~ClientCached() { + if (wield_mesh.mesh) + wield_mesh.mesh->drop(); + } + + DISABLE_CLASS_COPY(ClientCached); }; #endif @@ -265,17 +272,9 @@ public: #endif clear(); } + virtual ~CItemDefManager() { -#ifndef SERVER - const std::vector &values = m_clientcached.getValues(); - for (ClientCached *cc : values) { - if (cc->wield_mesh.mesh) - cc->wield_mesh.mesh->drop(); - delete cc; - } - -#endif for (auto &item_definition : m_item_definitions) { delete item_definition.second; } @@ -319,103 +318,72 @@ public: } #ifndef SERVER public: - ClientCached* createClientCachedDirect(const std::string &name, - Client *client) const + ClientCached* createClientCachedDirect(const ItemStack &item, Client *client) const { - infostream<<"Lazily creating item texture and mesh for \"" - <second.get(); ITextureSource *tsrc = client->getTextureSource(); - const ItemDefinition &def = get(name); // Create new ClientCached - cc = new ClientCached(); + auto cc = std::make_unique(); // Create an inventory texture cc->inventory_texture = NULL; - if (!def.inventory_image.empty()) - cc->inventory_texture = tsrc->getTexture(def.inventory_image); - - ItemStack item = ItemStack(); - item.name = def.name; - + if (!inventory_image.empty()) + cc->inventory_texture = tsrc->getTexture(inventory_image); getItemMesh(client, item, &(cc->wield_mesh)); cc->palette = tsrc->getPalette(def.palette_image); // Put in cache - m_clientcached.set(name, cc); - - return cc; + ClientCached *ptr = cc.get(); + m_clientcached[cache_key] = std::move(cc); + return ptr; } - ClientCached* getClientCached(const std::string &name, - Client *client) const - { - ClientCached *cc = NULL; - m_clientcached.get(name, &cc); - if (cc) - return cc; - if (std::this_thread::get_id() == m_main_thread) { - return createClientCachedDirect(name, client); - } - - // We're gonna ask the result to be put into here - static ResultQueue result_queue; - - // Throw a request in - m_get_clientcached_queue.add(name, 0, 0, &result_queue); - try { - while(true) { - // Wait result for a second - GetResult - result = result_queue.pop_front(1000); - - if (result.key == name) { - return result.item; - } - } - } catch(ItemNotFoundException &e) { - errorstream << "Waiting for clientcached " << name - << " timed out." << std::endl; - return &m_dummy_clientcached; - } - } // Get item inventory texture - virtual video::ITexture* getInventoryTexture(const std::string &name, + virtual video::ITexture* getInventoryTexture(const ItemStack &item, Client *client) const { - ClientCached *cc = getClientCached(name, client); - if(!cc) - return NULL; + ClientCached *cc = createClientCachedDirect(item, client); + if (!cc) + return nullptr; return cc->inventory_texture; } + // Get item wield mesh - virtual ItemMesh* getWieldMesh(const std::string &name, - Client *client) const + virtual ItemMesh* getWieldMesh(const ItemStack &item, Client *client) const { - ClientCached *cc = getClientCached(name, client); - if(!cc) - return NULL; + ClientCached *cc = createClientCachedDirect(item, client); + if (!cc) + return nullptr; return &(cc->wield_mesh); } // Get item palette - virtual Palette* getPalette(const std::string &name, - Client *client) const + virtual Palette* getPalette(const ItemStack &item, Client *client) const { - ClientCached *cc = getClientCached(name, client); - if(!cc) - return NULL; + ClientCached *cc = createClientCachedDirect(item, client); + if (!cc) + return nullptr; return cc->palette; } @@ -428,7 +396,7 @@ public: if (!colorstring.empty() && parseColorString(colorstring, directcolor, true)) return directcolor; // See if there is a palette - Palette *palette = getPalette(stack.name, client); + Palette *palette = getPalette(stack, client); const std::string &index = stack.metadata.getString("palette_index", 0); if (palette && !index.empty()) return (*palette)[mystoi(index, 0, 255)]; @@ -573,20 +541,7 @@ public: registerAlias(name, convert_to); } } - void processQueue(IGameDef *gamedef) - { -#ifndef SERVER - //NOTE this is only thread safe for ONE consumer thread! - while(!m_get_clientcached_queue.empty()) - { - GetRequest - request = m_get_clientcached_queue.pop(); - m_get_clientcached_queue.pushResult(request, - createClientCachedDirect(request.key, (Client *)gamedef)); - } -#endif - } private: // Key is name std::map m_item_definitions; @@ -595,12 +550,8 @@ private: #ifndef SERVER // The id of the thread that is allowed to use irrlicht directly std::thread::id m_main_thread; - // A reference to this can be returned when nothing is found, to avoid NULLs - mutable ClientCached m_dummy_clientcached; // Cached textures and meshes - mutable MutexedMap m_clientcached; - // Queued clientcached fetches (to be processed by the main thread) - mutable RequestQueue m_get_clientcached_queue; + mutable std::unordered_map> m_clientcached; #endif }; diff --git a/src/itemdef.h b/src/itemdef.h index 3bb27559e..556e96b4f 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -120,14 +120,16 @@ public: virtual bool isKnown(const std::string &name) const=0; #ifndef SERVER // Get item inventory texture - virtual video::ITexture* getInventoryTexture(const std::string &name, - Client *client) const=0; - // Get item wield mesh - virtual ItemMesh* getWieldMesh(const std::string &name, - Client *client) const=0; + virtual video::ITexture* getInventoryTexture(const ItemStack &item, Client *client) const=0; + + /** + * Get wield mesh + * + * Returns nullptr if there is an inventory image + */ + virtual ItemMesh* getWieldMesh(const ItemStack &item, Client *client) const = 0; // Get item palette - virtual Palette* getPalette(const std::string &name, - Client *client) const = 0; + virtual Palette* getPalette(const ItemStack &item, Client *client) const = 0; // Returns the base color of an item stack: the color of all // tiles that do not define their own color. virtual video::SColor getItemstackColor(const ItemStack &stack, @@ -144,23 +146,6 @@ public: virtual ~IWritableItemDefManager() = default; - // Get item definition - virtual const ItemDefinition& get(const std::string &name) const=0; - // Get alias definition - virtual const std::string &getAlias(const std::string &name) const=0; - // Get set of all defined item names and aliases - virtual void getAll(std::set &result) const=0; - // Check if item is known - virtual bool isKnown(const std::string &name) const=0; -#ifndef SERVER - // Get item inventory texture - virtual video::ITexture* getInventoryTexture(const std::string &name, - Client *client) const=0; - // Get item wield mesh - virtual ItemMesh* getWieldMesh(const std::string &name, - Client *client) const=0; -#endif - // Replace the textures of registered nodes with the ones specified in // the texture pack's override.txt files virtual void applyTextureOverrides(const std::vector &overrides)=0; @@ -177,11 +162,7 @@ public: virtual void registerAlias(const std::string &name, const std::string &convert_to)=0; - virtual void serialize(std::ostream &os, u16 protocol_version)=0; virtual void deSerialize(std::istream &is, u16 protocol_version)=0; - - // Do stuff asked by threads that can only be done in the main thread - virtual void processQueue(IGameDef *gamedef)=0; }; IWritableItemDefManager* createItemDefManager(); diff --git a/src/settings.cpp b/src/settings.cpp index a0225bc2c..b537005aa 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -562,13 +562,7 @@ v2f Settings::getV2F(const std::string &name) const v3f Settings::getV3F(const std::string &name) const { - v3f value; - Strfnd f(get(name)); - f.next("("); - value.X = stof(f.next(",")); - value.Y = stof(f.next(",")); - value.Z = stof(f.next(")")); - return value; + return str_to_v3f(get(name)); } diff --git a/src/util/string.cpp b/src/util/string.cpp index 5076a69ed..5c120926c 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "hex.h" #include "porting.h" #include "translation.h" +#include "strfnd.h" #include #include @@ -897,3 +898,15 @@ void safe_print_string(std::ostream &os, const std::string &str) } os.setf(flags); } + + +v3f str_to_v3f(const std::string &str) +{ + v3f value; + Strfnd f(str); + f.next("("); + value.X = stof(f.next(",")); + value.Y = stof(f.next(",")); + value.Z = stof(f.next(")")); + return value; +} diff --git a/src/util/string.h b/src/util/string.h index f2396af46..11d32b1ac 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -765,3 +765,11 @@ std::string sanitizeDirName(const std::string &str, const std::string &optional_ * brackets (e.g. "a\x1eb" -> "a<1e>b"). */ void safe_print_string(std::ostream &os, const std::string &str); + +/** + * Parses a string of form `(1, 2, 3)` to a v3f + * + * @param str String + * @return + */ +v3f str_to_v3f(const std::string &str);