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:
parent
1db5a2f950
commit
a6d4cd7c15
9 changed files with 117 additions and 74 deletions
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
video::SMaterial &material = m_meshnode->getMaterial(i);
|
||||
material.ColorParam = color;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue