diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 3f8b4d51f..dfa2649ed 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -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(); diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 30429f80e..624ed4b5b 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -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; } diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 35ac65de4..a53a5c073 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -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; diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index 5e69b9329..6d8ddc36c 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -246,10 +246,6 @@ public: } private: - struct AnimationInfo { - int frame; // last animation frame - TileLayer tile; - }; irr_ptr m_mesh[MAX_TILE_LAYERS]; std::vector m_minimap_mapblocks; diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 84281f84d..894e97339 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -3,6 +3,19 @@ // Copyright (C) 2010-2013 celeron55, Perttu Ahola #include "tile.h" +#include + +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 { diff --git a/src/client/tile.h b/src/client/tile.h index 420f0757f..ffbe78bac 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -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 *m_frames = nullptr; +}; + enum class TileRotation: u8 { None, R90, diff --git a/src/client/wieldmesh.cpp b/src/client/wieldmesh.cpp index 9e8d72cf2..dbba0de2a 100644 --- a/src/client/wieldmesh.cpp +++ b/src/client/wieldmesh.cpp @@ -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(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 *colors, const ContentFeatures &f) + std::vector *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; } diff --git a/src/client/wieldmesh.h b/src/client/wieldmesh.h index 0b65dfbd9..1dced09e1 100644 --- a/src/client/wieldmesh.h +++ b/src/client/wieldmesh.h @@ -11,6 +11,8 @@ #include #include #include +#include +#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 animation_info; }; struct ItemMesh { scene::IMesh *mesh = nullptr; /* - * Stores the color of each mesh buffer. + * Stores draw information of each mesh buffer. */ - std::vector buffer_colors; + std::vector 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 m_colors; + std::vector m_buffer_info; /*! * The base color of this mesh. This is the default * for all mesh buffers. diff --git a/src/gui/drawItemStack.cpp b/src/gui/drawItemStack.cpp index 728f5fd11..1afe93395 100644 --- a/src/gui/drawItemStack.cpp +++ b/src/gui/drawItemStack.cpp @@ -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);