godot_voxel icon indicating copy to clipboard operation
godot_voxel copied to clipboard

Custom shape for `VoxelViewer`

Open NuclearPhoenixx opened this issue 1 year ago • 10 comments

Is your feature request related to a problem? Please describe. The current VoxelViewer loads all voxels inside a sphere with the user-defined radius view_distance with priority to the closest ones. However, this behavior can be a visual problem for games like Minecraft clones. If the player is on a mountain or the surface of the world is very flat, most of what they see will be the surface blocks only and because of the way blocks are loaded, visual gaps will be almost a guarantee while loading/generating. This obviously depends on the view distance and the player's machine, so it will be more or less noticable for everyone. This is because all of the sub-surface blocks that will be generated first due to their distance to the player.

Describe the solution you'd like Since you cannot really define a clear surface for the world due to the cube chunk nature of the module and custom generators, a good way to mitigate this would be to have a custom shape for the VoxelViewer. We could use something like a disk shape on the surface and change it back to a sphere when underground dynamically depending on the needs. I think this would be a pretty clean solution since it would still retain all the other functionality of the VoxelViewer and you could default to a sphere anyways. I'm thinking of a shape selector a bit like the physics collision shapes for example. However, I clearly have no idea about the implementation in the module, so it might actually be a lot harder to implement than it sounds.

Describe alternatives you've considered An alternative would be to use multiple VoxelViewer nodes around the player on a flat surface each with a lower view distance than with the single VoxelViewer. This way, the horizontal view distance will be just as large as before, but the vertical one will be much lower, resulting in faster loading of the horizontal blocks. The only problem with this approach is that there is no priority given to the individual VoxelViewers, so they will each load around themselves first resulting in gaps between all the node positions and weird looking loading in general.

Please let me know what you guys think about that!

NuclearPhoenixx avatar Apr 16 '24 11:04 NuclearPhoenixx

The current VoxelViewer loads all voxels inside a sphere

That isn't really the way it works in the module. VoxelViewer has properties that actually act like hints, they are not always forcefully respected. With VoxelTerrain and VoxelLodTerrain with the Clipbox system, the area needs to be a box, clamped to terrain bounds. With the Octree system, it loads as a sphere, but unloading is still a box clamped to terrain bounds.

If the player is on a mountain or the surface of the world is very flat, most of what they see will be the surface blocks only and because of the way blocks are loaded, visual gaps will be almost a guarantee while loading/generating

I don't follow. These are completely different situations, not sure what gaps you are referring to. For the mountain case, do you mean that since the player is high, the bottom of the mountain would start "disappearing" by being out of range due to the view distance being a box with equal sides? In cases like this usually you can set the view distance to encompass the maximum height, and make it so the Y coordinate of the viewer remains centered in that range (assuming fixed world height like in Minecraft). Though I understand that you currently can't choose different horizontal and vertical ranges. If you want infinite world height though, I guess you're out of luck^^ For the flat world case, I don't undertsand the problem. If you think chunks high and below would waste loading time if there isn't supposed to be anything there in a fixed-height game, you can give the terrain smaller vertical bounds, and viewers will clamp their area to that.

This is because all of the sub-surface blocks that will be generated first due to their distance to the player.

I don't understand what you mean by that. Chunks are loaded in priority close to the viewer, so there shouldn't be problems with far away chunks loading first or something.

Since you cannot really define a clear surface for the world due to the cube chunk nature of the module and custom generators, a good way to mitigate this would be to have a custom shape for the VoxelViewer.

This is not always straightforward to do, but it might be possible to specify different horizontal and vertical ranges if desired, since in most cases, viewer areas are considered to be a box. However, not all streaming systems would support that properly. VoxelLodTerrain with the Octree system is designed to use a sphere, it might not work properly with a cylinder. The caps also assume the world has finite height, because otherwise it would likely cause LOD cracks wen loading chunks above and below. It would also significantly increase the amount of chunks, which that system doesn't handle well performance-wise. The distance function would also have to dynamically check which formula to use all the time. Switching between shapes may also trigger a lot of chunk switches, which can be as jarring and expensive as teleporting far away.

An alternative would be to use multiple VoxelViewer nodes around the player on a flat surface each with a lower view distance than with the single VoxelViewer. This way, the horizontal view distance will be just as large as before, but the vertical one will be much lower, resulting in faster loading of the horizontal blocks

What you describe could be accomplished easily by exposing separate vertical and horizontal view distances (only working with corresponding systems). But again if the goal is to replicate Minecraft, you could also just center the Y coordinate of viewers and change the vertical bounds of your terrain.

Zylann avatar Apr 16 '24 17:04 Zylann

I don't follow. These are completely different situations, not sure what gaps you are referring to. For the mountain case, do you mean that since the player is high, the bottom of the mountain would start "disappearing" by being out of range due to the view distance being a box with equal sides?

