Skip to content

Fix crack on animated tiles #15994

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 52 additions & 19 deletions src/client/mapblock_mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<scene::SMesh *>(m_mesh[layer].get());

Expand All @@ -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<std::string> 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;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now set to the current frame and not to the first frame.
In the crack case I noticed flickering which can be fixed this way.
I guess for non crack tiles, it is not noticeable since the map block mesh gets updated rarely, but it is probably still better to use the current frame.

// 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)
Expand All @@ -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;
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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);

Expand All @@ -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
Expand All @@ -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;
}

Expand Down
7 changes: 6 additions & 1 deletion src/client/mapblock_mesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,10 +266,15 @@ class MapBlockMesh
std::map<std::pair<u8, u32>, 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<std::pair<u8, u32>, 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<u8, u32>, std::pair<AnimationInfo, std::vector<std::string>>>
m_animation_info_crack;

// list of all semitransparent triangles in the mapblock
std::vector<MeshTriangle> m_transparent_triangles;
// Binary Space Partitioning tree for the block
Expand Down
12 changes: 10 additions & 2 deletions src/client/tile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@
#include <cassert>

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
Expand Down
7 changes: 7 additions & 0 deletions src/client/tile.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading