meshopt/
simplify.rs

1use crate::{ffi, DecodePosition, VertexDataAdapter};
2use bitflags::bitflags;
3use std::mem;
4
5bitflags! {
6    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
7    pub struct SimplifyOptions : u32 {
8        const None = 0;
9        /// Locks the vertices that lie on the topological border of the mesh in place such that
10        /// they don't move during simplification.
11        /// This can be valuable to simplify independent chunks of a mesh, for example terrain,
12        /// to ensure that individual levels of detail can be stitched together later without gaps.
13        const LockBorder = 1;
14        /// Improve simplification performance assuming input indices are a sparse subset of the mesh.
15        /// Note that error becomes relative to subset extents.
16        const Sparse = 2;
17        /// Treat error limit and resulting error as absolute instead of relative to mesh extents.
18        const ErrorAbsolute = 4;
19        /// Remove disconnected parts of the mesh during simplification incrementally, regardless of
20        /// the topological restrictions inside components.
21        const Prune = 8;
22        /// Produce more regular triangle sizes and shapes during simplification, at some cost to
23        /// geometric quality.
24        const Regularize = 16;
25        /// Allow collapses across attribute discontinuities, except for vertices that are tagged with meshopt_SimplifyVertex_Protect in vertex_lock.
26        const Permissive = 32;
27    }
28}
29
30/// Reduces the number of triangles in the mesh, attempting to preserve mesh
31/// appearance as much as possible.
32///
33/// The resulting index buffer references vertices from the original vertex buffer.
34///
35/// If the original vertex data isn't required, creating a compact vertex buffer
36/// using `optimize_vertex_fetch` is recommended.
37pub fn simplify(
38    indices: &[u32],
39    vertices: &VertexDataAdapter<'_>,
40    target_count: usize,
41    target_error: f32,
42    options: SimplifyOptions,
43    result_error: Option<&mut f32>,
44) -> Vec<u32> {
45    let mut result: Vec<u32> = vec![0; indices.len()];
46    let index_count = unsafe {
47        ffi::meshopt_simplify(
48            result.as_mut_ptr().cast(),
49            indices.as_ptr().cast(),
50            indices.len(),
51            vertices.pos_ptr(),
52            vertices.vertex_count,
53            vertices.vertex_stride,
54            target_count,
55            target_error,
56            options.bits(),
57            result_error.map_or_else(std::ptr::null_mut, |v| v as *mut _),
58        )
59    };
60    result.resize(index_count, 0u32);
61    result
62}
63
64/// Reduces the number of triangles in the mesh, attempting to preserve mesh
65/// appearance as much as possible.
66///
67/// The resulting index buffer references vertices from the original vertex buffer.
68///
69/// If the original vertex data isn't required, creating a compact vertex buffer
70/// using `optimize_vertex_fetch` is recommended.
71pub fn simplify_decoder<T: DecodePosition>(
72    indices: &[u32],
73    vertices: &[T],
74    target_count: usize,
75    target_error: f32,
76    options: SimplifyOptions,
77    result_error: Option<&mut f32>,
78) -> Vec<u32> {
79    let positions = vertices
80        .iter()
81        .map(|vertex| vertex.decode_position())
82        .collect::<Vec<[f32; 3]>>();
83    let mut result: Vec<u32> = vec![0; indices.len()];
84    let index_count = unsafe {
85        ffi::meshopt_simplify(
86            result.as_mut_ptr().cast(),
87            indices.as_ptr().cast(),
88            indices.len(),
89            positions.as_ptr().cast(),
90            positions.len(),
91            mem::size_of::<f32>() * 3,
92            target_count,
93            target_error,
94            options.bits(),
95            result_error.map_or_else(std::ptr::null_mut, |v| v as *mut _),
96        )
97    };
98    result.resize(index_count, 0u32);
99    result
100}
101
102/// Reduces the number of triangles in the mesh, attempting to preserve mesh
103/// appearance as much as possible, while respecting the given vertex locks
104///
105/// The resulting index buffer references vertices from the original vertex buffer.
106///
107/// If the original vertex data isn't required, creating a compact vertex buffer
108/// using `optimize_vertex_fetch` is recommended.
109pub fn simplify_with_locks(
110    indices: &[u32],
111    vertices: &VertexDataAdapter<'_>,
112    vertex_lock: &[bool],
113    target_count: usize,
114    target_error: f32,
115    options: SimplifyOptions,
116    result_error: Option<&mut f32>,
117) -> Vec<u32> {
118    let mut result: Vec<u32> = vec![0; indices.len()];
119    let index_count = unsafe {
120        ffi::meshopt_simplifyWithAttributes(
121            result.as_mut_ptr().cast(),
122            indices.as_ptr().cast(),
123            indices.len(),
124            vertices.pos_ptr(),
125            vertices.vertex_count,
126            vertices.vertex_stride,
127            std::ptr::null(),
128            0,
129            std::ptr::null(),
130            0,
131            vertex_lock.as_ptr().cast(),
132            target_count,
133            target_error,
134            options.bits(),
135            result_error.map_or_else(std::ptr::null_mut, |v| v as *mut _),
136        )
137    };
138    result.resize(index_count, 0u32);
139    result
140}
141
142/// Reduces the number of triangles in the mesh, attempting to preserve mesh
143/// appearance as much as possible, while respecting the given vertex locks
144///
145/// The resulting index buffer references vertices from the original vertex buffer.
146///
147/// If the original vertex data isn't required, creating a compact vertex buffer
148/// using `optimize_vertex_fetch` is recommended.
149pub fn simplify_with_locks_decoder<T: DecodePosition>(
150    indices: &[u32],
151    vertices: &[T],
152    vertex_lock: &[bool],
153    target_count: usize,
154    target_error: f32,
155    options: SimplifyOptions,
156    result_error: Option<&mut f32>,
157) -> Vec<u32> {
158    let positions = vertices
159        .iter()
160        .map(|vertex| vertex.decode_position())
161        .collect::<Vec<[f32; 3]>>();
162    let mut result: Vec<u32> = vec![0; indices.len()];
163    let index_count = unsafe {
164        ffi::meshopt_simplifyWithAttributes(
165            result.as_mut_ptr().cast(),
166            indices.as_ptr().cast(),
167            indices.len(),
168            positions.as_ptr().cast(),
169            positions.len(),
170            mem::size_of::<f32>() * 3,
171            std::ptr::null(),
172            0,
173            std::ptr::null(),
174            0,
175            vertex_lock.as_ptr().cast(),
176            target_count,
177            target_error,
178            options.bits(),
179            result_error.map_or_else(std::ptr::null_mut, |v| v as *mut _),
180        )
181    };
182    result.resize(index_count, 0u32);
183    result
184}
185
186/// Reduces the number of triangles in the mesh, attempting to preserve mesh
187/// appearance as much as possible, weighing vertex attributes by the supplied weights,
188/// while respecting the given vertex locks
189///
190/// The resulting index buffer references vertices from the original vertex buffer.
191///
192/// If the original vertex data isn't required, creating a compact vertex buffer
193/// using `optimize_vertex_fetch` is recommended.
194#[allow(clippy::too_many_arguments)]
195pub fn simplify_with_attributes_and_locks(
196    indices: &[u32],
197    vertices: &VertexDataAdapter<'_>,
198    vertex_attributes: &[f32],
199    vertex_attribute_weights: &[f32],
200    vertex_attributes_stride: usize,
201    vertex_lock: &[bool],
202    target_count: usize,
203    target_error: f32,
204    options: SimplifyOptions,
205    result_error: Option<&mut f32>,
206) -> Vec<u32> {
207    let mut result: Vec<u32> = vec![0; indices.len()];
208    let index_count = unsafe {
209        ffi::meshopt_simplifyWithAttributes(
210            result.as_mut_ptr().cast(),
211            indices.as_ptr().cast(),
212            indices.len(),
213            vertices.pos_ptr(),
214            vertices.vertex_count,
215            vertices.vertex_stride,
216            vertex_attributes.as_ptr(),
217            vertex_attributes_stride,
218            vertex_attribute_weights.as_ptr(),
219            vertex_attribute_weights.len(),
220            vertex_lock.as_ptr().cast(),
221            target_count,
222            target_error,
223            options.bits(),
224            result_error.map_or_else(std::ptr::null_mut, |v| v as *mut _),
225        )
226    };
227    result.resize(index_count, 0u32);
228    result
229}
230
231/// Reduces the number of triangles in the mesh, attempting to preserve mesh
232/// appearance as much as possible, weighing vertex attributes by the supplied weights,
233/// while respecting the given vertex locks
234///
235/// The resulting index buffer references vertices from the original vertex buffer.
236///
237/// If the original vertex data isn't required, creating a compact vertex buffer
238/// using `optimize_vertex_fetch` is recommended.
239#[allow(clippy::too_many_arguments)]
240pub fn simplify_with_attributes_and_locks_decoder<T: DecodePosition>(
241    indices: &[u32],
242    vertices: &[T],
243    vertex_attributes: &[f32],
244    vertex_attribute_weights: &[f32],
245    vertex_attributes_stride: usize,
246    vertex_lock: &[bool],
247    target_count: usize,
248    target_error: f32,
249    options: SimplifyOptions,
250    result_error: Option<&mut f32>,
251) -> Vec<u32> {
252    let positions = vertices
253        .iter()
254        .map(|vertex| vertex.decode_position())
255        .collect::<Vec<[f32; 3]>>();
256    let mut result: Vec<u32> = vec![0; indices.len()];
257    let index_count = unsafe {
258        ffi::meshopt_simplifyWithAttributes(
259            result.as_mut_ptr().cast(),
260            indices.as_ptr().cast(),
261            indices.len(),
262            positions.as_ptr().cast(),
263            positions.len(),
264            mem::size_of::<f32>() * 3,
265            vertex_attributes.as_ptr(),
266            vertex_attributes_stride,
267            vertex_attribute_weights.as_ptr(),
268            vertex_attribute_weights.len(),
269            vertex_lock.as_ptr().cast(),
270            target_count,
271            target_error,
272            options.bits(),
273            result_error.map_or_else(std::ptr::null_mut, |v| v as *mut _),
274        )
275    };
276    result.resize(index_count, 0u32);
277    result
278}
279
280/// Reduces the number of triangles in the mesh, sacrificing mesh appearance for simplification performance.
281///
282/// The algorithm doesn't preserve mesh topology but is always able to reach target triangle count.
283///
284/// The resulting index buffer references vertices from the original vertex buffer.
285///
286/// If the original vertex data isn't required, creating a compact vertex buffer using `optimize_vertex_fetch`
287/// is recommended.
288pub fn simplify_sloppy(
289    indices: &[u32],
290    vertices: &VertexDataAdapter<'_>,
291    target_count: usize,
292    target_error: f32,
293    result_error: Option<&mut f32>,
294) -> Vec<u32> {
295    let mut result: Vec<u32> = vec![0; indices.len()];
296    let index_count = unsafe {
297        ffi::meshopt_simplifySloppy(
298            result.as_mut_ptr().cast(),
299            indices.as_ptr().cast(),
300            indices.len(),
301            vertices.pos_ptr(),
302            vertices.vertex_count,
303            vertices.vertex_stride,
304            std::ptr::null(),
305            target_count,
306            target_error,
307            result_error.map_or_else(std::ptr::null_mut, |v| v as *mut _),
308        )
309    };
310    result.resize(index_count, 0u32);
311    result
312}
313
314/// Reduces the number of triangles in the mesh, sacrificing mesh appearance for simplification performance.
315///
316/// The algorithm doesn't preserve mesh topology but is always able to reach target triangle count.
317///
318/// The resulting index buffer references vertices from the original vertex buffer.
319///
320/// If the original vertex data isn't required, creating a compact vertex buffer using `optimize_vertex_fetch`
321/// is recommended.
322pub fn simplify_sloppy_decoder<T: DecodePosition>(
323    indices: &[u32],
324    vertices: &[T],
325    target_count: usize,
326    target_error: f32,
327    result_error: Option<&mut f32>,
328) -> Vec<u32> {
329    let positions = vertices
330        .iter()
331        .map(|vertex| vertex.decode_position())
332        .collect::<Vec<[f32; 3]>>();
333    let mut result: Vec<u32> = vec![0; indices.len()];
334    let index_count = unsafe {
335        ffi::meshopt_simplifySloppy(
336            result.as_mut_ptr().cast(),
337            indices.as_ptr().cast(),
338            indices.len(),
339            positions.as_ptr().cast(),
340            positions.len(),
341            mem::size_of::<f32>() * 3,
342            std::ptr::null(),
343            target_count,
344            target_error,
345            result_error.map_or_else(std::ptr::null_mut, |v| v as *mut _),
346        )
347    };
348    result.resize(index_count, 0u32);
349    result
350}
351
352/// Reduces the number of triangles in the mesh, sacrificing mesh appearance for simplification performance,
353/// while respecting the given vertex locks.
354///
355/// The algorithm doesn't preserve mesh topology but is always able to reach target triangle count.
356///
357/// The resulting index buffer references vertices from the original vertex buffer.
358///
359/// If the original vertex data isn't required, creating a compact vertex buffer using `optimize_vertex_fetch`
360/// is recommended.
361pub fn simplify_sloppy_with_locks(
362    indices: &[u32],
363    vertices: &VertexDataAdapter<'_>,
364    vertex_lock: &[bool],
365    target_count: usize,
366    target_error: f32,
367    result_error: Option<&mut f32>,
368) -> Vec<u32> {
369    let positions = vertices.pos_ptr();
370    let mut result: Vec<u32> = vec![0; indices.len()];
371    let index_count = unsafe {
372        ffi::meshopt_simplifySloppy(
373            result.as_mut_ptr().cast(),
374            indices.as_ptr().cast(),
375            indices.len(),
376            positions.cast(),
377            vertices.vertex_count,
378            vertices.vertex_stride,
379            vertex_lock.as_ptr().cast(),
380            target_count,
381            target_error,
382            result_error.map_or_else(std::ptr::null_mut, |v| v as *mut _),
383        )
384    };
385    result.resize(index_count, 0u32);
386    result
387}
388
389/// Reduces the number of triangles in the mesh, sacrificing mesh appearance for simplification performance,
390/// while respecting the given vertex locks.
391///
392/// The algorithm doesn't preserve mesh topology but is always able to reach target triangle count.
393///
394/// The resulting index buffer references vertices from the original vertex buffer.
395///
396/// If the original vertex data isn't required, creating a compact vertex buffer using `optimize_vertex_fetch`
397/// is recommended.
398pub fn simplify_sloppy_with_locks_decoder<T: DecodePosition>(
399    indices: &[u32],
400    vertices: &[T],
401    vertex_lock: &[bool],
402    target_count: usize,
403    target_error: f32,
404    result_error: Option<&mut f32>,
405) -> Vec<u32> {
406    let positions = vertices
407        .iter()
408        .map(|vertex| vertex.decode_position())
409        .collect::<Vec<[f32; 3]>>();
410    let mut result: Vec<u32> = vec![0; indices.len()];
411    let index_count = unsafe {
412        ffi::meshopt_simplifySloppy(
413            result.as_mut_ptr().cast(),
414            indices.as_ptr().cast(),
415            indices.len(),
416            positions.as_ptr().cast(),
417            positions.len(),
418            mem::size_of::<f32>() * 3,
419            vertex_lock.as_ptr().cast(),
420            target_count,
421            target_error,
422            result_error.map_or_else(std::ptr::null_mut, |v| v as *mut _),
423        )
424    };
425    result.resize(index_count, 0u32);
426    result
427}
428
429/// Returns the error scaling factor used by the simplifier to convert between absolute and relative extents
430///
431/// Absolute error must be *divided* by the scaling factor before passing it to `simplify` as `target_error`
432/// Relative error returned by `simplify` via `result_error` must be *multiplied* by the scaling factor to get absolute error.
433pub fn simplify_scale(vertices: &VertexDataAdapter<'_>) -> f32 {
434    unsafe {
435        ffi::meshopt_simplifyScale(
436            vertices.pos_ptr(),
437            vertices.vertex_count,
438            vertices.vertex_stride,
439        )
440    }
441}
442
443/// Returns the error scaling factor used by the simplifier to convert between absolute and relative extents
444///
445/// Absolute error must be *divided* by the scaling factor before passing it to `simplify` as `target_error`
446/// Relative error returned by `simplify` via `result_error` must be *multiplied* by the scaling factor to get absolute error.
447pub fn simplify_scale_decoder<T: DecodePosition>(vertices: &[T]) -> f32 {
448    let positions = vertices
449        .iter()
450        .map(|vertex| vertex.decode_position())
451        .collect::<Vec<[f32; 3]>>();
452
453    unsafe {
454        ffi::meshopt_simplifyScale(
455            positions.as_ptr().cast(),
456            positions.len(),
457            mem::size_of::<f32>() * 3,
458        )
459    }
460}
461
462#[cfg(test)]
463mod tests {
464    use super::*;
465    use crate::typed_to_bytes;
466
467    #[test]
468    fn test_simplify_sloppy_with_locks() {
469        // Test mesh from vendor tests - triangle fan with spine
470        // 0
471        // 1 2
472        // 3 4 5
473        let indices = &[
474            0, 2, 1, // triangle 0
475            1, 2, 3, // triangle 1
476            3, 2, 4, // triangle 2
477            2, 5, 4, // triangle 3
478        ];
479
480        let vertices: &[f32] = &[
481            0.0, 4.0, 0.0, // vertex 0
482            0.0, 1.0, 0.0, // vertex 1
483            2.0, 2.0, 0.0, // vertex 2
484            0.0, 0.0, 0.0, // vertex 3
485            1.0, 0.0, 0.0, // vertex 4
486            4.0, 0.0, 0.0, // vertex 5
487        ];
488
489        // Lock the spine vertices (0, 2, 5)
490        let vertex_locks = &[true, false, true, false, false, true];
491
492        let vertices_adapter =
493            VertexDataAdapter::new(typed_to_bytes(vertices), 3 * mem::size_of::<f32>(), 0).unwrap();
494
495        let mut result_error = 0.0f32;
496        let result = simplify_sloppy_with_locks(
497            indices,
498            &vertices_adapter,
499            vertex_locks,
500            3, // target 3 indices (1 triangle)
501            1.0,
502            Some(&mut result_error),
503        );
504
505        // Should preserve 6 indices (2 triangles) because of locks
506        assert_eq!(result.len(), 6);
507        assert_eq!(result_error, 0.0);
508
509        // Expected result based on vendor test
510        let expected = &[0, 2, 1, 1, 2, 5];
511        assert_eq!(result, expected);
512    }
513}