bevy/crates/bevy_pbr/src/meshlet/from_mesh.rs
JMS55 4f20faaa43
Meshlet rendering (initial feature) (#10164)
# Objective
- Implements a more efficient, GPU-driven
(https://github.com/bevyengine/bevy/issues/1342) rendering pipeline
based on meshlets.
- Meshes are split into small clusters of triangles called meshlets,
each of which acts as a mini index buffer into the larger mesh data.
Meshlets can be compressed, streamed, culled, and batched much more
efficiently than monolithic meshes.


![image](https://github.com/bevyengine/bevy/assets/47158642/cb2aaad0-7a9a-4e14-93b0-15d4e895b26a)

![image](https://github.com/bevyengine/bevy/assets/47158642/7534035b-1eb7-4278-9b99-5322e4401715)

# Misc
* Future work: https://github.com/bevyengine/bevy/issues/11518
* Nanite reference:
https://advances.realtimerendering.com/s2021/Karis_Nanite_SIGGRAPH_Advances_2021_final.pdf
Two pass occlusion culling explained very well:
https://medium.com/@mil_kru/two-pass-occlusion-culling-4100edcad501

---------

Co-authored-by: Ricky Taylor <rickytaylor26@gmail.com>
Co-authored-by: vero <email@atlasdostal.com>
Co-authored-by: François <mockersf@gmail.com>
Co-authored-by: atlas dostal <rodol@rivalrebels.com>
2024-03-25 19:08:27 +00:00

98 lines
3.6 KiB
Rust

use super::asset::{Meshlet, MeshletBoundingSphere, MeshletMesh};
use bevy_render::{
mesh::{Indices, Mesh},
render_resource::PrimitiveTopology,
};
use meshopt::{build_meshlets, compute_meshlet_bounds_decoder, VertexDataAdapter};
use std::borrow::Cow;
impl MeshletMesh {
/// Process a [`Mesh`] to generate a [`MeshletMesh`].
///
/// This process is very slow, and should be done ahead of time, and not at runtime.
///
/// This function requires the `meshlet_processor` cargo feature.
///
/// The input mesh must:
/// 1. Use [`PrimitiveTopology::TriangleList`]
/// 2. Use indices
/// 3. Have the exact following set of vertex attributes: `{POSITION, NORMAL, UV_0, TANGENT}`
pub fn from_mesh(mesh: &Mesh) -> Result<Self, MeshToMeshletMeshConversionError> {
// Validate mesh format
if mesh.primitive_topology() != PrimitiveTopology::TriangleList {
return Err(MeshToMeshletMeshConversionError::WrongMeshPrimitiveTopology);
}
if mesh.attributes().map(|(id, _)| id).ne([
Mesh::ATTRIBUTE_POSITION.id,
Mesh::ATTRIBUTE_NORMAL.id,
Mesh::ATTRIBUTE_UV_0.id,
Mesh::ATTRIBUTE_TANGENT.id,
]) {
return Err(MeshToMeshletMeshConversionError::WrongMeshVertexAttributes);
}
let indices = match mesh.indices() {
Some(Indices::U32(indices)) => Cow::Borrowed(indices.as_slice()),
Some(Indices::U16(indices)) => indices.iter().map(|i| *i as u32).collect(),
_ => return Err(MeshToMeshletMeshConversionError::MeshMissingIndices),
};
let vertex_buffer = mesh.get_vertex_buffer_data();
let vertices =
VertexDataAdapter::new(&vertex_buffer, mesh.get_vertex_size() as usize, 0).unwrap();
// Split the mesh into meshlets
let meshopt_meshlets = build_meshlets(&indices, &vertices, 64, 64, 0.0);
// Calculate meshlet bounding spheres
let meshlet_bounding_spheres = meshopt_meshlets
.iter()
.map(|meshlet| {
compute_meshlet_bounds_decoder(
meshlet,
mesh.attribute(Mesh::ATTRIBUTE_POSITION)
.unwrap()
.as_float3()
.unwrap(),
)
})
.map(|bounds| MeshletBoundingSphere {
center: bounds.center.into(),
radius: bounds.radius,
})
.collect();
// Assemble into the final asset
let mut total_meshlet_triangles = 0;
let meshlets = meshopt_meshlets
.meshlets
.into_iter()
.map(|m| {
total_meshlet_triangles += m.triangle_count as u64;
Meshlet {
start_vertex_id: m.vertex_offset,
start_index_id: m.triangle_offset,
triangle_count: m.triangle_count,
}
})
.collect();
Ok(Self {
total_meshlet_triangles,
vertex_data: vertex_buffer.into(),
vertex_ids: meshopt_meshlets.vertices.into(),
indices: meshopt_meshlets.triangles.into(),
meshlets,
meshlet_bounding_spheres,
})
}
}
/// An error produced by [`MeshletMesh::from_mesh`].
#[derive(thiserror::Error, Debug)]
pub enum MeshToMeshletMeshConversionError {
#[error("Mesh primitive topology was not TriangleList")]
WrongMeshPrimitiveTopology,
#[error("Mesh attributes were not {{POSITION, NORMAL, UV_0, TANGENT}}")]
WrongMeshVertexAttributes,
#[error("Mesh had no indices")]
MeshMissingIndices,
}