mirror of
https://github.com/bevyengine/bevy
synced 2025-01-22 18:05:17 +00:00
4f20faaa43
# 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>
98 lines
3.6 KiB
Rust
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,
|
|
}
|