Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions builtin/game/features.lua
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ core.features = {
particlespawner_exclude_player = true,
generate_decorations_biomes = true,
chunksize_vector = true,
item_inventory_image_animation = true,
}

function core.has_feature(arg)
Expand Down
1 change: 1 addition & 0 deletions builtin/game/misc_s.lua
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ core.protocol_versions = {
["5.12.0"] = 48,
["5.13.0"] = 49,
["5.14.0"] = 50,
["5.15.0"] = 51,
}

setmetatable(core.protocol_versions, {__newindex = function()
Expand Down
33 changes: 22 additions & 11 deletions doc/lua_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2705,10 +2705,10 @@ Some of the values in the key-value store are handled specially:
See also: `get_description` in [`ItemStack`](#itemstack)
* `short_description`: Set the item stack's short description.
See also: `get_short_description` in [`ItemStack`](#itemstack)
* `inventory_image`: Override inventory_image
* `inventory_overlay`: Override inventory_overlay
* `wield_image`: Override wield_image
* `wield_overlay`: Override wield_overlay
* `inventory_image`: Override inventory_image.name
* `inventory_overlay`: Override inventory_overlay.name
* `wield_image`: Override wield_image.name
* `wield_overlay`: Override wield_overlay.name
* `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
Expand Down Expand Up @@ -5851,6 +5851,9 @@ Utilities
generate_decorations_biomes = true,
-- 'chunksize' mapgen setting can be a vector, instead of a single number (5.15.0)
chunksize_vector = true,
-- Item definition fields `inventory_image`, `inventory_overlay`, `wield_image`
-- and `wield_overlay` accept a table containing animation definitions. (5.16.0)
item_image_animation = true,
}
```

Expand Down Expand Up @@ -9515,6 +9518,7 @@ Player properties need to be saved manually.
-- `core.itemstring_with_palette()`), the entity will inherit the color.
-- Wielditems are scaled a bit. If you want a wielditem to appear
-- to be as large as a node, use `0.667` in `visual_size`
-- Currently, item image animations are not played. This may change in the future.
-- "item" is similar to "wielditem" but ignores the 'wield_image' parameter.
-- "node" looks exactly like a node in-world (supported since 5.12.0)
-- Note that visual effects like waving or liquid reflections will not work.
Expand Down Expand Up @@ -9855,6 +9859,13 @@ Tile animation definition
}
```

Item image definition
---------------------

* `"image.png"`
* `{name="image.png", animation={Tile Animation definition}}`
* Basically a tile definition but for items

Item definition
---------------

Expand All @@ -9881,18 +9892,18 @@ Used by `core.register_node`, `core.register_craftitem`, and
-- {bendy = 2, snappy = 1},
-- {hard = 1, metal = 1, spikes = 1}

inventory_image = "",
-- Texture shown in the inventory GUI
inventory_image = <Item image definition>,
-- Image shown in the inventory GUI
-- Defaults to a 3D rendering of the node if left empty.

inventory_overlay = "",
-- An overlay texture which is not affected by colorization
inventory_overlay = <Item image definition>,
-- An overlay image which is not affected by colorization

wield_image = "",
-- Texture shown when item is held in hand
wield_image = <Item image definition>,
-- Image shown when item is held in hand
-- Defaults to a 3D rendering of the node if left empty.

wield_overlay = "",
wield_overlay = <Item image definition>,
-- Like inventory_overlay but only used in the same situation as wield_image

wield_scale = {x = 1, y = 1, z = 1},
Expand Down
56 changes: 56 additions & 0 deletions games/devtest/mods/testitems/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,59 @@ core.register_craftitem("testitems:tree_spawner_vmanip", {
core.register_on_leaveplayer(function(player, timed_out)
vmanip_for_trees[player:get_player_name()] = nil
end)

-- Animation test items

local animated_image = {
name = "testnodes_anim.png^[invert:rgb",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 4.0,
}
}

local animated_overlay = {
name = "testitems_animation_overlay.png",
animation = {
type = "sheet_2d",
frames_w = 1,
frames_h = 4,
frame_length = 1.0,
}
}

core.register_craftitem("testitems:inventory_image_animation", {
description = S("Animated Test Item").."\n"..
S("Image animate from A to D in 4s cycle"),
inventory_image = animated_image
})

core.register_craftitem("testitems:inventory_image_animation_overlay", {
description = S("Animated Test Item With Overlay").."\n"..
S("Should be colored red and have green stripes that move").."\n"..
S("Image animate from A to D in 4s cycle"),
inventory_image = animated_image,
inventory_overlay = animated_overlay,
color = "#ff0000",
})

core.register_craftitem("testitems:wield_image_animation", {
description = S("Wield Animated Test Item").."\n"..
S("Looks like the Animated Test Item, "..
"but only animated for the wield mesh."),
inventory_image = "testnodes_anim.png^[invert:rgb^[verticalframe:4:0",
wield_image = animated_image,
})

core.register_craftitem("testitems:wield_image_animation_overlay", {
description = S("Wield Animated Test Item With Overlay").."\n"..
S("Looks like the animated Test Item With Overlay, "..
"but only animated for the wield mesh."),
inventory_image = "testnodes_anim.png^[invert:rgb^[verticalframe:4:0",
inventory_overlay = "testitems_animation_overlay.png^[verticalframe:4:0",
wield_image = animated_image,
wield_overlay = animated_overlay,
color = "#ff0000",
})
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
142 changes: 117 additions & 25 deletions src/client/item_visuals_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,28 @@
#include "itemdef.h"
#include "inventory.h"

ItemVisualsManager::ItemVisuals::~ItemVisuals() {
if (wield_mesh.mesh)
wield_mesh.mesh->drop();
}
struct ItemVisualsManager::ItemVisuals
{
ItemMesh item_mesh;
Palette *palette;

AnimationInfo inventory_normal;
AnimationInfo inventory_overlay;

ItemVisuals() :
palette(nullptr)
{}

~ItemVisuals()
{
inventory_normal.freeFrames();
inventory_overlay.freeFrames();
if (item_mesh.mesh)
item_mesh.mesh->drop();
}

DISABLE_CLASS_COPY(ItemVisuals);
};

ItemVisualsManager::ItemVisuals *ItemVisualsManager::createItemVisuals( const ItemStack &item,
Client *client) const
Expand All @@ -24,13 +42,18 @@ ItemVisualsManager::ItemVisuals *ItemVisualsManager::createItemVisuals( const It
IItemDefManager *idef = client->idef();

const ItemDefinition &def = item.getDefinition(idef);
std::string inventory_image = item.getInventoryImage(idef);
std::string inventory_overlay = item.getInventoryOverlay(idef);
std::string cache_key = def.name;
if (!inventory_image.empty())
cache_key += "/" + inventory_image;
if (!inventory_overlay.empty())
cache_key += ":" + inventory_overlay;
ItemImageDef inventory_image = item.getInventoryImage(idef);
ItemImageDef inventory_overlay = item.getInventoryOverlay(idef);

// Key only consists of item name + image name,
// because animation currently cannot be overridden by meta
std::ostringstream os(def.name);
if (!inventory_image.name.empty())
os << "/" << inventory_image.name;
if (!inventory_overlay.name.empty())
os << ":" << inventory_overlay.name;
std::string cache_key = os.str();


// Skip if already in cache
auto it = m_cached_item_visuals.find(cache_key);
Expand All @@ -43,36 +66,105 @@ ItemVisualsManager::ItemVisuals *ItemVisualsManager::createItemVisuals( const It
ITextureSource *tsrc = client->getTextureSource();

// Create new ItemVisuals
auto cc = std::make_unique<ItemVisuals>();

cc->inventory_texture = NULL;
if (!inventory_image.empty())
cc->inventory_texture = tsrc->getTexture(inventory_image);
getItemMesh(client, item, &(cc->wield_mesh));

cc->palette = tsrc->getPalette(def.palette_image);
auto iv = std::make_unique<ItemVisuals>();

auto populate_texture_and_animation = [tsrc](
const ItemImageDef &image,
AnimationInfo &animation)
{
int frame_length_ms = 0;
auto frames = std::make_unique<std::vector<FrameSpec>>();
if (image.name.empty()) {
// no-op
} else if (image.animation.type == TileAnimationType::TAT_NONE) {
frames->push_back({0, tsrc->getTexture(image.name)});
} else {
// Animated
// Get inventory texture frames
*frames = createAnimationFrames(tsrc, image.name, image.animation, frame_length_ms);
}
animation = AnimationInfo(frames.release(), frame_length_ms);
// `frames` are freed in `ItemVisuals::~ItemVisuals`
};

populate_texture_and_animation(inventory_image, iv->inventory_normal);
populate_texture_and_animation(inventory_overlay, iv->inventory_overlay);

createItemMesh(client, def,
iv->inventory_normal,
iv->inventory_overlay,
&(iv->item_mesh));

iv->palette = tsrc->getPalette(def.palette_image);

// Put in cache
ItemVisuals *ptr = cc.get();
m_cached_item_visuals[cache_key] = std::move(cc);
ItemVisuals *ptr = iv.get();
m_cached_item_visuals[cache_key] = std::move(iv);
return ptr;
}

video::ITexture* ItemVisualsManager::getInventoryTexture(const ItemStack &item,
// Needed because `ItemVisuals` is not known in the header.
ItemVisualsManager::ItemVisualsManager()
{
m_main_thread = std::this_thread::get_id();
}

ItemVisualsManager::~ItemVisualsManager()
{
}

void ItemVisualsManager::clear()
{
m_cached_item_visuals.clear();
}


video::ITexture *ItemVisualsManager::getInventoryTexture(const ItemStack &item,
Client *client) const
{
ItemVisuals *iv = createItemVisuals(item, client);
if (!iv)
return nullptr;
return iv->inventory_texture;

// Texture animation update (if >1 frame)
return iv->inventory_normal.getTexture(client->getAnimationTime());
}

ItemMesh* ItemVisualsManager::getWieldMesh(const ItemStack &item, Client *client) const
video::ITexture *ItemVisualsManager::getInventoryOverlayTexture(const ItemStack &item,
Client *client) const
{
ItemVisuals *iv = createItemVisuals(item, client);
if (!iv)
return nullptr;
return &(iv->wield_mesh);

// Texture animation update (if >1 frame)
return iv->inventory_overlay.getTexture(client->getAnimationTime());
}

ItemMesh *ItemVisualsManager::getItemMesh(const ItemStack &item, Client *client) const
{
ItemVisuals *iv = createItemVisuals(item, client);
return iv ? &(iv->item_mesh) : nullptr;
}

AnimationInfo *ItemVisualsManager::getInventoryAnimation(const ItemStack &item,
Client *client) const
{
ItemVisuals *iv = createItemVisuals(item, client);
if (!iv || iv->inventory_normal.getFrameCount() <= 1)
return nullptr;
return &iv->inventory_normal;
}

// Get item inventory overlay animation
// returns nullptr if it is not animated
AnimationInfo *ItemVisualsManager::getInventoryOverlayAnimation(const ItemStack &item,
Client *client) const
{
ItemVisuals *iv = createItemVisuals(item, client);
if (!iv || iv->inventory_overlay.getFrameCount() <= 1)
return nullptr;
return &iv->inventory_overlay;
}

Palette* ItemVisualsManager::getPalette(const ItemStack &item, Client *client) const
Expand Down
42 changes: 18 additions & 24 deletions src/client/item_visuals_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,29 @@ namespace video { class ITexture; }

struct ItemVisualsManager
{
ItemVisualsManager()
{
m_main_thread = std::this_thread::get_id();
}
ItemVisualsManager();
~ItemVisualsManager();

void clear() {
m_cached_item_visuals.clear();
}
/// Clears the cached visuals
void clear();

// Get item inventory texture
video::ITexture* getInventoryTexture(const ItemStack &item, Client *client) const;

// Get item wield mesh
// Get item inventory overlay texture
video::ITexture* getInventoryOverlayTexture(const ItemStack &item, Client *client) const;

// Get item inventory animation
// returns nullptr if it is not animated
AnimationInfo *getInventoryAnimation(const ItemStack &item, Client *client) const;

// Get item inventory overlay animation
// returns nullptr if it is not animated
AnimationInfo *getInventoryOverlayAnimation(const ItemStack &item, Client *client) const;

// Get item mesh
// Once said to return nullptr if there is an inventory image, but this is wrong
ItemMesh* getWieldMesh(const ItemStack &item, Client *client) const;
ItemMesh *getItemMesh(const ItemStack &item, Client *client) const;

// Get item palette
Palette* getPalette(const ItemStack &item, Client *client) const;
Expand All @@ -44,21 +52,7 @@ struct ItemVisualsManager
video::SColor getItemstackColor(const ItemStack &stack, Client *client) const;

private:
struct ItemVisuals
{
video::ITexture *inventory_texture;
ItemMesh wield_mesh;
Palette *palette;

ItemVisuals():
inventory_texture(nullptr),
palette(nullptr)
{}

~ItemVisuals();

DISABLE_CLASS_COPY(ItemVisuals);
};
struct ItemVisuals;

// The id of the thread that is allowed to use irrlicht directly
std::thread::id m_main_thread;
Expand Down
Loading
Loading