I was just giving examples for when the player is looking at a lot of surface voxels over the whole viewing distance with not much in between to hide the loading of new blocks. When you have a fairly flat surface, you will be able to look far, same when you're on a mountain/hill. What I meant by "gaps" is the fact that a mountain or hill for example can be in multiple blocks and when one block loads, it might only load like half of the hill leaving a hole or gap in the mesh that you can see through until the surrounding blocks have finished loading as well.

In cases like this usually you can set the view distance to encompass the maximum height, and make it so the Y coordinate of the viewer remains centered in that range (assuming fixed world height like in Minecraft). Though I understand that you currently can't choose different horizontal and vertical ranges. If you want infinite world height though, I guess you're out of luck^^

Yes, limiting the y range is an obvious solution to this. Having different horizontal and vertical ranges would be nice though.

For the flat world case, I don't undertsand the problem. If you think chunks high and below would waste loading time if there isn't supposed to be anything there in a fixed-height game, you can give the terrain smaller vertical bounds, and viewers will clamp their area to that.

The problem is that I want the player to be able to build as high as they want even if nothing up there will be generated naturally. However, I understand that this is kind of a self-made problem and sort of a contradiction to wanting faster horizontal loading times.

I don't understand what you mean by that. Chunks are loaded in priority close to the viewer, so there shouldn't be problems with far away chunks loading first or something.

OK, to be specific, I experimented with instead of having a single 512 VoxelViewer, having 5x 256 VoxelViewers: one in the center where the player is, and the other four in the same horizontal plane with a distance of 256 to the player. This results in the same 512 horizontal viewing distance like before, but with less of a vertical viewing distance. The problem with this approach was that each VoxelViewer prioritizes loading around themselves first obviously, resulting in a weird loading pattern where I would have empty blocks between each of the VoxelViewer until it finished loading, i.e. filling the gaps. I hope I explained that OK now.

This is not always straightforward to do, but it might be possible to specify different horizontal and vertical ranges if desired, since in most cases, viewer areas are considered to be a box. However, not all streaming systems would support that properly. VoxelLodTerrain with the Octree system is designed to use a sphere, it might not work properly with a cylinder. The caps also assume the world has finite height, because otherwise it would likely cause LOD cracks wen loading chunks above and below. It would also significantly increase the amount of chunks, which that system doesn't handle well performance-wise. The distance function would also have to dynamically check which formula to use all the time. Switching between shapes may also trigger a lot of chunk switches, which can be as jarring and expensive as teleporting far away.

I understand, and I get the potential problem with VoxelLodTerrain. I figured it could be a lot harder to implement than it sounds. What I'm ultimately concerned with is a distinction between horizontal and vertical view distances, like you mentioned. Having more fancy shapes was just an idea since selecting a shape would fit very nicely with other Godot features like selecting a collision shape or mesh primitive. However, I can't really imagine someone needing a torus shaped view distance lol

NuclearPhoenixx avatar Apr 16 '24 20:04 NuclearPhoenixx

So what should be done then? If your world is fixed height like in Minecraft, you can already use terrain bounds. If not, then viewers would have to have separate hints for vertical and horizontal view distances (as well as terrains, which themselves have a maximum distance). As for being able to edit as high as desired, having chunks load or not load is unavoidable.

Zylann avatar Apr 16 '24 20:04 Zylann

Having different horizontal and vertical view distances on the VoxelViewer instead of just a single setting would be neat IMO. The terrain itself would then have to have a similarly separate horizontal and vertical max view distance setting.

NuclearPhoenixx avatar Apr 16 '24 20:04 NuclearPhoenixx

That shouldn't be hard to add, however it would break compatibility since existing viewers are not aware of a second property. It might require a third property that enables the second one.

Zylann avatar Apr 16 '24 21:04 Zylann

Maybe having a Vector2 type of setting that allows to scale the horizontal and vertical view distances individually using the current view distance parameter would be a good way to do that? We could default to Vector2(1,1) for the same view distance in both directions like it is now. In my examples from above I could go Vector2(1, 0.25) or something to scale down the vertical view distance, therefore getting faster horizontal block loading while not restricting the vertical build space in general.

NuclearPhoenixx avatar Apr 17 '24 14:04 NuclearPhoenixx

Is there a reason it should be a Vector2 instead of just one vertical_view_distance_ratio?

Zylann avatar Apr 17 '24 20:04 Zylann

Nope, that's actually a better idea.

NuclearPhoenixx avatar Apr 18 '24 09:04 NuclearPhoenixx

I pushed a new branch view_distance_vertical_ratio

Zylann avatar Apr 18 '24 19:04 Zylann

Compiles on Linux and works just as expected in the project itself. I didn't encounter any issues with the new parameter. Thanks a bunch, great work.

NuclearPhoenixx avatar Apr 20 '24 10:04 NuclearPhoenixx

The feature was merged into the main branch.

Zylann avatar May 01 '24 19:05 Zylann