Fixes and improvements for item image animations (#16620)

This commit is contained in:
cx384 2025-10-29 10:26:59 +01:00 committed by GitHub
parent 97c9f8f709
commit d4d3e10531
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 37 additions and 43 deletions

View File

@ -5855,7 +5855,7 @@ Utilities
-- 'chunksize' mapgen setting can be a vector, instead of a single number (5.15.0) -- 'chunksize' mapgen setting can be a vector, instead of a single number (5.15.0)
chunksize_vector = true, chunksize_vector = true,
-- Item definition fields `inventory_image`, `inventory_overlay`, `wield_image` -- Item definition fields `inventory_image`, `inventory_overlay`, `wield_image`
-- and `wield_overlay` accept a table containing animation definitions. (5.16.0) -- and `wield_overlay` accept a table containing animation definitions. (5.15.0)
item_image_animation = true, item_image_animation = true,
} }
``` ```

View File

@ -18,14 +18,16 @@ struct ItemVisualsManager::ItemVisuals
AnimationInfo inventory_normal; AnimationInfo inventory_normal;
AnimationInfo inventory_overlay; AnimationInfo inventory_overlay;
// ItemVisuals owns the frames and AnimationInfo points to them
std::vector<FrameSpec> frames_normal;
std::vector<FrameSpec> frames_overlay;
ItemVisuals() : ItemVisuals() :
palette(nullptr) palette(nullptr)
{} {}
~ItemVisuals() ~ItemVisuals()
{ {
inventory_normal.freeFrames();
inventory_overlay.freeFrames();
if (item_mesh.mesh) if (item_mesh.mesh)
item_mesh.mesh->drop(); item_mesh.mesh->drop();
} }
@ -65,30 +67,18 @@ ItemVisualsManager::ItemVisuals *ItemVisualsManager::createItemVisuals( const It
ITextureSource *tsrc = client->getTextureSource(); ITextureSource *tsrc = client->getTextureSource();
// Create new ItemVisuals
auto iv = std::make_unique<ItemVisuals>(); auto iv = std::make_unique<ItemVisuals>();
auto populate_texture_and_animation = [tsrc]( // Create inventory image textures
const ItemImageDef &image, int frame_length = 0;
AnimationInfo &animation) iv->frames_normal = createAnimationFrames(tsrc, inventory_image.name,
{ inventory_image.animation, frame_length);
int frame_length_ms = 0; iv->inventory_normal = AnimationInfo(&iv->frames_normal, frame_length);
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); // Create inventory overlay textures
populate_texture_and_animation(inventory_overlay, iv->inventory_overlay); iv->frames_overlay = createAnimationFrames(tsrc, inventory_overlay.name,
inventory_overlay.animation, frame_length);
iv->inventory_overlay = AnimationInfo(&iv->frames_overlay, frame_length);
createItemMesh(client, def, createItemMesh(client, def,
iv->inventory_normal, iv->inventory_normal,

View File

@ -41,7 +41,6 @@ struct ItemVisualsManager
AnimationInfo *getInventoryOverlayAnimation(const ItemStack &item, Client *client) const; AnimationInfo *getInventoryOverlayAnimation(const ItemStack &item, Client *client) const;
// Get item mesh // Get item mesh
// Once said to return nullptr if there is an inventory image, but this is wrong
ItemMesh *getItemMesh(const ItemStack &item, Client *client) const; ItemMesh *getItemMesh(const ItemStack &item, Client *client) const;
// Get item palette // Get item palette

View File

@ -5,7 +5,7 @@
#include "tile.h" #include "tile.h"
#include <cassert> #include <cassert>
video::ITexture *AnimationInfo::getTexture(float animation_time) video::ITexture *AnimationInfo::getTexture(float animation_time) const
{ {
if (getFrameCount() == 0) if (getFrameCount() == 0)
return nullptr; return nullptr;

View File

@ -156,19 +156,13 @@ struct AnimationInfo {
m_frame_length_ms(tile.animation_frame_length_ms), m_frame_length_ms(tile.animation_frame_length_ms),
m_frame_count(tile.animation_frame_count), m_frame_count(tile.animation_frame_count),
m_frames(tile.frames) m_frames(tile.frames)
{}; {}
AnimationInfo(std::vector<FrameSpec> *frames, u16 frame_length_ms) : AnimationInfo(std::vector<FrameSpec> *frames, u16 frame_length_ms) :
m_frame_length_ms(frame_length_ms), m_frame_length_ms(frame_length_ms),
m_frame_count(frames->size()), m_frame_count(frames->size()),
m_frames(frames) m_frames(frames)
{}; {}
void freeFrames()
{
delete m_frames;
m_frames = nullptr;
}
size_t getFrameCount() const size_t getFrameCount() const
{ {
@ -178,14 +172,13 @@ struct AnimationInfo {
void updateTexture(video::SMaterial &material, float animation_time); void updateTexture(video::SMaterial &material, float animation_time);
// Returns nullptr if texture did not change since last time // Returns nullptr if texture did not change since last time
video::ITexture *getTexture(float animation_time); video::ITexture *getTexture(float animation_time) const;
private: private:
u16 m_frame_length_ms = 0; u16 m_frame_length_ms = 0;
u16 m_frame_count = 1; u16 m_frame_count = 1;
/// @note by default not owned by this struct /// @note by default not owned by this struct
/// TODO. Change this to a shared pointer.
std::vector<FrameSpec> *m_frames = nullptr; std::vector<FrameSpec> *m_frames = nullptr;
}; };

View File

@ -415,9 +415,16 @@ std::vector<FrameSpec> createAnimationFrames(ITextureSource *tsrc,
{ {
result_frame_length_ms = 0; result_frame_length_ms = 0;
if (image_name.empty() || animation.type == TileAnimationType::TAT_NONE) if (image_name.empty())
return {}; return {};
// Still create texture if not animated
if (animation.type == TileAnimationType::TAT_NONE) {
u32 id;
video::ITexture *texture = tsrc->getTextureForMesh(image_name, &id);
return {{id, texture}};
}
video::ITexture *orginal_texture = tsrc->getTexture(image_name); video::ITexture *orginal_texture = tsrc->getTexture(image_name);
if (!orginal_texture) if (!orginal_texture)
return {}; return {};
@ -656,8 +663,8 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh)
} }
void createItemMesh(Client *client, const ItemDefinition &def, void createItemMesh(Client *client, const ItemDefinition &def,
AnimationInfo &animation_normal, const AnimationInfo &animation_normal,
AnimationInfo &animation_overlay, const AnimationInfo &animation_overlay,
ItemMesh *result) ItemMesh *result)
{ {
ITextureSource *tsrc = client->getTextureSource(); ITextureSource *tsrc = client->getTextureSource();
@ -700,7 +707,9 @@ void createItemMesh(Client *client, const ItemDefinition &def,
case NDT_PLANTLIKE: { case NDT_PLANTLIKE: {
const TileLayer &l0 = f.tiles[0].layers[0]; const TileLayer &l0 = f.tiles[0].layers[0];
const TileLayer &l1 = f.tiles[0].layers[1]; const TileLayer &l1 = f.tiles[0].layers[1];
mesh = getExtrudedMesh(l0.texture, l1.texture); mesh = getExtrudedMesh(
extractTexture(f.tiledef[0], l0, tsrc),
extractTexture(f.tiledef[1], l1, tsrc));
// Add color // Add color
result->buffer_info.emplace_back(0, l0); result->buffer_info.emplace_back(0, l0);
result->buffer_info.emplace_back(1, l1); result->buffer_info.emplace_back(1, l1);
@ -709,7 +718,9 @@ void createItemMesh(Client *client, const ItemDefinition &def,
case NDT_PLANTLIKE_ROOTED: { case NDT_PLANTLIKE_ROOTED: {
// Use the plant tile // Use the plant tile
const TileLayer &l0 = f.special_tiles[0].layers[0]; const TileLayer &l0 = f.special_tiles[0].layers[0];
mesh = getExtrudedMesh(l0.texture); mesh = getExtrudedMesh(
extractTexture(f.tiledef_special[0], l0, tsrc)
);
result->buffer_info.emplace_back(0, l0); result->buffer_info.emplace_back(0, l0);
break; break;
} }

View File

@ -187,6 +187,6 @@ scene::SMesh *getExtrudedMesh(video::ITexture *texture,
// This is only used to initially generate an ItemMesh // This is only used to initially generate an ItemMesh
// To get the mesh, use ItemVisualsManager::getItemMesh(item, client) instead // To get the mesh, use ItemVisualsManager::getItemMesh(item, client) instead
void createItemMesh(Client *client, const ItemDefinition &def, void createItemMesh(Client *client, const ItemDefinition &def,
AnimationInfo &animation_normal, const AnimationInfo &animation_normal,
AnimationInfo &animation_overlay, const AnimationInfo &animation_overlay,
ItemMesh *result); ItemMesh *result);

View File

@ -45,5 +45,6 @@ struct TileAnimationParams
// Modifies the texture name such that it only contains the first frame // Modifies the texture name such that it only contains the first frame
// If the texture_size is know (client code), getTextureModifer should be used instead // If the texture_size is know (client code), getTextureModifer should be used instead
// This function only exists for compatibility with old clients
void extractFirstFrame(std::string &name) const; void extractFirstFrame(std::string &name) const;
}; };