diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 8181c2e6d9..72555751ce 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -638,6 +638,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()); @@ -648,9 +651,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) @@ -658,7 +691,7 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data): u8 tiles = p.layer.scale; if (tiles > 1) os << ":" << (u32)tiles; - os << ":" << (u32)p.layer.animation_frame_count << ":"; + os << ":1:"; m_crack_materials.insert(std::make_pair( std::pair(layer, i), os.str())); // Replace tile texture with the cracked one @@ -666,13 +699,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; @@ -722,7 +748,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() @@ -752,12 +779,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); @@ -768,8 +789,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 @@ -779,6 +798,19 @@ 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); + video::ITexture *new_texture = m_tsrc->getTextureForMesh(s); + 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 d0fce21402..4fecd7f09c 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -278,10 +278,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 894e97339f..12533b403c 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -6,16 +6,24 @@ #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 3293b4dd17..1d0e620935 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;