Skip to content

Commit

Permalink
Merge branch 'master' into refactor_gpu
Browse files Browse the repository at this point in the history
# Conflicts:
#	engine/gpu/compute_shader_resource.cpp
  • Loading branch information
Zylann committed Dec 11, 2024
2 parents c6101cf + 700fd27 commit 9ab8cab
Show file tree
Hide file tree
Showing 77 changed files with 1,895 additions and 1,062 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/mono.yml
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,13 @@ jobs:
repository: godotengine/godot
ref: ${{ env.GODOT_BASE_BRANCH }}

# The version of ThorVG in 4.3-stable hits an error in latest MSVC (see godot#95861).
# We should no longer need this in 4.3.1 and later.
- name: Patch ThorVG
run: |
curl -LO https://github.com/godotengine/godot/commit/4abc358952a69427617b0683fd76427a14d6faa8.patch
git apply 4abc358952a69427617b0683fd76427a14d6faa8.patch
# Clone our module under the correct directory
- uses: actions/checkout@v4
with:
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ jobs:
with:
path: modules/voxel

# The version of ThorVG in 4.3-stable hits an error in latest MSVC (see godot#95861).
# We should no longer need this in 4.3.1 and later.
- name: Patch ThorVG
run: |
curl -LO https://github.com/godotengine/godot/commit/4abc358952a69427617b0683fd76427a14d6faa8.patch
git apply 4abc358952a69427617b0683fd76427a14d6faa8.patch
# Upload cache on completion and check it out now
# Editing this is pretty dangerous for Windows since it can break and needs to be properly tested with a fresh cache.
- name: Load .scons_cache directory
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ SummitCollie
nulshift
ddel-rio (Daniel del Río Román)
Cyberphinx
Mia (Tigxette)
```


18 changes: 17 additions & 1 deletion doc/classes/VoxelAStarGrid3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,57 +6,73 @@
<description>
This can be used to find paths between two voxel positions on blocky terrain.
It is tuned for agents 2 voxels tall and 1 voxel wide, which must stand on solid voxels and can jump 1 voxel high.
Search radius may also be limited (50 voxels and above starts to be relatively expensive).
No navmesh is required, it uses voxels directly with no baking. However, search radius is limited by an area (50 voxels and above starts to be relatively expensive).
At the moment, this pathfinder only considers voxels with ID 0 to be air, and the rest is considered solid.
Note: "positions" in this class are expected to be in voxels. If your terrain is offset or if voxels are smaller or bigger than world units, you may have to convert coordinates.
</description>
<tutorials>
</tutorials>
<methods>
<method name="debug_get_visited_positions" qualifiers="const">
<return type="Vector3i[]" />
<description>
Gets the list of voxel positions that were visited by the last pathfinding request (relates to how A* works under the hood). This is for debugging.
</description>
</method>
<method name="find_path">
<return type="Vector3i[]" />
<param index="0" name="from_position" type="Vector3i" />
<param index="1" name="to_position" type="Vector3i" />
<description>
Calculates a path starting from a voxel position to a target voxel position.
Those positions should be air voxels just above ground with enough room for agents to fit in.
The returned path will be a series of contiguous voxel positions to walk through in order to get to the destination.
If no path is found, or if either start or destination position is outside of the search area, an empty array will be returned.
You may also use [method set_region] to specify the search area.
</description>
</method>
<method name="find_path_async">
<return type="void" />
<param index="0" name="from_position" type="Vector3i" />
<param index="1" name="to_position" type="Vector3i" />
<description>
Same as [method find_path], but performs the calculation on a separate thread. The result will be emitted with the [signal async_search_completed] signal.
Only one asynchronous search can be active at a given time. Use [method is_running_async] to check this.
</description>
</method>
<method name="get_region">
<return type="AABB" />
<description>
Gets the maximum region limit that will be considered for pathfinding, in voxels.
</description>
</method>
<method name="is_running_async" qualifiers="const">
<return type="bool" />
<description>
Returns true if a path is currently being calculated asynchronously. See [method find_path_async].
</description>
</method>
<method name="set_region">
<return type="void" />
<param index="0" name="box" type="AABB" />
<description>
Sets the maximum region limit that will be considered for pathfinding, in voxels. You should usually set this before calling [method find_path].
The larger the region, the more expensive the search can get. Keep in mind voxel volumes scale cubically, so don't use this on large areas (for example 50 voxels is quite big).
</description>
</method>
<method name="set_terrain">
<return type="void" />
<param index="0" name="terrain" type="VoxelTerrain" />
<description>
Sets the terrain that will be used to do searches in.
</description>
</method>
</methods>
<signals>
<signal name="async_search_completed">
<param index="0" name="path" type="Vector3i[]" />
<description>
Emitted when searches triggered with [method find_path_async] are complete.
</description>
</signal>
</signals>
Expand Down
1 change: 1 addition & 0 deletions doc/classes/VoxelGeneratorNoise2D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<member name="curve" type="Curve" setter="set_curve" getter="get_curve">
When assigned, this curve will alter the distribution of height variations, allowing to give some kind of "profile" to the generated shapes.
By default, a linear curve from 0 to 1 is used.
It is assumed that the curve's domain goes from 0 to 1.
</member>
<member name="height_range" type="float" setter="set_height_range" getter="get_height_range" overrides="VoxelGeneratorHeightmap" default="200.0" />
<member name="noise" type="Noise" setter="set_noise" getter="get_noise">
Expand Down
7 changes: 5 additions & 2 deletions doc/classes/VoxelStream.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,21 @@
<method name="load_voxel_block">
<return type="int" enum="VoxelStream.ResultCode" />
<param index="0" name="out_buffer" type="VoxelBuffer" />
<param index="1" name="origin_in_voxels" type="Vector3i" />
<param index="1" name="block_position" type="Vector3i" />
<param index="2" name="lod_index" type="int" />
<description>
[code]out_buffer[/code]: Block of voxels to load. Must be a pre-created instance (not null).
[code]block_position[/code]: Position of the block in block coordinates within the specified LOD.
</description>
</method>
<method name="save_voxel_block">
<return type="void" />
<param index="0" name="buffer" type="VoxelBuffer" />
<param index="1" name="origin_in_voxels" type="Vector3i" />
<param index="1" name="block_position" type="Vector3i" />
<param index="2" name="lod_index" type="int" />
<description>
[code]buffer[/code]: Block of voxels to save. It is strongly recommended to not keep a reference to that data afterward, because streams are allowed to cache it, and saved data must represent either snapshots (copies) or last references to the data after the volume they belonged to is destroyed.
[code]block_position[/code]: Position of the block in block coordinates within the specified LOD.
</description>
</method>
</methods>
Expand Down
2 changes: 1 addition & 1 deletion doc/classes/VoxelTool.xml
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@
<param index="2" name="max_distance" type="float" default="10.0" />
<param index="3" name="collision_mask" type="int" default="4294967295" />
<description>
Runs a voxel-based raycast to find the first hit from an origin and a direction.
Runs a voxel-based raycast to find the first hit from an origin and a direction. Coordinates are in world space.
Returns a result object if a voxel got hit, otherwise returns [code]null[/code].
This is useful when colliders cannot be relied upon. It might also be faster (at least at short range), and is more precise to find which voxel is hit. It internally uses the DDA algorithm.
[code]collision_mask[/code] is currently only used with blocky voxels. It is combined with [member VoxelBlockyModel.collision_mask] to decide which voxel types the ray can collide with.
Expand Down
2 changes: 1 addition & 1 deletion doc/graph_nodes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
<output name="out"/>
<parameter name="curve" type="Object" default_value="null"/>
<description>
Returns the value of a custom [code]curve[/code] at coordinate [code]x[/code], where [code]x[/code] is in the range [code]\[0..1][/code]. The [code]curve[/code] is specified with a [Curve] resource.
Returns the value of a custom [code]curve[/code] at coordinate [code]x[/code], where [code]x[/code] is in the range specified by its domain properties (in Godot 4.3 and earlier, it is in [code]\[0..1][/code]). The [code]curve[/code] is specified with a [Curve] resource.
</description>
</node>
<node name="CustomInput" category="Input">
Expand Down
8 changes: 7 additions & 1 deletion doc/source/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ Semver is not yet in place, so each version can have breaking changes, although

Primarily developped with Godot 4.3.

- `VoxelBlockyModel`: Added option to turn off "LOD skirts" when used with `VoxelLodTerrain`, which may be useful with transparent models
- `VoxelBlockyModelCube`: Added support for mesh rotation like `VoxelBlockyMesh` (prior to that, rotation buttons in the editor only swapped tiles around)
- `VoxelEngine`: Added the `tasks.gpu` entry to the dictionary returned by `get_stats`, which may be useful for loading screens (notably asynchronous compiling of compute shaders, which can delay generation if GPU is enabled)
- `VoxelInstanceGenerator`: Added `OnePerTriangle` emission mode
- `VoxelToolLodTerrain`: Implemented raycast when the mesher is `VoxelMesherBlocky` or `VoxelMesherCubes`
- `VoxelInstanceGenerator`: Added ability to filter spawning by voxel texture indices, when using `VoxelMesherTransvoxel` with `texturing_mode` set to `4-blend over 16 textures`

- Fixes
- Fixed potential deadlock when using detail rendering and various editing features (thanks to lenesxy, issue #693)
Expand All @@ -25,7 +28,9 @@ Primarily developped with Godot 4.3.
- Fixed blocks were saved with incorrect LOD index when they get unloaded using Clipbox, leading to holes and mismatched terrain (#691)
- Fixed incorrect loading of chunks near terrain borders when viewers are far away from bounds, when using the Clipbox streaming system
- `VoxelStreamSQLite`: fixed connection leaks (thanks to lenesxy, issue #713)
- `VoxelTerrain`: edits and copies across fixed bounds no longer behave as if terrain generates beyond (was causing "walls" to appear).
- `VoxelTerrain`:
- Edits and copies across fixed bounds no longer behave as if terrain generates beyond (was causing "walls" to appear).
- Viewers with collision-only should no longer cause visual meshes to appear
- `VoxelGeneratorGraph`:
- Fixed wrong values when using `OutputWeight` with optimized execution map enabled, when weights are determined to be locally constant
- Fixed occasional holes in terrain when using `FastNoise3D` nodes with the `OpenSimplex2S` noise type
Expand All @@ -39,6 +44,7 @@ Primarily developped with Godot 4.3.
- `VoxelInstanceLibrary`: Items should no longer be accessed using generated properties (`item1`, `item2` etc). Use `get_item` instead.
- `VoxelMesherTransvoxel`: Removed `deep_sampling` experimental option
- `VoxelTool`: The `flat_direction` of `do_hemisphere` now points away from the flat side of the hemisphere (like its normal), instead of pointing towards it
- `VoxelToolLodTerrain`: `raycast` used to take coordinates in terrain space. It is now in world space, for consistency with `VoxelToolTerrain`.


1.3 - 17/08/2024 - branch `1.3` - tag `v1.3.0`
Expand Down
53 changes: 43 additions & 10 deletions doc/source/performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,15 @@ Terrains are rendered with many unique meshes. That can amount for a lot of draw
- Increase mesh block size: they default to 16, but it can be set to 32 instead. This reduces the number of draw calls, but may increase the time it takes to modify voxels.


Slow mesh updates issue with OpenGL
------------------------------------
### Slow mesh updates issue with OpenGL

### Issue
#### Issue

Godot 3.x is using OpenGL, and there is an issue which currently degrades performance of this voxel engine a lot. Framerate is not necessarily bad, but the speed at which voxel terrain updates is very low, compared to what it should be. So far the issue has been seen on Windows, on both Intel or nVidia cards.

Note: Godot 4.x will have an OpenGL renderer, but this issue has not been tested here yet.

### Workarounds
#### Workarounds

Note: you don't have to do them all at once, picking just one of them can improve the situation.

Expand All @@ -66,7 +65,7 @@ Note: you don't have to do them all at once, picking just one of them can improv
- Or turn off `display/window/vsync/use_vsync` in project settings. Not as effective and eats more resources, but improves performance.
- Or turn on `display/window/vsync/vsync_via_compositor` in project settings. Not as effective but can improve performance in windowed mode.

### Explanation
#### Explanation

The engine relies a lot on uploading many meshes at runtime, and this cannot be threaded efficiently in Godot 3.x so far. So instead, meshes are uploaded in the main thread, until part of the frame time elapsed. Beyond that time, the engine stops and continues next frame. This is intented to smooth out the load and avoid stutters *caused by the task CPU-side*. Other tasks that cannot be threaded are also put into the same queue, like creating colliders.

Expand All @@ -76,10 +75,9 @@ When one workaround is used, like enabling `verbose_stdout`, this slowdown compl
For more information, see [Godot issue #52801](https://github.com/godotengine/godot/issues/52801).


Slowdown when moving fast with Vulkan
--------------------------------------
### Slowdown when moving fast with Vulkan

### Issue
#### Issue

If you move fast while near a terrain with a lot of chunks (mesh size 16 and high LOD detail), the renderer can cause noticeable slowdowns. This is because Godot4's Vulkan allocator is much slower to destroy mesh buffers than Godot 3 was, and it does that on the main thread. When you move fast, a lot of meshes get created in front of the camera, and a lot get destroyed behind the camera at the same time. Creation is cheap, destruction is expensive.

Expand All @@ -93,7 +91,7 @@ This issue also was not noticeable in Godot 3.

This problem reproduces specifically when a lot of small meshes are destroyed (small as in 16x16 pieces of terrain, variable size), while a lot of them (thousands) already exist at the same time. Note, some of them are not necessarily visible.

### Workarounds
#### Workarounds

It is not possible for the module to just "pool the meshes", because when new meshes need to be created, the API requires to create new buffers anyways and drops the old ones (AFAIK). It is also not possible to use a thread on our side because the work is deferred to the end of the frame, not on the call site.

Expand All @@ -106,7 +104,42 @@ The only workarounds involve limiting the game:
- Reduce LOD distance so less blocks have to be destroyed, at the expense of quality


Iteration order
Physics
----------

The voxel engine offers two different approaches to physics:
- Standard Physics: the official API Godot exposes through `PhysicsServer3D` (Godot Physics, Godot Jolt...).
- Box Physics: a small specialized API that only works with axis-aligned boxes on blocky terrain, exposed with `VoxelBoxMover` and voxel raycasts. It is much more limited and requires some setup, but performs faster.

### Standard Physics

#### Mesh colliders simulation

Similar to rendering 16x16x16 or 32x32x32 blocks, the voxel engine uses "mesh" colliders for every terrain block (or "chunk"). These colliders are static and can be concave. Therefore, any terrain shape should be supported, but depends a lot on how performant these colliders are in the underlying physics engine.

Moving terrain remains possible, but do not expect physics to work correctly on the surface while moving it.

#### Tunnelling

Mesh colliders used by terrain have no "thickness". An object can sit undisturbed outside or inside of it, contrary to convex colliders which usually have a "depenetration force" pushing objects away from their inside. This makes mesh colliders more prone to "tunneling": if an object goes too fast, or is too small relative to its velocity, it can pass through the ground.

- Limit speed of your objects
- For fast-moving objects (projectiles?), use elongated shapes, or just raycasts, making sure that the "trail" of the shape "connects" between each physics frame
- Enable Continuous Collision Detection, if the physics engine supports it
- Check voxel data to find out if a point is underground and move up the object

#### Shape creation is very slow

Similar to rendering, the voxel engine has to convert voxels into meshes ("meshing"), and does this with our own prioritised pool of threads.
It will use those meshes as colliders. Creating a collider from a mesh is actually much more expensive than meshing itself (about 3 to 5 times), because it involves creating an acceleration structure to speed up collision detection (BVH, octree...).

Unfortunately, Godot does not offer a reliable way to safely create these shapes *including their acceleration structure* from within out meshing threads. So instead, we had to defer it all to the main thread, and spread it over multiple frames. This slows down terrain loading tremendously (compared to disabling collisions).

- A [proposal](https://github.com/godotengine/godot-proposals/issues/483) has been opened to expose this issue, still not addressed
- [Godot Jolt](https://github.com/godotengine/godot/pull/99895) also has this issue, exacerbated by the fact it was implemented to defer shape setup to the very last moment, when entering the scene tree. So even if we were allowed to create mesh colliders from our threads, it still defers all the hard work to the main thread.


Voxel Iteration order
-----------------

In this engine, voxels are stored in flat arrays indexed in ZXY order. Y is the "deepest" coordinate: when iterating a `VoxelBuffer` of dimensions `(size.x, size.y, size.z)`, adding 1 to the Y coordinate is equivalent to advancing by 1 element in memory. Conversely, adding 1 to the X coordinate advances by `size.y` elements, and adding 1 to the Z coordinate advances by `(size.x * size.y)` elements.
Expand Down
5 changes: 4 additions & 1 deletion doc/source/quick_start.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ Here are some reasons why you might not need it:

- "I need to make a planet": you can make more efficient planets by stitching 6 spherified heightmaps together. Take a cube where each face is a heightmap, then puff that cube to turn it into a sphere.

- "I want to make Minecraft but free and with my own blocks": Minecraft is a lot more than voxels. While the module can replicate basic functionalities, it is more general than this at the moment, so it doesn't provide a lot of features found in Minecraft out of the box. Alternatively, you could create a mod with [Minetest](https://www.minetest.net/), which is a more specialized engine.
- "I want to make Minecraft but different and with my own blocks": Minecraft is a lot more than voxels. While the module can replicate basic functionalities, it is more general/low-level than this at the moment, so it doesn't provide a lot of features found in Minecraft out of the box. Alternatively, you could create a mod with [Minetest](https://www.minetest.net/), which is a more specialized engine.

- "I want super small voxels like Teardown or John Lin's sandbox": these games use a very different tech than this module uses. They raytrace voxels in real-time. This module instead uses a classic polygon-based approach. While you could in theory make terrain that looks like that, it won't perform well.

- "GridMap sucks": how large do you want your grid to be? How complex are your models? This module's blocky mesher is geared towards very large grids with simple geometry, so it has its own restrictions.

Loading

0 comments on commit 9ab8cab

Please sign in to comment.