diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index a53a5c073..d94c28b07 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -647,6 +647,9 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data): m_bounding_radius = std::sqrt(collector.m_bounding_radius_sq); + // Using the current time avoids flickering before animate() gets called + auto animation_time = client->getAnimationTime(); + for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { scene::SMesh *mesh = static_cast(m_mesh[layer].get()); @@ -657,9 +660,39 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data): p.applyTileColor(); // Generate animation data - // - Cracks - if (p.layer.material_flags & MATERIAL_FLAG_CRACK) { - // Find the texture name plus ^[crack:N: + if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) { + if (p.layer.material_flags & MATERIAL_FLAG_CRACK) { + // - Texture animation with crack + std::vector crack_frames{p.layer.frames->size()}; + for (std::size_t j = 0; j < p.layer.frames->size(); j++) { + std::ostringstream os(std::ios::binary); + os << m_tsrc->getTextureName((*p.layer.frames)[j].texture_id) << "^[crack"; + if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY) + os << "o"; // use ^[cracko + os << ":" << 1 << ":"; + + crack_frames[j] = os.str(); + } + AnimationInfo animation_info = AnimationInfo(p.layer); + animation_info.needUpdate(animation_time); // Update current frame + // Replace tile texture with the cracked animated one + p.layer.texture = m_tsrc->getTextureForMesh( + crack_frames[animation_info.getCurrentFrame()] + "0", + &p.layer.texture_id); + + m_animation_info_crack.emplace(std::make_pair(layer, i), + std::make_pair(std::move(animation_info), std::move(crack_frames))); + } else { + // - Texture animation + AnimationInfo animation_info = AnimationInfo(p.layer); + animation_info.needUpdate(animation_time); // Update current frame + // Replace tile texture with the current animation frame + p.layer.texture = (*p.layer.frames)[animation_info.getCurrentFrame()].texture; + // Add to MapBlockMesh in order to animate these tiles + m_animation_info.emplace(std::make_pair(layer, i), std::move(animation_info)); + } + } else if (p.layer.material_flags & MATERIAL_FLAG_CRACK) { + // - Cracks std::ostringstream os(std::ios::binary); os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack"; if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY) @@ -675,13 +708,6 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data): os.str() + "0", &p.layer.texture_id); } - // - Texture animation - if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) { - // Add to MapBlockMesh in order to animate these tiles - 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; - } // Create material video::SMaterial material; @@ -731,7 +757,8 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data): // Check if animation is required for this mesh m_has_animation = !m_crack_materials.empty() || - !m_animation_info.empty(); + !m_animation_info.empty() || + !m_animation_info_crack.empty(); } MapBlockMesh::~MapBlockMesh() @@ -761,12 +788,6 @@ 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); @@ -777,8 +798,6 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, m_tsrc->getTextureForMesh(s, &new_texture_id); buf->getMaterial().setTexture(0, new_texture); } - - m_last_crack = crack; } // Texture animation @@ -788,6 +807,20 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, it.second.updateTexture(material, time); } + // Texture animation with crack + for (auto &it : m_animation_info_crack) { + if (crack != m_last_crack || it.second.first.needUpdate(time)) { + scene::IMeshBuffer *buf = m_mesh[it.first.first]->getMeshBuffer(it.first.second); + + u16 frame = it.second.first.getCurrentFrame(); + std::string s = it.second.second[frame] + itos(crack); + u32 new_texture_id = 0; + video::ITexture *new_texture = m_tsrc->getTextureForMesh(s, &new_texture_id); + buf->getMaterial().setTexture(0, new_texture); + } + } + + m_last_crack = crack; return true; } diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index 6d8ddc36c..9eb4fba84 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -266,10 +266,15 @@ private: std::map, std::string> m_crack_materials; // Animation info: texture animation - // Maps mesh and mesh buffer indices to TileSpecs + // Maps mesh and mesh buffer indices to AnimationInfo // Keys are pairs of (mesh index, buffer index in the mesh) std::map, AnimationInfo> m_animation_info; + // Same as above, but in the case both are needed, animation and crack + // The vector contains base crack textures for each animation frame + std::map, std::pair>> + m_animation_info_crack; + // list of all semitransparent triangles in the mapblock std::vector m_transparent_triangles; // Binary Space Partitioning tree for the block diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 894e97339..a6b20fd1a 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -6,15 +6,23 @@ #include void AnimationInfo::updateTexture(video::SMaterial &material, float animation_time) +{ + if (needUpdate(animation_time)) { + assert(m_frame < m_frames->size()); + material.setTexture(0, (*m_frames)[m_frame].texture); + } +}; + +bool AnimationInfo::needUpdate(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); + return true; } + return false; }; void TileLayer::applyMaterialOptions(video::SMaterial &material, int layer) const diff --git a/src/client/tile.h b/src/client/tile.h index ffbe78bac..30e7bf5a7 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -164,6 +164,13 @@ struct AnimationInfo { void updateTexture(video::SMaterial &material, float animation_time); + // Returns true if texture needs to be updated + // Also proceeds to next frame if update is needed + bool needUpdate(float animation_time); + + // Returns last used frame + u16 getCurrentFrame() const { return m_frame; }; + private: u16 m_frame = 0; // last animation frame u16 m_frame_length_ms = 0;