1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00

Draw node animation for items (#15930)

This commit is contained in:
cx384 2025-04-04 18:47:11 +02:00 committed by GitHub
parent 1db5a2f950
commit a6d4cd7c15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 117 additions and 74 deletions

View file

@ -134,7 +134,8 @@ void Camera::step(f32 dtime)
if (m_wield_change_timer >= 0 && was_under_zero) {
m_wieldnode->setItem(m_wield_item_next, m_client);
m_wieldnode->setNodeLightColor(m_player_light_color);
m_wieldnode->setLightColorAndAnimation(m_player_light_color,
m_client->getAnimationTime());
}
if (m_view_bobbing_state != 0)
@ -537,7 +538,8 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio)
m_wieldnode->setRotation(wield_rotation);
m_player_light_color = player->light_color;
m_wieldnode->setNodeLightColor(m_player_light_color);
m_wieldnode->setLightColorAndAnimation(m_player_light_color,
m_client->getAnimationTime());
// Set render distance
updateViewingRange();

View file

@ -809,7 +809,8 @@ void GenericCAO::setNodeLight(const video::SColor &light_color)
{
if (m_prop.visual == OBJECTVISUAL_WIELDITEM || m_prop.visual == OBJECTVISUAL_ITEM) {
if (m_wield_meshnode)
m_wield_meshnode->setNodeLightColor(light_color);
m_wield_meshnode->setLightColorAndAnimation(light_color,
m_client->getAnimationTime());
return;
}

View file

@ -678,9 +678,7 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data):
// - Texture animation
if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
// Add to MapBlockMesh in order to animate these tiles
auto &info = m_animation_info[{layer, i}];
info.tile = p.layer;
info.frame = 0;
m_animation_info.emplace(std::make_pair(layer, i), AnimationInfo(p.layer));
// Replace tile texture with the first animation frame
p.layer.texture = (*p.layer.frames)[0].texture;
}
@ -763,6 +761,12 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack,
// Cracks
if (crack != m_last_crack) {
for (auto &crack_material : m_crack_materials) {
// TODO crack on animated tiles does not work
auto anim_it = m_animation_info.find(crack_material.first);
if (anim_it != m_animation_info.end())
continue;
scene::IMeshBuffer *buf = m_mesh[crack_material.first.first]->
getMeshBuffer(crack_material.first.second);
@ -772,16 +776,6 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack,
video::ITexture *new_texture =
m_tsrc->getTextureForMesh(s, &new_texture_id);
buf->getMaterial().setTexture(0, new_texture);
// If the current material is also animated, update animation info
auto anim_it = m_animation_info.find(crack_material.first);
if (anim_it != m_animation_info.end()) {
TileLayer &tile = anim_it->second.tile;
tile.texture = new_texture;
tile.texture_id = new_texture_id;
// force animation update
anim_it->second.frame = -1;
}
}
m_last_crack = crack;
@ -789,20 +783,9 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack,
// Texture animation
for (auto &it : m_animation_info) {
const TileLayer &tile = it.second.tile;
// Figure out current frame
int frameno = (int)(time * 1000 / tile.animation_frame_length_ms) %
tile.animation_frame_count;
// If frame doesn't change, skip
if (frameno == it.second.frame)
continue;
it.second.frame = frameno;
scene::IMeshBuffer *buf = m_mesh[it.first.first]->getMeshBuffer(it.first.second);
const FrameSpec &frame = (*tile.frames)[frameno];
buf->getMaterial().setTexture(0, frame.texture);
video::SMaterial &material = buf->getMaterial();
it.second.updateTexture(material, time);
}
return true;

View file

@ -246,10 +246,6 @@ public:
}
private:
struct AnimationInfo {
int frame; // last animation frame
TileLayer tile;
};
irr_ptr<scene::IMesh> m_mesh[MAX_TILE_LAYERS];
std::vector<MinimapMapblock*> m_minimap_mapblocks;

View file

