Skip to content
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

Redrawing occasionally shows brief holes in the terrain #57

Open
TokisanGames opened this issue Aug 30, 2019 · 9 comments
Open

Redrawing occasionally shows brief holes in the terrain #57

TokisanGames opened this issue Aug 30, 2019 · 9 comments

Comments

@TokisanGames
Copy link
Contributor

Often when modifying a terrain, an update draws an artifact that breaks the illusion of being deep inside the terrain. It's quick, but not imperceptible even at high framerates. If you turn on vsync and/or multithreading the redraw speed is slower and they are even more noticeable.

In all three examples below, I'm digging deep into a mountain, yet one frame shows light peeking through, then it disappears. In the first example, this might have happened on two sections simultaneously. Most edits draw fine, but I could reproduce the artifact in less than a minute.

It only occurs during the redraw of the edit. Once the edit completes it's fine. It's like it deletes the mesh, then gets hung up long enough for the renderer to draw the frame as is, then finally creates the new mesh in the next frame.

image

If you want to see it, you'll probably want to use my demo (in my PR) so you can test high amounts of changes. I've been regularly updating it and it does a pretty good job of keeping the camera within the terrain. See the readme for controls, but right-click destroys terrain.

@TokisanGames
Copy link
Contributor Author

This is with the latest update, including the voxelblock fix you did today.

Sometimes the redraws happen over two frames. Here the lower blue is an actual hole I've dug through. The upper left blue shouldn't be there. You can see in the last two frames the ceiling fills in there:

voxel artifact

@Zylann
Copy link
Owner

Zylann commented Aug 30, 2019

I suspect this is happening because the edit you made is intersecting more than one block. In the worst case, there are 8 blocks to remesh if you do this at an intersection. Because that's an asynchronous process, the re-meshed results aren't guaranteed to come back at the same frame. So you will see through the terrain for a very brief moment. It is more likely to happen if the process of meshing and creating the Mesh resources takes longer than a frame.

No mesh get deleted here (to my knowledge at least). The holes are just the consequence of seeing removed voxels in one mesh, but the neighbor mesh hasn't been updated yet so you are seeing its insides.

I think this is also happening in Minecraft, especially when using TNT.
I also saw that in Minetest as well (tested just now).

Something we could try to minimize this would be to set request batching to 8 in the updater, so that it will always process by packs of 8 and come back all at once, but the main thread creation of meshes could still cause it to happen over two frames. It could be told to run more as well, but you'll get stuttering. So other possibility would be to hold on before updating the meshes, which would make updates feel a bit slower to come.

@TokisanGames TokisanGames changed the title Redrawing artifacts show holes in the terrain Redrawing occasionally shows brief holes in the terrain Sep 5, 2019
@TokisanGames
Copy link
Contributor Author

Using the VoxelTool::do_sphere in C++ has reduced artifacting significantly at a sphere radius of 3.5. However at a radius of 10.5, which isn't huge, it is very common and obvious.

@Zylann
Copy link
Owner

Zylann commented Sep 13, 2019

At such a large radius it becomes really hard to expect this to not happen. radius of 10 can span across 3x3x3 blocks (27) which would all have to complete remeshing during a single frame.

@TokisanGames
Copy link
Contributor Author

It's not too big though. My character is 2.5 units tall. I mean big to me would be like 100.... speaking of which * trying it out * hmm it doesn't work. But 50 works and it's interesting. Here the redrawing looks kind of cool, kind of matrixy.

image

@Zylann
Copy link
Owner

Zylann commented Sep 13, 2019

I mean big to me would be like 100.... speaking of which * trying it out * hmm it doesn't work

LOD terrain has a limitation of how large the edited area is, because you can only edit within a margin of LOD0.

@nic96
Copy link
Contributor

nic96 commented Jul 28, 2020

I have this same issue even when destroying just one block at a time in blocky terrain. Would it be possible to run the mesher first as if the blocks that are supposed to be destroyed were like transparent blocks where other faces get rendered behind first?

@Zylann
Copy link
Owner

Zylann commented Jul 28, 2020

Destroying a block is instant. There is no "first" to speak of. You could only do that kind of workaround if it took time to destroy, which introduces complexity which you could handle in game logic.
Solutions you could use in a script include temporarily replacing the mined cube with a transparent variant, like you said, and reverting it if the player decides to stop before it's destroyed. Or better, place a MeshInstance variant of the cube during one frame. Or the other way around, using a mesher to polygonize a 3x3x3 area without the cube in the main thread, and show that mesh just for one frame from the moment you destroy it, to cover eventual holes. That covers the issue for single block scenarios.

Otherwise, so far I really haven't a generic fix for this issue that doesn't come with drawbacks. When destroying single blocks It's possible to make it less likely to happen but latency issues will come up as I described here #57 (comment).
To sum it up, the cause is that blocks are updated asynchronously. Because of threading, and because their main thread stage is staggered to avoid stuttering. One variable to tune easily the main thread part would be this:

const uint32_t MAIN_THREAD_MESHING_BUDGET_MS = 8;
. If Godot can't handle 8 mesh updates in 8 milliseconds, then there is a chance to see a hole briefly if you are at a block boundary. Increasing this gives it more time, but also means the game has a higher chance to stall. If that still doesn't fix it, a brutal fix would be to force updates at close range around the player to also be processed immediately, in the main thread. So even more stalling, but it would do it all in one go.

With more blocks being destroyed it's much harder to avoid. It's also worth noting that Minetest and Minecraft still have the same problem to this day, which is partly covered with smoke particles. If you just want to cover up the case of single blocks being removed, game logic solutions described above sound like less trouble.
(note: always test this at least in release_debug mode, it's totally possible debug has this the worst).

@nic96
Copy link
Contributor

nic96 commented Jul 28, 2020

I like the idea of replacing the block with a MeshInstance cube for one frame. Anyway this gives me some ideas for things to try. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants