Current version: 0.6.0
(the following is generated from the crate's rustdoc. reading it on docs.rs will probably be a better experience)
This is an implementation of Eric Lengyel's Transvoxel Algorithm in Rust
Credits: Eric Lengyel's Transvoxel Algorithm. https://transvoxel.org/
When extracting meshes with Marching Cubes for adjacent blocks at different level of detail independently, the meshes generally do not match at the blocks' junctions, inducing visible holes:
The Transvoxel Algorithm allows generating a mesh for a block mostly with marching cubes, but with increased detail on one or several external faces of the block, in order to match with neighbouring blocks' meshes:
Eric Lengyel's website describes this better and in more details.
This library only provides functions for extracting a mesh for a block, independently of other blocks. To implement a fully consistent dynamic level-of-detail system, you will also probably need to: * decide which blocks you need to render and generate meshes for, and at which resolution (typically depending on the camera position and/or orientation) * track yourself constraints: * two rendered adjacent blocks can only either have the same resolution, or one have double the resolution of the other * in that second case, the low resolution block must also be rendered with a transition face in the direction of the high resolution block Currently, it is not possible to "flip" a transition face status on a block, without re-extracting a new mesh for the block. Which means changing the resolution for one block can cascade through constraints to re-generating a few other blocks as well
Either try calling one of the functions in [extraction], or follow the example below: ```rust // The first thing you need is a density provider. You can implement a ScalarField for that // but a simpler way, if you are just experimenting, is to use a function: use transvoxel::density::ScalarFieldForFn;
fn sphere_density(x: f32, y: f32, z: f32) -> f32 { 1f32 - (x * x + y * y + z * z).sqrt() / 5f32 }
let mut field = ScalarFieldForFn(sphere_density);
// Going along with your density function, you need a threshold value for your density: // This is the value for which the surface will be generated. You can typically choose 0. // Values over the threshold are considered inside the volume, and values under the threshold // outside of the volume. In our case, we will have a density of 0 on a sphere centered on the // world center, of radius 5. let threshold = 0f32;
// Then you need to decide for which region of the world you want to generate the mesh, and how
// many subdivisions should be used (the "resolution"). You also need to tell which sides of the
// block need to be transition (double-resolution) faces. We use no_side
here for simplicity,
// and will get just a regular Marching Cubes extraction, but the Transvoxel transitions can be
// obtained simply by providing some sides instead (that is shown a bit later):
use transvoxel::structs::Block;
use transvoxel::transitionsides::noside;
let subdivisions = 10;
let block = Block::from([0.0, 0.0, 0.0], 10.0, subdivisions);
let transitionsides = noside();
// Finally, you can run the mesh extraction: use transvoxel::extraction::extractfromfield; let mesh = extractfromfield(&mut field, &block, threshold, transition_sides); assert!(mesh.tris().len() == 103);
// Extracting with some transition faces results in a slightly more complex mesh: use transvoxel::transitionsides::TransitionSide::LowX; let mesh = extractfrom_field(&mut field, &block, threshold, LowX.into()); assert!(mesh.tris().len() == 131);
// Unless, of course, the surface does not cross that face: use transvoxel::transitionsides::TransitionSide::HighZ; let mesh = extractfrom_field(&mut field, &block, threshold, HighZ.into()); assert!(mesh.tris().len() == 103); ```
A mesh for a simple square looks like this:
ron
Extracted mesh: Mesh {
positions: [
10.0,
5.0,
0.0,
0.0,
5.0,
0.0,
0.0,
5.0,
10.0,
10.0,
5.0,
10.0,
],
normals: [
-0.0,
1.0,
-0.0,
-0.0,
1.0,
-0.0,
-0.0,
1.0,
-0.0,
-0.0,
1.0,
-0.0,
],
triangle_indices: [
0,
1,
2,
0,
2,
3,
],
}
It is made of 4 vertices, arranged in 2 triangles.
The first vertex is at position x=10.0, y=5.0, z=0.0 (the first 3 floats in position).
As the first in the list, it's index is 0, and we can see it is used in the 2 triangles
(the first triangle uses vertices 0 1 2, and the second triangle vertices 0 2 3)
If you need to use the mesh in Bevy, you can enable feature bevy_mesh
and use functions in [bevy_mesh]
```rust use transvoxel::transitionsides::{TransitionSide, noside};
// If you don't hardcode sides like in the example above, you can build a set of sides incrementally: // They use the FlagSet crate let mut sides = no_side(); sides |= TransitionSide::LowX; sides |= TransitionSide::HighY;
assert!(sides.contains(TransitionSide::LowX)); assert!(!sides.contains(TransitionSide::HighX)); ```
Licensed under either of
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.