@ -3,6 +3,19 @@
// Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
#include "tile.h"
#include <cassert>
void AnimationInfo::updateTexture(video::SMaterial &material, float animation_time)
{
// Figure out current frame
u16 frame = (u16)(animation_time * 1000 / m_frame_length_ms) % m_frame_count;
// Only adjust if frame changed
if (frame != m_frame) {
m_frame = frame;
assert(m_frame < m_frames->size());
material.setTexture(0, (*m_frames)[m_frame].texture);
}
};
void TileLayer::applyMaterialOptions(video::SMaterial &material, int layer) const
{

View file

@ -151,6 +151,28 @@ struct TileLayer
bool has_color = false;
};
// Stores information for drawing an animated tile
struct AnimationInfo {
AnimationInfo() = default;
AnimationInfo(const TileLayer &tile) :
m_frame_length_ms(tile.animation_frame_length_ms),
m_frame_count(tile.animation_frame_count),
m_frames(tile.frames)
{};
void updateTexture(video::SMaterial &material, float animation_time);
private:
u16 m_frame = 0; // last animation frame
u16 m_frame_length_ms = 0;
u16 m_frame_count = 1;
/// @note not owned by this struct
std::vector<FrameSpec> *m_frames = nullptr;
};
enum class TileRotation: u8 {
None,
R90,

View file

@ -30,6 +30,14 @@
#define MIN_EXTRUSION_MESH_RESOLUTION 16
#define MAX_EXTRUSION_MESH_RESOLUTION 512
ItemMeshBufferInfo::ItemMeshBufferInfo(const TileLayer &layer) :
override_color(layer.color),
override_color_set(layer.has_color),
animation_info((layer.material_flags & MATERIAL_FLAG_ANIMATION) ?
std::make_unique<AnimationInfo>(layer) :
nullptr)
{}
static scene::IMesh *createExtrusionMesh(int resolution_x, int resolution_y)
{
const f32 r = 0.5;
@ -285,7 +293,7 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename,
}
static scene::SMesh *createGenericNodeMesh(Client *client, MapNode n,
std::vector<ItemPartColor> *colors, const ContentFeatures &f)
std::vector<ItemMeshBufferInfo> *buffer_info, const ContentFeatures &f)
{
n.setParam1(0xff);
if (n.getParam2()) {
@ -309,7 +317,7 @@ static scene::SMesh *createGenericNodeMesh(Client *client, MapNode n,
MapblockMeshGenerator(&mmd, &collector).generate();
}
colors->clear();
buffer_info->clear();
scene::SMesh *mesh = new scene::SMesh();
for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
auto &prebuffers = collector.prebuffers[layer];
@ -329,7 +337,7 @@ static scene::SMesh *createGenericNodeMesh(Client *client, MapNode n,
p.layer.applyMaterialOptions(buf->Material, layer);
mesh->addMeshBuffer(buf.get());
colors->emplace_back(p.layer.has_color, p.layer.color);
buffer_info->emplace_back(p.layer);
}
}
mesh->recalculateBoundingBox();
@ -352,7 +360,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
m_material_type = shdrsrc->getShaderInfo(shader_id).material;
// Color-related
m_colors.clear();
m_buffer_info.clear();
m_base_color = idef->getItemstackColor(item, client);
const std::string wield_image = item.getWieldImage(idef);
@ -361,11 +369,10 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
// If wield_image needs to be checked and is defined, it overrides everything else
if (!wield_image.empty() && check_wield_image) {
setExtruded(wield_image, wield_overlay, wield_scale, tsrc,
1);
m_colors.emplace_back();
setExtruded(wield_image, wield_overlay, wield_scale, tsrc, 1);
m_buffer_info.emplace_back();
// overlay is white, if present
m_colors.emplace_back(true, video::SColor(0xFFFFFFFF));
m_buffer_info.emplace_back(true, video::SColor(0xFFFFFFFF));
// initialize the color
setColor(video::SColor(0xFFFFFFFF));
return;
@ -394,8 +401,8 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
wscale, tsrc,
l0.animation_frame_count);
// Add color
m_colors.emplace_back(l0.has_color, l0.color);
m_colors.emplace_back(l1.has_color, l1.color);
m_buffer_info.emplace_back(l0.has_color, l0.color);
m_buffer_info.emplace_back(l1.has_color, l1.color);
break;
}
case NDT_PLANTLIKE_ROOTED: {
@ -404,7 +411,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
setExtruded(tsrc->getTextureName(l0.texture_id),
"", wield_scale, tsrc,
l0.animation_frame_count);
m_colors.emplace_back(l0.has_color, l0.color);
m_buffer_info.emplace_back(l0.has_color, l0.color);
break;
}
default: {
@ -413,7 +420,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
if (def.place_param2)
n.setParam2(*def.place_param2);
mesh = createGenericNodeMesh(client, n, &m_colors, f);
mesh = createGenericNodeMesh(client, n, &m_buffer_info, f);
changeToMesh(mesh);
mesh->drop();
m_meshnode->setScale(
@ -447,9 +454,9 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
setExtruded("no_texture.png", "", def.wield_scale, tsrc, 1);
}
m_colors.emplace_back();
m_buffer_info.emplace_back();
// overlay is white, if present
m_colors.emplace_back(true, video::SColor(0xFFFFFFFF));
m_buffer_info.emplace_back(true, video::SColor(0xFFFFFFFF));
// initialize the color
setColor(video::SColor(0xFFFFFFFF));
@ -471,33 +478,38 @@ void WieldMeshSceneNode::setColor(video::SColor c)
u8 blue = c.getBlue();
const u32 mc = mesh->getMeshBufferCount();
if (mc > m_colors.size())
m_colors.resize(mc);
if (mc > m_buffer_info.size())
m_buffer_info.resize(mc);
for (u32 j = 0; j < mc; j++) {
video::SColor bc(m_base_color);
m_colors[j].applyOverride(bc);
m_buffer_info[j].applyOverride(bc);
video::SColor buffercolor(255,
bc.getRed() * red / 255,
bc.getGreen() * green / 255,
bc.getBlue() * blue / 255);
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
if (m_colors[j].needColorize(buffercolor)) {
if (m_buffer_info[j].needColorize(buffercolor)) {
buf->setDirty(scene::EBT_VERTEX);
setMeshBufferColor(buf, buffercolor);
}
}
}
void WieldMeshSceneNode::setNodeLightColor(video::SColor color)
void WieldMeshSceneNode::setLightColorAndAnimation(video::SColor color, float animation_time)
{
if (!m_meshnode)
return;
{
for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) {
// Color
video::SMaterial &material = m_meshnode->getMaterial(i);
material.ColorParam = color;
// Animation
const ItemMeshBufferInfo &buf_info = m_buffer_info[i];
if (buf_info.animation_info) {
buf_info.animation_info->updateTexture(material, animation_time);
}
}
}
@ -544,9 +556,9 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
const std::string inventory_overlay = item.getInventoryOverlay(idef);
if (!inventory_image.empty()) {
mesh = getExtrudedMesh(tsrc, inventory_image, inventory_overlay);
result->buffer_colors.emplace_back();
result->buffer_info.emplace_back();
// overlay is white, if present
result->buffer_colors.emplace_back(true, video::SColor(0xFFFFFFFF));
result->buffer_info.emplace_back(true, video::SColor(0xFFFFFFFF));
// Items with inventory images do not need shading
result->needs_shading = false;
} else if (def.type == ITEM_NODE && f.drawtype == NDT_AIRLIKE) {
@ -562,8 +574,8 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
tsrc->getTextureName(l0.texture_id),
tsrc->getTextureName(l1.texture_id));
// Add color
result->buffer_colors.emplace_back(l0.has_color, l0.color);
result->buffer_colors.emplace_back(l1.has_color, l1.color);
result->buffer_info.emplace_back(l0.has_color, l0.color);
result->buffer_info.emplace_back(l1.has_color, l1.color);
break;
}
case NDT_PLANTLIKE_ROOTED: {
@ -571,7 +583,7 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
const TileLayer &l0 = f.special_tiles[0].layers[0];
mesh = getExtrudedMesh(tsrc,
tsrc->getTextureName(l0.texture_id), "");
result->buffer_colors.emplace_back(l0.has_color, l0.color);
result->buffer_info.emplace_back(l0.has_color, l0.color);
break;
}
default: {
@ -580,7 +592,7 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
if (def.place_param2)
n.setParam2(*def.place_param2);
mesh = createGenericNodeMesh(client, n, &result->buffer_colors, f);
mesh = createGenericNodeMesh(client, n, &result->buffer_info, f);
scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
break;
}

