use crate::ffi;
use crate::{DecodePosition, VertexDataAdapter};
use std::mem;
pub type Bounds = ffi::meshopt_Bounds;
fn finalize_meshlets(
mut meshlets: Vec<ffi::meshopt_Meshlet>,
mut meshlet_verts: Vec<u32>,
mut meshlet_tris: Vec<u8>,
count: usize,
) -> Meshlets {
if count > 0 {
let last_meshlet = meshlets[count - 1];
meshlet_verts
.truncate(last_meshlet.vertex_offset as usize + last_meshlet.vertex_count as usize);
meshlet_tris.truncate(
last_meshlet.triangle_offset as usize
+ ((last_meshlet.triangle_count as usize * 3 + 3) & !3),
);
}
meshlets.truncate(count);
for meshlet in meshlets.iter_mut().take(count) {
unsafe {
ffi::meshopt_optimizeMeshlet(
&mut meshlet_verts[meshlet.vertex_offset as usize],
&mut meshlet_tris[meshlet.triangle_offset as usize],
meshlet.triangle_count as usize,
meshlet.vertex_count as usize,
);
};
}
Meshlets {
meshlets,
vertices: meshlet_verts,
triangles: meshlet_tris,
}
}
#[derive(Copy, Clone)]
pub struct Meshlet<'data> {
pub vertices: &'data [u32],
pub triangles: &'data [u8],
}
pub struct Meshlets {
pub meshlets: Vec<ffi::meshopt_Meshlet>,
pub vertices: Vec<u32>,
pub triangles: Vec<u8>,
}
impl Meshlets {
#[inline]
pub fn len(&self) -> usize {
self.meshlets.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.meshlets.is_empty()
}
fn meshlet_from_ffi(&self, meshlet: &ffi::meshopt_Meshlet) -> Meshlet<'_> {
Meshlet {
vertices: &self.vertices[meshlet.vertex_offset as usize
..meshlet.vertex_offset as usize + meshlet.vertex_count as usize],
triangles: &self.triangles[meshlet.triangle_offset as usize
..meshlet.triangle_offset as usize + meshlet.triangle_count as usize * 3],
}
}
#[inline]
pub fn get(&self, idx: usize) -> Meshlet<'_> {
self.meshlet_from_ffi(&self.meshlets[idx])
}
pub fn iter(&self) -> impl Iterator<Item = Meshlet<'_>> {
self.meshlets
.iter()
.map(|meshlet| self.meshlet_from_ffi(meshlet))
}
}
pub fn build_meshlets(
indices: &[u32],
vertices: &VertexDataAdapter<'_>,
max_vertices: usize,
max_triangles: usize,
cone_weight: f32,
) -> Meshlets {
let meshlet_count =
unsafe { ffi::meshopt_buildMeshletsBound(indices.len(), max_vertices, max_triangles) };
let mut meshlets: Vec<ffi::meshopt_Meshlet> =
vec![unsafe { ::std::mem::zeroed() }; meshlet_count];
let mut meshlet_verts: Vec<u32> = vec![0; meshlet_count * max_vertices];
let mut meshlet_tris: Vec<u8> = vec![0; meshlet_count * max_triangles * 3];
let count = unsafe {
ffi::meshopt_buildMeshlets(
meshlets.as_mut_ptr(),
meshlet_verts.as_mut_ptr(),
meshlet_tris.as_mut_ptr(),
indices.as_ptr(),
indices.len(),
vertices.pos_ptr(),
vertices.vertex_count,
vertices.vertex_stride,
max_vertices,
max_triangles,
cone_weight,
)
};
finalize_meshlets(meshlets, meshlet_verts, meshlet_tris, count)
}
pub fn build_meshlets_flex(
indices: &[u32],
vertices: &VertexDataAdapter<'_>,
max_vertices: usize,
min_triangles: usize,
max_triangles: usize,
cone_weight: f32,
split_factor: f32,
) -> Meshlets {
let meshlet_count =
unsafe { ffi::meshopt_buildMeshletsBound(indices.len(), max_vertices, min_triangles) };
let mut meshlets: Vec<ffi::meshopt_Meshlet> = vec![unsafe { mem::zeroed() }; meshlet_count];
let mut meshlet_verts: Vec<u32> = vec![0; meshlet_count * max_vertices];
let mut meshlet_tris: Vec<u8> = vec![0; meshlet_count * max_triangles * 3];
let count = unsafe {
ffi::meshopt_buildMeshletsFlex(
meshlets.as_mut_ptr(),
meshlet_verts.as_mut_ptr(),
meshlet_tris.as_mut_ptr(),
indices.as_ptr(),
indices.len(),
vertices.pos_ptr(),
vertices.vertex_count,
vertices.vertex_stride,
max_vertices,
min_triangles,
max_triangles,
cone_weight,
split_factor,
)
};
finalize_meshlets(meshlets, meshlet_verts, meshlet_tris, count)
}
pub fn build_meshlets_spatial(
indices: &[u32],
vertices: &VertexDataAdapter<'_>,
max_vertices: usize,
min_triangles: usize,
max_triangles: usize,
fill_weight: f32,
) -> Meshlets {
let meshlet_count =
unsafe { ffi::meshopt_buildMeshletsBound(indices.len(), max_vertices, min_triangles) };
let mut meshlets: Vec<ffi::meshopt_Meshlet> = vec![unsafe { mem::zeroed() }; meshlet_count];
let mut meshlet_verts: Vec<u32> = vec![0; meshlet_count * max_vertices];
let mut meshlet_tris: Vec<u8> = vec![0; meshlet_count * max_triangles * 3];
let count = unsafe {
ffi::meshopt_buildMeshletsSpatial(
meshlets.as_mut_ptr(),
meshlet_verts.as_mut_ptr(),
meshlet_tris.as_mut_ptr(),
indices.as_ptr(),
indices.len(),
vertices.pos_ptr(),
vertices.vertex_count,
vertices.vertex_stride,
max_vertices,
min_triangles,
max_triangles,
fill_weight,
)
};
finalize_meshlets(meshlets, meshlet_verts, meshlet_tris, count)
}
pub fn partition_clusters(
destination: &mut [u32],
cluster_indices: &[u32],
cluster_index_counts: &[u32],
vertex_count: usize,
target_partition_size: usize,
) -> usize {
assert_eq!(destination.len(), cluster_index_counts.len());
debug_assert_eq!(
cluster_indices.len(),
cluster_index_counts.iter().sum::<u32>() as usize
);
unsafe {
ffi::meshopt_partitionClusters(
destination.as_mut_ptr(),
cluster_indices.as_ptr(),
cluster_indices.len(),
cluster_index_counts.as_ptr(),
cluster_index_counts.len(),
std::ptr::null(),
vertex_count,
0,
target_partition_size,
)
}
}
pub fn partition_clusters_with_positions(
destination: &mut [u32],
cluster_indices: &[u32],
cluster_index_counts: &[u32],
vertices: &VertexDataAdapter<'_>,
target_partition_size: usize,
) -> usize {
assert_eq!(destination.len(), cluster_index_counts.len());
debug_assert_eq!(
cluster_indices.len(),
cluster_index_counts.iter().sum::<u32>() as usize
);
unsafe {
ffi::meshopt_partitionClusters(
destination.as_mut_ptr(),
cluster_indices.as_ptr(),
cluster_indices.len(),
cluster_index_counts.as_ptr(),
cluster_index_counts.len(),
vertices.pos_ptr(),
vertices.vertex_count,
vertices.vertex_stride,
target_partition_size,
)
}
}
pub fn partition_clusters_with_decoder<T: DecodePosition>(
destination: &mut [u32],
cluster_indices: &[u32],
cluster_index_counts: &[u32],
vertices: &[T],
target_partition_size: usize,
) -> usize {
assert_eq!(destination.len(), cluster_index_counts.len());
debug_assert_eq!(
cluster_indices.len(),
cluster_index_counts.iter().sum::<u32>() as usize
);
let positions = vertices
.iter()
.map(|vertex| vertex.decode_position())
.collect::<Vec<[f32; 3]>>();
unsafe {
ffi::meshopt_partitionClusters(
destination.as_mut_ptr(),
cluster_indices.as_ptr(),
cluster_indices.len(),
cluster_index_counts.as_ptr(),
cluster_index_counts.len(),
positions.as_ptr().cast(),
positions.len(),
mem::size_of::<f32>() * 3,
target_partition_size,
)
}
}
pub fn compute_cluster_bounds(indices: &[u32], vertices: &VertexDataAdapter<'_>) -> Bounds {
unsafe {
ffi::meshopt_computeClusterBounds(
indices.as_ptr(),
indices.len(),
vertices.pos_ptr(),
vertices.vertex_count,
vertices.vertex_stride,
)
}
}
pub struct PositionDataAdapter<'a> {
pub data: &'a [u8],
pub position_count: usize,
pub position_stride: usize,
pub position_offset: usize,
}
pub struct RadiusDataAdapter<'a> {
pub data: &'a [u8],
pub radius_count: usize,
pub radius_stride: usize,
pub radius_offset: usize,
}
pub struct Sphere {
pub center: [f32; 3],
pub radius: f32,
}
pub fn compute_sphere_bounds(
positions: PositionDataAdapter<'_>,
radius: Option<RadiusDataAdapter<'_>>,
) -> Sphere {
if let Some(ref r) = radius {
assert_eq!(positions.position_count, r.radius_count);
assert!(r.data.len() >= r.radius_count * r.radius_stride);
assert!(r.radius_offset + mem::size_of::<f32>() <= r.radius_stride);
}
assert!(positions.data.len() >= positions.position_count * positions.position_stride);
assert!(positions.position_offset + mem::size_of::<f32>() * 3 <= positions.position_stride);
unsafe {
let (radius_ptr, radius_stride) = match radius {
Some(r) => (r.data.as_ptr().add(r.radius_offset).cast(), r.radius_stride),
None => (std::ptr::null(), 0),
};
let bounds = ffi::meshopt_computeSphereBounds(
positions
.data
.as_ptr()
.add(positions.position_offset)
.cast(),
positions.position_count,
positions.position_stride,
radius_ptr,
radius_stride,
);
Sphere {
center: [bounds.center[0], bounds.center[1], bounds.center[2]],
radius: bounds.radius,
}
}
}
pub fn compute_cluster_bounds_decoder<T: DecodePosition>(
indices: &[u32],
vertices: &[T],
) -> Bounds {
let vertices = vertices
.iter()
.map(|vertex| vertex.decode_position())
.collect::<Vec<[f32; 3]>>();
unsafe {
ffi::meshopt_computeClusterBounds(
indices.as_ptr(),
indices.len(),
vertices.as_ptr().cast(),
vertices.len() * 3,
mem::size_of::<f32>() * 3,
)
}
}
pub fn compute_meshlet_bounds(meshlet: Meshlet<'_>, vertices: &VertexDataAdapter<'_>) -> Bounds {
unsafe {
ffi::meshopt_computeMeshletBounds(
meshlet.vertices.as_ptr(),
meshlet.triangles.as_ptr(),
meshlet.triangles.len() / 3,
vertices.pos_ptr(),
vertices.vertex_count,
vertices.vertex_stride,
)
}
}
pub fn compute_meshlet_bounds_decoder<T: DecodePosition>(
meshlet: Meshlet<'_>,
vertices: &[T],
) -> Bounds {
let vertices = vertices
.iter()
.map(|vertex| vertex.decode_position())
.collect::<Vec<[f32; 3]>>();
unsafe {
ffi::meshopt_computeMeshletBounds(
meshlet.vertices.as_ptr(),
meshlet.triangles.as_ptr(),
meshlet.triangles.len() / 3,
vertices.as_ptr().cast(),
vertices.len() * 3,
mem::size_of::<f32>() * 3,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::typed_to_bytes;
#[test]
fn test_cluster_sphere_bounds() {
let vbr: &[f32] = &[
0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 2.0, 1.0, 0.0, 1.0, 3.0,
];
let bounds = compute_sphere_bounds(
PositionDataAdapter {
data: typed_to_bytes(vbr),
position_count: 4,
position_stride: 4 * mem::size_of::<f32>(),
position_offset: 0,
},
None,
);
assert!(bounds.radius < 0.97);
let eps: &[f32] = &[1e-3, 2e-3, 3e-3, 4e-3];
let bounds = compute_sphere_bounds(
PositionDataAdapter {
data: typed_to_bytes(vbr),
position_count: 4,
position_stride: 4 * mem::size_of::<f32>(),
position_offset: 0,
},
Some(RadiusDataAdapter {
data: typed_to_bytes(eps),
radius_count: 4,
radius_stride: mem::size_of::<f32>(),
radius_offset: 0,
}),
);
assert!(bounds.radius < 0.87);
assert!((bounds.center[0] - 0.5).abs() < 1e-2);
assert!((bounds.center[1] - 0.5).abs() < 1e-2);
assert!((bounds.center[2] - 0.5).abs() < 1e-2);
let bounds = compute_sphere_bounds(
PositionDataAdapter {
data: typed_to_bytes(vbr),
position_count: 4,
position_stride: 4 * mem::size_of::<f32>(),
position_offset: 0,
},
Some(RadiusDataAdapter {
data: typed_to_bytes(vbr),
radius_count: 4,
radius_stride: 4 * mem::size_of::<f32>(),
radius_offset: 3 * mem::size_of::<f32>(),
}),
);
assert!((bounds.radius - 3.0).abs() < 1e-2);
assert!((bounds.center[0] - 1.0).abs() < 1e-2);
assert!((bounds.center[1] - 0.0).abs() < 1e-2);
assert!((bounds.center[2] - 1.0).abs() < 1e-2);
}
#[test]
fn test_partition_basic() {
let cluster_indices: &[u32] = &[
0, 1, 3, 4, 5, 6, 1, 2, 3, 6, 7, 8, 4, 5, 6, 9, 10, 11, 6, 7, 8, 9, 11, 12, ];
let cluster_index_counts: &[u32] = &[6, 6, 6, 6];
let mut partitions = vec![0u32; cluster_index_counts.len()];
assert_eq!(
partition_clusters(
&mut partitions,
cluster_indices,
cluster_index_counts,
13,
1
),
4
);
assert_eq!(partitions, [0, 1, 2, 3]);
assert_eq!(
partition_clusters(
&mut partitions,
cluster_indices,
cluster_index_counts,
13,
2
),
2
);
assert_eq!(partitions, [0, 0, 1, 1]);
assert_eq!(
partition_clusters(
&mut partitions,
cluster_indices,
cluster_index_counts,
13,
4
),
1
);
assert_eq!(partitions, [0, 0, 0, 0]);
}
#[test]
fn test_partition_with_positions() {
let cluster_indices: &[u32] = &[
0, 1, 3, 4, 5, 6, 1, 2, 3, 6, 7, 8, 4, 5, 6, 9, 10, 11, 6, 7, 8, 9, 11, 12, ];
let cluster_index_counts: &[u32] = &[6, 6, 6, 6];
let mut partitions = vec![0u32; cluster_index_counts.len()];
let vertices: &[f32] = &[
0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 2.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 2.0, 0.0, 1.0, 2.0, 0.0, 2.0, 2.0, 0.0, 3.0, 2.0, 0.0, 4.0, 2.0, 0.0, 2.0, 3.0, 0.0, 0.0, 4.0, 0.0, 1.0, 4.0, 0.0, 2.0, 4.0, 0.0, ];
let vertex_adapter =
VertexDataAdapter::new(typed_to_bytes(vertices), 3 * mem::size_of::<f32>(), 0).unwrap();
let result = partition_clusters_with_positions(
&mut partitions,
cluster_indices,
cluster_index_counts,
&vertex_adapter,
2,
);
assert_eq!(result, 2);
assert_eq!(partitions, [0, 1, 0, 1]);
}
#[test]
fn test_partition_with_decoder() {
use crate::Vertex;
let cluster_indices: &[u32] = &[
0, 1, 3, 4, 5, 6, 1, 2, 3, 6, 7, 8, 4, 5, 6, 9, 10, 11, 6, 7, 8, 9, 11, 12,
];
let cluster_index_counts: &[u32] = &[6, 6, 6, 6];
let mut partitions = vec![0u32; cluster_index_counts.len()];
let vertices: Vec<Vertex> = vec![
Vertex {
p: [0.0, 0.0, 0.0],
..Default::default()
}, Vertex {
p: [1.0, 0.0, 0.0],
..Default::default()
}, Vertex {
p: [2.0, 0.0, 0.0],
..Default::default()
}, Vertex {
p: [1.0, 1.0, 0.0],
..Default::default()
}, Vertex {
p: [0.0, 2.0, 0.0],
..Default::default()
}, Vertex {
p: [1.0, 2.0, 0.0],
..Default::default()
}, Vertex {
p: [2.0, 2.0, 0.0],
..Default::default()
}, Vertex {
p: [3.0, 2.0, 0.0],
..Default::default()
}, Vertex {
p: [4.0, 2.0, 0.0],
..Default::default()
}, Vertex {
p: [2.0, 3.0, 0.0],
..Default::default()
}, Vertex {
p: [0.0, 4.0, 0.0],
..Default::default()
}, Vertex {
p: [1.0, 4.0, 0.0],
..Default::default()
}, Vertex {
p: [2.0, 4.0, 0.0],
..Default::default()
}, ];
let result = partition_clusters_with_decoder(
&mut partitions,
cluster_indices,
cluster_index_counts,
&vertices,
2,
);
assert_eq!(result, 2);
assert_eq!(partitions, [0, 1, 0, 1]);
}
#[test]
fn test_meshlets_flex() {
let vb: &[f32] = &[
0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 10.0, 0.0, 0.0, 11.0, 0.0, 0.0, 10.0, 1.0, 0.0, 10.0, 0.0, 1.0,
];
let ib: &[u32] = &[
0, 1, 2, 0, 2, 3, 0, 3, 1, 1, 3, 2, 4, 5, 6, 4, 6, 7, 4, 7, 5, 5, 7, 6,
];
let vertices =
VertexDataAdapter::new(typed_to_bytes(vb), mem::size_of::<[f32; 3]>(), 0).unwrap();
assert_eq!(
unsafe { ffi::meshopt_buildMeshletsBound(ib.len(), 16, 4) },
2
);
let meshlets = build_meshlets_flex(ib, &vertices, 16, 4, 8, 0.0, 0.0);
assert_eq!(meshlets.len(), 1);
assert_eq!(meshlets.meshlets[0].triangle_count, 8);
assert_eq!(meshlets.meshlets[0].vertex_count, 8);
let meshlets = build_meshlets_flex(ib, &vertices, 16, 4, 8, 0.0, 10.0);
assert_eq!(meshlets.len(), 1);
assert_eq!(meshlets.meshlets[0].triangle_count, 8);
assert_eq!(meshlets.meshlets[0].vertex_count, 8);
let meshlets = build_meshlets_flex(ib, &vertices, 16, 4, 8, 0.0, 1.0);
assert_eq!(meshlets.len(), 2);
assert_eq!(meshlets.meshlets[0].triangle_count, 4);
assert_eq!(meshlets.meshlets[0].vertex_count, 4);
assert_eq!(meshlets.meshlets[1].triangle_count, 4);
assert_eq!(meshlets.meshlets[1].vertex_count, 4);
let meshlets = build_meshlets_flex(ib, &vertices, 16, 4, 8, -1.0, 10.0);
assert_eq!(meshlets.len(), 1);
assert_eq!(meshlets.meshlets[0].triangle_count, 8);
assert_eq!(meshlets.meshlets[0].vertex_count, 8);
let meshlets = build_meshlets_flex(ib, &vertices, 16, 4, 8, -1.0, 1.0);
assert_eq!(meshlets.len(), 2);
assert_eq!(meshlets.meshlets[0].triangle_count, 4);
assert_eq!(meshlets.meshlets[0].vertex_count, 4);
assert_eq!(meshlets.meshlets[1].triangle_count, 4);
assert_eq!(meshlets.meshlets[1].vertex_count, 4);
}
#[test]
fn test_meshlets_spatial() {
let vb: &[f32] = &[
0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 10.0, 0.0, 0.0, 11.0, 0.0,
0.0, 10.0, 1.0, 0.0, 10.0, 0.0, 1.0,
];
let ib: &[u32] = &[
0, 1, 2, 0, 2, 3, 0, 3, 1, 1, 3, 2, 4, 5, 6, 4, 6, 7, 4, 7, 5, 5, 7, 6,
];
let vertices =
VertexDataAdapter::new(typed_to_bytes(vb), 3 * mem::size_of::<f32>(), 0).unwrap();
assert_eq!(
unsafe { ffi::meshopt_buildMeshletsBound(ib.len(), 16, 4) },
2
);
let meshlets = build_meshlets_spatial(ib, &vertices, 16, 8, 8, 0.0);
assert_eq!(meshlets.len(), 1);
assert_eq!(meshlets.meshlets[0].triangle_count, 8);
assert_eq!(meshlets.meshlets[0].vertex_count, 8);
let meshlets = build_meshlets_spatial(ib, &vertices, 16, 4, 4, 0.0);
assert_eq!(meshlets.len(), 2);
assert_eq!(meshlets.meshlets[0].triangle_count, 4);
assert_eq!(meshlets.meshlets[0].vertex_count, 4);
assert_eq!(meshlets.meshlets[1].triangle_count, 4);
assert_eq!(meshlets.meshlets[1].vertex_count, 4);
let meshlets = build_meshlets_spatial(ib, &vertices, 4, 4, 8, 0.0);
assert_eq!(meshlets.len(), 2);
assert_eq!(meshlets.meshlets[0].triangle_count, 4);
assert_eq!(meshlets.meshlets[0].vertex_count, 4);
assert_eq!(meshlets.meshlets[1].triangle_count, 4);
assert_eq!(meshlets.meshlets[1].vertex_count, 4);
}
}