Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't lock external edges during simplification #28

Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 74 additions & 13 deletions crates/bevy_pbr/src/meshlet/from_mesh.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
#![allow(unsafe_code)]

use super::asset::{Meshlet, MeshletBoundingSphere, MeshletBoundingSpheres, MeshletMesh};
use bevy_render::{
mesh::{Indices, Mesh},
render_resource::PrimitiveTopology,
};
use bevy_utils::HashMap;
use bevy_utils::{HashMap, HashSet};
use itertools::Itertools;
use meshopt::{
build_meshlets, compute_cluster_bounds, compute_meshlet_bounds, ffi::meshopt_Bounds, simplify,
build_meshlets, compute_cluster_bounds, compute_meshlet_bounds,
ffi::{meshopt_Bounds, meshopt_simplifyWithAttributes},
Meshlets, SimplifyOptions, VertexDataAdapter,
};
use metis::Graph;
Expand All @@ -32,6 +35,8 @@ impl MeshletMesh {
let vertex_buffer = mesh.get_vertex_buffer_data();
let vertex_stride = mesh.get_vertex_size() as usize;
let vertices = VertexDataAdapter::new(&vertex_buffer, vertex_stride, 0).unwrap();
let mut mesh_boundary = vec![false; vertex_buffer.len()];
compute_mesh_boundary(&indices, &mut mesh_boundary);
let mut meshlets = compute_meshlets(&indices, &vertices);
let mut bounding_spheres = meshlets
.iter()
Expand All @@ -52,6 +57,7 @@ impl MeshletMesh {

// Build further LODs
let mut simplification_queue = 0..meshlets.len();
let mut group_boundary = vec![false; vertex_buffer.len()];
while simplification_queue.len() > 1 {
// For each meshlet build a list of connected meshlets (meshlets that share a triangle edge)
let connected_meshlets_per_meshlet =
Expand All @@ -68,9 +74,13 @@ impl MeshletMesh {

for group_meshlets in groups.into_iter().filter(|group| group.len() > 1) {
// Simplify the group to ~50% triangle count
let Some((simplified_group_indices, mut group_error)) =
simplify_meshlet_group(&group_meshlets, &meshlets, &vertices)
else {
let Some((simplified_group_indices, mut group_error)) = simplify_meshlet_group(
&group_meshlets,
&meshlets,
&vertices,
&mesh_boundary,
&mut group_boundary,
) else {
continue;
};

Expand Down Expand Up @@ -163,6 +173,29 @@ fn validate_input_mesh(mesh: &Mesh) -> Result<Cow<'_, [u32]>, MeshToMeshletMeshC
}
}

fn compute_mesh_boundary(indices: &[u32], result: &mut [bool]) {
let mut seen_edge_set = HashSet::new();
let mut border_edge_set = HashSet::new();
for tri in indices.chunks(3) {
for (v0, v1) in [(tri[0], tri[1]), (tri[1], tri[2]), (tri[0], tri[2])] {
let edge = (v0.min(v1), v0.max(v1));
if seen_edge_set.insert(edge) {
// We've never seen this before, so it's possibly on the border
border_edge_set.insert(edge);
} else {
// We've seen this before, so remove it from the border set
border_edge_set.remove(&edge);
}
}
}

result.fill(false);
for (v0, v1) in border_edge_set {
result[v0 as usize] = true;
result[v1 as usize] = true;
}
}

fn compute_meshlets(indices: &[u32], vertices: &VertexDataAdapter) -> Meshlets {
build_meshlets(indices, vertices, 255, 128, 0.0) // Meshoptimizer won't currently let us do 256 vertices
}
Expand Down Expand Up @@ -263,6 +296,8 @@ fn simplify_meshlet_group(
group_meshlets: &[usize],
meshlets: &Meshlets,
vertices: &VertexDataAdapter<'_>,
mesh_boundary: &[bool],
group_boundary: &mut [bool],
) -> Option<(Vec<u32>, f32)> {
// Build a new index buffer into the mesh vertex data by combining all meshlet data in the group
let mut group_indices = Vec::new();
Expand All @@ -273,17 +308,43 @@ fn simplify_meshlet_group(
}
}

// Compute the boundary of just this group, then unlock any vertices that are also a mesh
// boundary, so we only end up locking interior boundaries
compute_mesh_boundary(&group_indices, group_boundary);
for (group, &mesh) in group_boundary.iter_mut().zip(mesh_boundary) {
*group = *group && !mesh;
}

// Simplify the group to ~50% triangle count
// TODO: Simplify using vertex attributes
let mut error = 0.0;
let simplified_group_indices = simplify(
&group_indices,
vertices,
group_indices.len() / 2,
f32::MAX,
SimplifyOptions::LockBorder | SimplifyOptions::Sparse | SimplifyOptions::ErrorAbsolute, // TODO: Specify manual vertex locks instead of meshopt's overly-strict locks
Some(&mut error),
);
let simplified_group_indices = unsafe {
// Use the ffi function directly because `meshopt::simplif_with_locks` doesn't give us the
// result error
let vertex_data = vertices.reader.get_ref();
let vertex_data = vertex_data.as_ptr().cast::<u8>();
let positions = vertex_data.add(vertices.position_offset);
let mut result: Vec<u32> = vec![0; group_indices.len()];
let index_count = meshopt_simplifyWithAttributes(
result.as_mut_ptr().cast(),
group_indices.as_ptr().cast(),
group_indices.len(),
positions.cast::<f32>(),
vertices.vertex_count,
vertices.vertex_stride,
std::ptr::null(),
0,
std::ptr::null(),
0,
group_boundary.as_ptr().cast(),
group_indices.len() / 2,
f32::MAX,
(SimplifyOptions::Sparse | SimplifyOptions::ErrorAbsolute).bits(),
(&mut error) as *mut _,
);
result.resize(index_count, 0u32);
result
};

// Check if we were able to simplify at least a little (95% of the original triangle count)
if simplified_group_indices.len() as f32 / group_indices.len() as f32 > 0.95 {
Expand Down