12个版本 (2个稳定版本)

1.1.0 2023年9月15日
1.0.0 2023年5月13日
0.6.0 2023年4月19日
0.5.1 2023年4月13日
0.1.1 2021年5月18日

游戏开发 中排名第67

MIT/Apache

230KB
2.5K SLoC

Crate transvoxel

当前版本:1.1.0

Maintenance

(以下内容由crate的rustdoc生成。在docs.rs上阅读可能体验更好)

这是Eric Lengyel的Transvoxel算法在Rust中的实现

感谢:Eric Lengyel的Transvoxel算法。 https://transvoxel.org/

问题描述的简要说明

当独立为不同细节级别的相邻块提取Marching Cubes网格时,网格通常在块的交界处不匹配,导致可见的空洞: gap-solid gap-wireframe Transvoxel算法允许通过Marching Cubes生成一个块的网格 大部分,但在一个或几个外部面上增加细节,以便与相邻块的网格匹配: fixed-wireframe

Eric Lengyel的网站对此做了更好的描述和更详细的说明。

范围

这个库只提供提取一个块网格的函数,独立于其他块。要实现完全一致的动态细节级别系统,你可能还需要

  • 决定你需要渲染哪些块,并生成哪些分辨率的网格(通常取决于相机位置和/或方向)
  • 跟踪自己约束
    • 两个渲染的相邻块只能具有相同的分辨率,或者一个块的分辨率是另一个块的两倍
    • 在后一种情况下,低分辨率块还必须以与高分辨率块方向一致的过渡面进行渲染。目前,无法在不重新提取块的新网格的情况下“翻转”过渡面的状态。这意味着更改一个块的分辨率可能会级联通过约束重新生成几个其他块

1.0.0版本中新功能

  • 接口的全面重构。值得注意的是:你现在可以自己实现一个 MeshBuilder
  • 移除了 bevy_mesh 功能:我们的示例中包含为 bevy 准备的各种网格构建器代码

基本用法

或者尝试调用 [提取] 中的一个函数,或者按照下面的示例操作

// The first thing you need is a density provider. You can implement a DataField for that
// but a simpler way, if you are just experimenting, is to use a function:

fn sphere_density(x: f32, y: f32, z: f32) -> f32 {
    1f32 - (x * x + y * y + z * z).sqrt() / 5f32
}

// 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::prelude::*;
let subdivisions = 10;
let block = Block::from([0.0, 0.0, 0.0], 10.0, subdivisions);
let transition_sides = transition_sides::no_side();

// Finally, you can run the mesh extraction:
use transvoxel::generic_mesh::GenericMeshBuilder;
let builder = GenericMeshBuilder::new();
let builder = extract_from_field(&sphere_density, &block, threshold, transition_sides, builder);
let mesh = builder.build();
assert!(mesh.tris().len() == 103);

// Extracting with some transition faces results in a slightly more complex mesh:
use transition_sides::TransitionSide::LowX;
let builder = GenericMeshBuilder::new();
let builder = extract_from_field(&sphere_density, &block, threshold, LowX.into(), builder);
let mesh = builder.build();
assert!(mesh.tris().len() == 131);

// Unless, of course, the surface does not cross that face:
use transvoxel::transition_sides::TransitionSide::HighZ;
let builder = GenericMeshBuilder::new();
let builder = extract_from_field(&sphere_density, &block, threshold, HighZ.into(), builder);
let mesh = builder.build();
assert!(mesh.tris().len() == 103);

如何使用生成的网格

一个简单正方形的网格看起来是这样的

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,
    ],
}

它由 4 个顶点组成,排列成 2 个三角形。第一个顶点位于位置 x=10.0, y=5.0, z=0.0(位置中的前 3 个浮点数)。作为列表中的第一个,它的索引是 0,我们可以看到它在 2 个三角形中使用(第一个三角形使用顶点 0 1 2,第二个三角形使用顶点 0 2 3)

如何请求过渡边

use transvoxel::transition_sides::{TransitionSide, no_side};

// 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));

限制/可能的改进

  • 提供一个方法来提取没有法线或有面法线的网格,这将更快
  • 输出/输入位置/法线仅支持 f32。应很容易将其扩展到 f64
  • 体素密度缓存不理想:可能只有在空块的情况下,体素的密度才会查询一次。在非空块中,某些体素的密度可能会被多次查询
  • 算法改进。见 算法

许可证:MIT OR Apache-2.0

根据您的选择,许可如下

任选其一。

贡献

除非您明确说明,否则根据 Apache-2.0 许可证定义的,您有意提交给作品包含的任何贡献,将根据上述条款双许可,没有额外的条款或条件。

依赖关系

~1–11MB
~121K SLoC