View file

@ -11,6 +11,8 @@
#include <EMaterialTypes.h>
#include <IMeshSceneNode.h>
#include <SColor.h>
#include <memory>
#include "tile.h"
namespace irr::scene
{
@ -28,9 +30,10 @@ struct ContentFeatures;
class ShadowRenderer;
/*
* Holds color information of an item mesh's buffer.
* Holds information of an item mesh's buffer.
* Used for coloring and animation.
*/
class ItemPartColor
class ItemMeshBufferInfo
{
/*
* Optional color that overrides the global base color.
@ -47,12 +50,14 @@ class ItemPartColor
public:
ItemPartColor() = default;
ItemMeshBufferInfo() = default;
ItemPartColor(bool override, video::SColor color) :
ItemMeshBufferInfo(bool override, video::SColor color) :
override_color(color), override_color_set(override)
{}
ItemMeshBufferInfo(const TileLayer &layer);
void applyOverride(video::SColor &dest) const {
if (override_color_set)
dest = override_color;
@ -65,15 +70,18 @@ public:
last_colorized = target;
return true;
}
// Null for no animated parts
std::unique_ptr<AnimationInfo> animation_info;
};
struct ItemMesh
{
scene::IMesh *mesh = nullptr;
/*
* Stores the color of each mesh buffer.
* Stores draw information of each mesh buffer.
*/
std::vector<ItemPartColor> buffer_colors;
std::vector<ItemMeshBufferInfo> buffer_info;
/*
* If false, all faces of the item should have the same brightness.
* Disables shading based on normal vectors.
@ -101,7 +109,7 @@ public:
// Must only be used if the constructor was called with lighting = false
void setColor(video::SColor color);
void setNodeLightColor(video::SColor color);
void setLightColorAndAnimation(video::SColor color, float animation_time);
scene::IMesh *getMesh() { return m_meshnode->getMesh(); }
@ -120,10 +128,10 @@ private:
bool m_bilinear_filter;
bool m_trilinear_filter;
/*!
* Stores the colors of the mesh's mesh buffers.
* Stores the colors and animation data of the mesh's mesh buffers.
* This does not include lighting.
*/
std::vector<ItemPartColor> m_colors;
std::vector<ItemMeshBufferInfo> m_buffer_info;
/*!
* The base color of this mesh. This is the default
* for all mesh buffers.

View file

@ -118,13 +118,13 @@ void drawItemStack(
client->idef()->getItemstackColor(item, client);
const u32 mc = mesh->getMeshBufferCount();
if (mc > imesh->buffer_colors.size())
imesh->buffer_colors.resize(mc);
if (mc > imesh->buffer_info.size())
imesh->buffer_info.resize(mc);
for (u32 j = 0; j < mc; ++j) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
video::SColor c = basecolor;
auto &p = imesh->buffer_colors[j];
auto &p = imesh->buffer_info[j];
p.applyOverride(c);
// TODO: could be moved to a shader
@ -137,6 +137,12 @@ void drawItemStack(
}
video::SMaterial &material = buf->getMaterial();
// Texture animation
if (p.animation_info) {
p.animation_info->updateTexture(material, client->getAnimationTime());
}
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
driver->setMaterial(material);
driver->drawMeshBuffer(buf);