diff --git a/README.md b/README.md index 7736bf48..357b8408 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,14 @@ Features - Voxel data is streamed from a variety of sources, which includes the ability to write your own - Minecraft-style blocky voxel terrain, with multiple materials and baked ambient occlusion - Smooth terrain using Dual Marching Cubes -- Levels of details for smooth terrain (though not user editable yet) +- Levels of detail for smooth terrain - Voxel storage using 8-bit channels for any general purpose What This Module Doesn't Provide ----------------------------------- -- Level of detail for blocky terrain +- Levels of detail for blocky terrain - Game specific features such as cave generation or procedural trees (though it might include tools to help doing them) - Editor tools (only a few things are exposed) - Import and export of voxel formats @@ -34,7 +34,7 @@ How To Install And Use Voxel Tools is a custom C++ module for Godot 3.1+. It must be compiled into the engine to work. -Please see the [Getting Started Guide](doc/01_get-started.md) guide for instructions, or the [the demo](https://github.com/Zylann/voxelgame) for working examples. +Please see the [Getting Started Guide](doc/01_get-started.md) for instructions, or [Zylann's demos](https://github.com/Zylann/voxelgame) and [TinmanJuggernaut's demo](https://github.com/tinmanjuggernaut/voxelgame) for working examples. Roadmap diff --git a/doc/01_get-started.md b/doc/01_get-started.md index f74bca17..9c772af4 100644 --- a/doc/01_get-started.md +++ b/doc/01_get-started.md @@ -9,9 +9,10 @@ Voxel Tools for Godot is a C++ module that must be compiled into the engine. It * [Texturing & Materials](04_materials.md) * [Collision](05_collision.md) * [Custom Data Streams](06_custom-streams.md) +* [Performance Tips](07_performance-tips.md) ## API -* [Overview](07_api-overview.md) +* [Overview](08_api-overview.md) * [API Class List](api/Class_List.md) @@ -19,4 +20,6 @@ Voxel Tools for Godot is a C++ module that must be compiled into the engine. It Find more information about related topics here. * [Custom C++ modules in Godot](https://godot.readthedocs.io/en/latest/development/cpp/custom_modules_in_cpp.html) +* [Dual Marching Cubes algorithm](https://www.volume-gfx.com/volume-rendering/dual-marching-cubes/) + diff --git a/doc/02_build-voxel-tools.md b/doc/02_build-voxel-tools.md index ea4ea305..2fe7959c 100644 --- a/doc/02_build-voxel-tools.md +++ b/doc/02_build-voxel-tools.md @@ -4,6 +4,7 @@ These steps will walk you through creating a custom build of Godot with the Voxe ## Build Godot From Source 1. Download and compile the [Godot source](https://github.com/godotengine/godot) by following [the official guide](https://docs.godotengine.org/en/latest/development/compiling/index.html). If you want to regularly update your build (recommended), clone the repository with Git instead of downloading a zip file. +1. Generally you want the master branch to match with the master branch of Voxel Tools. However as of Godot 3.1, a stable branch is being maintained. 1. Build Godot before adding this or any other modules and make sure it produces an executable. 1. Run the newly built executable found in `godot/bin`. Look under Help/About and confirm that the version string indicates you are running a development version (e.g. 3.2dev.custom_build.ee5ba3e). @@ -11,11 +12,12 @@ These steps will walk you through creating a custom build of Godot with the Voxe ## Add Voxel Tools 1. Download or clone the repository for [Voxel Tools](https://github.com/Zylann/godot_voxel). Use Git to clone the repository if you want to make it easy to update your builds (recommended). +1. Generally you want the master branch to get the latest features and fixes. If you desired, you can use the branch that corresponds with Godot's stable branch (e.g. godot3.1). 1. Place the Voxel Tools directory inside your Godot source, in the `godot/modules` directory. 1. Rename the Voxel Tools folder to `voxel`. When correct, the files (e.g. README.md) will be located in `godot/modules/voxel`. **This is important!** 1. Rebuild Godot and make sure it produces an executable. 1. Test that your build has Voxel support: - 1. Run your new build of Godot. + 1. Run your new Godot build. 1. Create a new project. 1. Create a new 3D scene. 1. Add a new node, search for "Voxel" and see if "VoxelTerrain" appears. If so, you have a successful build. If not, review these instructions and your build logs to see if you missed a step or something failed along the way. @@ -29,7 +31,7 @@ If you cloned Godot and Voxel Tools, you can use git to update your local code. 1. Rebuild Godot. **Note:** -Since you are pulling from two development branches, it's probable that on occasion your build won't compile, your project won't open, or your Voxel Tools won't work properly or even crash Godot. To minimize downtime, save your successful builds. Move them out of the build folder and rename them with the version number (e.g. godot-3.2-ee5ba3e.exe). This way, you can continue to use previously working builds until the Godot or Voxel developers fix whatever is broken. It is generally desired by all that code published to repositories will at least build. +Since you are pulling from two development branches, it's probable that on occasion your build won't compile, your project won't open, or your Voxel Tools won't work properly or even crash Godot. To minimize downtime, save your successful builds. Move them out of the build folder and rename them with the version number (e.g. godot-3.2-ee5ba3e.exe). This way, you can continue to use previously working builds until the Godot or Voxel developers fix whatever is broken. It is generally desired by all that code published to repositories will at least build, but stuff happens. --- diff --git a/doc/03_create-terrain.md b/doc/03_create-terrain.md index ab04d34c..c9a655cc 100644 --- a/doc/03_create-terrain.md +++ b/doc/03_create-terrain.md @@ -1,13 +1,13 @@ # Creating A Voxel Terrain -Now that your Godot Engine has voxel support built in, you can either download the [Voxel Game demo](https://github.com/Zylann/voxelgame) and start playing with it, or start creating your own terrain. +Now that your Godot Engine has voxel support built in, you can either download one of the demos ([Zylann's demos](https://github.com/Zylann/voxelgame) or [TinmanJuggernaut's fps_demo](https://github.com/tinmanjuggernaut/voxelgame)) and start playing with them, or start creating your own terrain. -## Create A Terrain In The Editor +## Create A Terrain In The Editor: VoxelTerrain + Heightmap 1. Create a new project and a new 3D scene, with a Spatial type root node. -1. Add a Camera and elevate it by setting Transform Y = 75 and Rotation X = -25. The terrain starts generating around (0,0,0), but creates high hills, and may be invisible from underneath. We will be looking down from above. +1. Add a Camera and elevate it by setting the transform: Translation Y = 75 and Rotation X = -25. The terrain starts generating around (0,0,0), but creates high hills, and may be invisible from underneath. We will be looking down from above. 1. Import a black and white heightmap texture such as [this one](https://github.com/Zylann/voxelgame/blob/master/project/blocky_terrain/noise_distorted.png) from the demo. Make sure that the file is imported as an Image and NOT a Texture on the Import panel. You'll likely have to re-import and restart. -1. Add a VoxelTerrain node and adjust the following settings: - 1. Provider: New VoxelStreamImage. Then click VoxelStreamImage again. +1. Add a `VoxelTerrain` node and adjust the following settings: + 1. Stream: New VoxelStreamImage. Then click VoxelStreamImage again. 1. Image: Load. Select your imported noise texture. 1. Decide on the type of terrain you want: * Blocky: Set Channel = "Type" (or 0), and leave Smooth Meshing Enabled unchecked (lower down). @@ -16,7 +16,48 @@ Now that your Godot Engine has voxel support built in, you can either download t 1. Set the Viewer Path, Assign, select your camera or player (the parent of your camera). 1. Play your scene and you should see a terrain. -![Generated voxel terrain](images/default-terrain.jpg) + + + + + +## Create A Terrain In The Editor: VoxelLODTerrain + 3D Noise +Here we'll look at `VoxelLODTerrain` with a noise data stream. + +1. Create a new project and a new 3D scene, with a Spatial type root node. +1. Add a Camera and adjust the following settings: + 1. Transform: Translation Y = 200. + 1. Rotation X = -25. + 1. Far: 2048 (or up to the maximum of 8192, which I use). +1. Add a `VoxelLODTerrain` node and adjust the following settings: + 1. Stream: New VoxelStreamNoise. Then click VoxelStreamNoise again. + 1. Noise: New OpenSimplexNoise. Then click OpenSimplexNoise again. Leave the settings at the default, but here's where they are. + 1. Set the Viewer Path, Assign, select your camera or player (the parent of your camera). +1. Play your scene and you should see a terrain. + + + + +### Experiment With Noise +Now try playing with the numbers. For instance, these settings will reach the horizon and give a noise terrain with a greater distance between the highest and lowest points: + +1. Camera + 1. Far: 8192 + 1. Translate: Y = 240 (Make sure Y is near or above Height Start + Height Range below). +1. VoxelLODTerrain + 1. Adjust the noise values as desired or leave at the defaults. + 1. Noise: Height Start: -200 + 1. Noise: Height Range: 600 (This will kill your performance if set too high. However I've experimented with 10,000 on a GTX1060.) + 1. View Distance: 4096 + 1. LOD Count: 8 + + + + +### Additional Notes +* We will add materials on the next page. + +* Heightmaps, 3D Noise, and custom streams all work with either `VoxelTerrain` or `VoxelLODTerrain`. ## Create A Terrain With Code @@ -37,6 +78,7 @@ func _ready(): add_child(terrain) # Add the terrain to the tree so Godot will render it ``` + --- * [Next Page](04_materials.md) * [Doc Index](01_get-started.md) diff --git a/doc/04_materials.md b/doc/04_materials.md index 374a7bfe..5f0cd576 100644 --- a/doc/04_materials.md +++ b/doc/04_materials.md @@ -38,14 +38,17 @@ Rather than writing your own shader from scratch, especially with triplanar mapp If you want to start without a texture and just want to use a color, try turning down roughness, or adding some metalic to give the surface some reflectivity. This will allow light to reflect off the curves of the terrain in the distance. Otherwise you'll just see an undifferentiated mass of color. -Also, VoxelTerrain adds Ambient Occlusion to the vertex colors (blocky only). You can add this to your material by enabling the `Vertex Color/Use As Albedo` flag in your material. +## Enable Built-In Ambient Occlusion +VoxelTerrain adds Ambient Occlusion to the vertex colors on blocky terrains. You can add this to your material by enabling the `Vertex Color/Use As Albedo` flag in your material. Below is what that looks like on an otherwise plain white material. AO can also be added to any terrain using the AO feature built in to materials. + -### How To View Live Changes To Materials + +## How To View Live Changes To Materials You can't see the terrain in the viewport, so there are two options to view your material live while making changes: -* Add a sphere or cube with the same material, then adjust the material. This option is OK, but the UV scale is usually different than the terrain, so it's not ideal. -* Run your scene, focus the camera on the terrain, move the window to the side, and adjust the material in the editor window. All edits to a SpatialMaterial (or shader parameters if using a ShaderMaterial) will update live. +* Add a sphere or cube with the same material, then adjust the material in the editor. This option is OK, but the UV scale is usually different than the terrain, so it's not ideal. +* Run your scene, focus the camera on the terrain, reduce the game window and move it to the side, then move the editor window to the other side. With both the editor and game displayed simultaneously, you can adjust the material in the inspector panel and it will update live. All edits to a SpatialMaterial or the shader parameters of your ShaderMaterial will update live. Editing your shader code will not update live. (Though it may be possible to trigger the engine to recompile your shader code.) -### Convert To Shader Code +## Convert To Shader Code The SpatialMaterial is a very good base, but can only go so far. If you need further customization, you'll want to convert it to shader code. @@ -59,17 +62,17 @@ Note, you can't edit the shader code and see live changes. You must stop and res ## Advanced Shading -What if you want to have multiple materials, such as grass on the top and rock on the sides? +One of the best ways to learn about shaders is to pick apart and experiment with other's shader code. -What if you want to have two materials, each with their own triplanar mapped albedo normal and AO maps, then blend them together based on if their normal faces the upward direction or the sides? +Here's a shader that supports two materials, such as grass on the top and rock on the sides, each with triplanar mapped albedo, normal and AO maps, then blended together based on if their normal faces the upward direction or the sides. -You can find a working example of that in this [development demo](https://github.com/tinmanjuggernaut/voxelgame/tree/fps_demo/project), under fps_demo, or see the [shader](https://github.com/tinmanjuggernaut/voxelgame/blob/fps_demo/project/fps_demo/materials/triplanar.shader) itself. (*Editor-* update these links after publishing demo) +You can find a working example in the [fps demo](https://github.com/tinmanjuggernaut/voxelgame), or see the [shader](https://github.com/tinmanjuggernaut/voxelgame/blob/master/project/fps_demo/materials/triplanar.shader) itself. In the shader parameters, add your two albedo maps, and optionally normal, and AO maps. Then play with the `AB Mix 1` and `AB Mix 2` sliders to adjust how the top and sides blend together. The other settings should be self explanatory. The screenshot below also has a little bit of fog and far DOF added. -![textured terrain](images/textured-terrain.jpg) + --- * [Next Page](05_collision.md) -* [Doc Index](01_get-started.md) \ No newline at end of file +* [Doc Index](01_get-started.md) diff --git a/doc/05_collision.md b/doc/05_collision.md index 9fc2cb72..9bc597ab 100644 --- a/doc/05_collision.md +++ b/doc/05_collision.md @@ -11,13 +11,13 @@ The collision is built along with the mesh. So any blocks that have already been You can also turn on the collision wire mesh for debugging. In the editor, look under the Debug menu for `Visible Collision Shapes`. -![heightmap](https://user-images.githubusercontent.com/632766/62386300-61ab9c00-b592-11e9-8f01-3f454593dd91.gif) + # Alternatives To Physics Based Collision -There are some alternative collision related tools available: +Though physics based collision is recommended, there might be times when alternative methods are desired. ## Axis Aligned Bounding Box (Blocky only) diff --git a/doc/06_custom-streams.md b/doc/06_custom-streams.md index e22318e7..cef60a27 100644 --- a/doc/06_custom-streams.md +++ b/doc/06_custom-streams.md @@ -28,13 +28,16 @@ func _ready(): add_child(terrain) ``` -![Custom Data Stream](images/custom-stream.jpg) + -`VoxelBuffer.fill()` is probably not what you want to use. Emerge_block generally gives you a block of 16x16x16 cubes to fill all at once, so you probably want to use `VoxelBuffer.set_voxel()` to specify each one. See the API here or in the editor for up to date function definitions. +Though `VoxelBuffer.fill()` is probably not what you want to use, the above is a quick example. Emerge_block generally gives you a block of 16x16x16 cubes to fill all at once, so you probably want to use `VoxelBuffer.set_voxel()` to specify each one. + +In the fps_demo, there is a [custom gdscript stream](https://github.com/tinmanjuggernaut/voxelgame/blob/master/project/fps_demo/scripts/MyStream.gd) that makes a sine wave. This was copied from the [C++ version](../streams/voxel_stream_test.cpp), which runs a lot faster. + + -You should review the [C++ code](../streams) for the built-in streams to see how they utilize the API to fill the buffers one voxel at a time. --- -* [Next Page](07_api-overview.md) +* [Next Page](07_performance-tips.md) * [Doc Index](01_get-started.md) diff --git a/doc/07_performance-tips.md b/doc/07_performance-tips.md new file mode 100644 index 00000000..8cf3e844 --- /dev/null +++ b/doc/07_performance-tips.md @@ -0,0 +1,16 @@ +# Performance Tips + +* Disable VSync and set threading to `Single Safe` in the project settings. Terrain building and editing performs significnatly faster with these settings. + +* Excessive noise height will kill your performance, use with care. A height range of 300 can run at 300-400fps on a GTX1060, but a range of 6-10,000 might run at 30-40fps. Hopefully this will be addressed when occlusion culling and other features are available in Godot 4. + +* Lower shadow distance. I might use 200-400 on a GTX1060, but maybe 20 or disabled on an integrated card. + +* Optimize and simplify your shaders. In the fps_demo, there are two grass-rock shaders. The second version randomizes the tiling of texture maps so there is no obvious repeating pattern. It only requires two extra texture lookups, but it pretty much halves my frame rate from around 300 to 150 or less. + + + + +--- +* [Next Page](08_api-overview.md) +* [Doc Index](01_get-started.md) diff --git a/doc/07_api-overview.md b/doc/08_api-overview.md similarity index 88% rename from doc/07_api-overview.md rename to doc/08_api-overview.md index 3a7582c6..287cf44f 100644 --- a/doc/07_api-overview.md +++ b/doc/08_api-overview.md @@ -36,7 +36,7 @@ Note that the engine and your graphics card do not work with voxels. They are co **Blocky [Terrain]** - An algorithm that uses full cubes to approximate Voxel data. This is Minecraft, building an airplane out of Legos, or representing a bell curve with a bar chart. It is better suited for man-made shapes with hard corners. -**Smooth [Terrain]** - One of many isometric surface extraction algorithms that attempts to represent voxel data with smoother surfaces and is a better fit for organic shapes. Rather than using a full cube to describe the surface, a cube with one or more planes at potentially any angle is used to provide a far more accurate representation of the source data. As an example if you took a Lego block and sanded down the sharp corners to better represent the airplane curves, or sanded down the tops of the bar chart to better represent the bell curve. However you can only sand down to flat planes, no curves within the cubes. +**Smooth [Terrain]** - One of many isosurface extraction algorithms that attempts to represent voxel data with smoother surfaces and is a better fit for organic shapes. Rather than using a full cube to describe the surface, a cube with one or more planes at potentially any angle is used to provide a far more accurate representation of the source data. As an example if you took a Lego block and sanded down the sharp corners to better represent the airplane curves, or sanded down the tops of the bar chart to better represent the bell curve. However you can only sand down to flat planes, no curves within the cubes. Voxel Tools supports Dual Marching Cubes, Transvoxel (WIP) and is structured to support other algorithms in the future. Some are faster, use less memory, provide manifold meshes (cleaner meshes), and/or provide more accurate representations of the voxel data. @@ -61,16 +61,17 @@ An infinite, paged terrain made of voxel blocks, all with the same level of deta * Stores a terrain in a VoxelMap. * Supports blocky and smooth terrains. * Handles the main Godot \_process() call to update the terrain, load and unload voxel blocks, and generate the mesh and collision. -* Supports raycasts (for manual collision and click detection). +* Provides raycasts (for manual collision and click detection) in addition to physics collision based raycasts. + ### VoxelLodTerrain -A (not yet infinite) paged terrain made of voxel blocks with a variable level of detail. Designed for high view distances. Voxels are polygonized around the viewer by distance in a very large sphere, usually extending beyond the cameras Far clip. +An infinite, paged terrain made of voxel blocks with a variable level of detail. Designed for high view distances. Voxels are polygonized around the viewer by distance in a very large sphere. * Data is streamed using a VoxelStream, which must support LOD. * Stores a terrain in a VoxelMap. * Handles the main Godot \_process() call to update the terrain, load and unload voxel blocks, and generate the mesh and collision. * Supports only smooth terrains. -* Does not yet support raycasts (PR #29 pending). +* Supports raycasts via physics based collision. * Manages Levels of detail (LOD). @@ -105,7 +106,7 @@ Generates 3D noise via OpenSimplexNoise. ## Mesher Classes -These classes use full cubes or an isometric surface extraction algorithm to represent the source voxel data. They polygonize arbitrary-sized chunks of voxels. +These classes use full cubes or an isosurface extraction algorithm to represent the source voxel data. They polygonize arbitrary-sized chunks of voxels. Note: Their initial use case was for terrains, so the input data must be padded by a certain amount of voxels to work (get_minimum_padding() gives you that number. It's usually 1 or 2). @@ -127,6 +128,7 @@ Builds a smooth mesh using the Dual Marching Cubes algorithm. * Supports LOD by simply scaling up the result. * Uses marching squares skirts to hide the cracks between sections with a different LOD. + ### VoxelMesherTransvoxel Builds a smooth mesh using the Transvoxel algorithm. diff --git a/doc/images/custom-stream-sine.jpg b/doc/images/custom-stream-sine.jpg new file mode 100644 index 00000000..759143fc Binary files /dev/null and b/doc/images/custom-stream-sine.jpg differ diff --git a/doc/images/debug-collision-shapes.gif b/doc/images/debug-collision-shapes.gif new file mode 100644 index 00000000..3b45e04b Binary files /dev/null and b/doc/images/debug-collision-shapes.gif differ diff --git a/doc/images/noise-terrain-default.jpg b/doc/images/noise-terrain-default.jpg new file mode 100644 index 00000000..9a1e676d Binary files /dev/null and b/doc/images/noise-terrain-default.jpg differ diff --git a/doc/images/noise-terrain-far.jpg b/doc/images/noise-terrain-far.jpg new file mode 100644 index 00000000..a4b78590 Binary files /dev/null and b/doc/images/noise-terrain-far.jpg differ diff --git a/streams/voxel_stream.cpp b/streams/voxel_stream.cpp index 278cce5e..a2ceebf2 100644 --- a/streams/voxel_stream.cpp +++ b/streams/voxel_stream.cpp @@ -18,13 +18,11 @@ void VoxelStream::emerge_block(Ref out_buffer, Vector3i origin_in_v const Variant *args[3] = { &arg1, &arg2, &arg3 }; Variant::CallError err; script->call("emerge_block", args, 3, err); - if (err.error != Variant::CallError::CALL_OK) { - ERR_EXPLAIN("voxel_stream.cpp:emerge_block gave an error: " + String::num(err.error) + - ", Argument: " + String::num(err.argument) + - ", Expected type: " + Variant::get_type_name(err.expected)); - ERR_FAIL(); - // This had to be explicitely logged due to the usual GD debugger not working with threads - } + ERR_FAIL_COND_MSG(err.error != Variant::CallError::CALL_OK, + "voxel_stream.cpp:emerge_block gave an error: " + String::num(err.error) + + ", Argument: " + String::num(err.argument) + + ", Expected type: " + Variant::get_type_name(err.expected)); + // This had to be explicitely logged due to the usual GD debugger not working with threads } } @@ -39,13 +37,11 @@ void VoxelStream::immerge_block(Ref buffer, Vector3i origin_in_voxe const Variant *args[3] = { &arg1, &arg2, &arg3 }; Variant::CallError err; script->call("immerge_block", args, 3, err); - if (err.error != Variant::CallError::CALL_OK) { - ERR_EXPLAIN("voxel_stream.cpp:immerge_block gave an error: " + String::num(err.error) + - " Argument: " + String::num(err.argument) + - " Expected type: " + Variant::get_type_name(err.expected)); - ERR_FAIL(); - // This had to be explicitely logged due to the usual GD debugger not working with threads - } + ERR_FAIL_COND_MSG(err.error != Variant::CallError::CALL_OK, + "voxel_stream.cpp:immerge_block gave an error: " + String::num(err.error) + + " Argument: " + String::num(err.argument) + + " Expected type: " + Variant::get_type_name(err.expected)); + // This had to be explicitely logged due to the usual GD debugger not working with threads } }