meshopt 0.6.2

Rust ffi bindings and idiomatic wrapper for mesh optimizer
Documentation
use crate::{ffi, DecodePosition, VertexDataAdapter, VertexStream};
use std::mem;

/// Generates a vertex remap table from the vertex buffer and an optional index buffer and returns number of unique vertices.
///
/// As a result, all vertices that are binary equivalent map to the same (new) location, with no gaps in the resulting sequence.
/// Resulting remap table maps old vertices to new vertices and can be used in `remap_vertex_buffer`/`remap_index_buffer`.
///
/// The `indices` can be `None` if the input is unindexed.
pub fn generate_vertex_remap<T>(vertices: &[T], indices: Option<&[u32]>) -> (usize, Vec<u32>) {
    let mut remap: Vec<u32> = vec![0; vertices.len()];
    let vertex_count = unsafe {
        match indices {
            Some(indices) => ffi::meshopt_generateVertexRemap(
                remap.as_mut_ptr().cast(),
                indices.as_ptr().cast(),
                indices.len(),
                vertices.as_ptr().cast(),
                vertices.len(),
                mem::size_of::<T>(),
            ),
            None => ffi::meshopt_generateVertexRemap(
                remap.as_mut_ptr(),
                std::ptr::null(),
                vertices.len(),
                vertices.as_ptr().cast(),
                vertices.len(),
                mem::size_of::<T>(),
            ),
        }
    };
    (vertex_count, remap)
}

/// Generates a vertex remap table from multiple vertex streams and an optional index buffer and returns number of unique vertices.
///
/// As a result, all vertices that are binary equivalent map to the same (new) location, with no gaps in the resulting sequence.
/// Resulting remap table maps old vertices to new vertices and can be used in `remap_vertex_buffer`/`remap_index_buffer`.
///
/// To remap vertex buffers, you will need to call `remap_vertex_buffer` for each vertex stream.
///
/// The `indices` can be `None` if the input is unindexed.
pub fn generate_vertex_remap_multi(
    vertex_count: usize,
    streams: &[VertexStream<'_>],
    indices: Option<&[u32]>,
) -> (usize, Vec<u32>) {
    let streams: Vec<ffi::meshopt_Stream> = streams
        .iter()
        .map(|stream| ffi::meshopt_Stream {
            data: stream.data.cast(),
            size: stream.size,
            stride: stream.stride,
        })
        .collect();
    let mut remap: Vec<u32> = vec![0; vertex_count];
    let vertex_count = unsafe {
        match indices {
            Some(indices) => ffi::meshopt_generateVertexRemapMulti(
                remap.as_mut_ptr(),
                indices.as_ptr(),
                indices.len(),
                vertex_count,
                streams.as_ptr(),
                streams.len(),
            ),
            None => ffi::meshopt_generateVertexRemapMulti(
                remap.as_mut_ptr(),
                std::ptr::null(),
                vertex_count,
                vertex_count,
                streams.as_ptr(),
                streams.len(),
            ),
        }
    };
    (vertex_count, remap)
}

/// Generate index buffer from the source index buffer and remap table generated by `generate_vertex_remap`.
///
/// `indices` can be `None` if the input is unindexed.
pub fn remap_index_buffer(indices: Option<&[u32]>, vertex_count: usize, remap: &[u32]) -> Vec<u32> {
    let mut result: Vec<u32> = Vec::new();
    if let Some(indices) = indices {
        result.resize(indices.len(), 0u32);
        unsafe {
            ffi::meshopt_remapIndexBuffer(
                result.as_mut_ptr(),
                indices.as_ptr(),
                indices.len(),
                remap.as_ptr(),
            );
        }
    } else {
        result.resize(vertex_count, 0u32);
        unsafe {
            ffi::meshopt_remapIndexBuffer(
                result.as_mut_ptr(),
                std::ptr::null(),
                vertex_count,
                remap.as_ptr(),
            );
        }
    }

    result
}

/// Generates vertex buffer from the source vertex buffer and remap table generated by `generate_vertex_remap`.
pub fn remap_vertex_buffer<T: Clone + Default>(
    vertices: &[T],
    vertex_count: usize,
    remap: &[u32],
) -> Vec<T> {
    let mut result: Vec<T> = vec![T::default(); vertex_count];
    unsafe {
        ffi::meshopt_remapVertexBuffer(
            result.as_mut_ptr().cast(),
            vertices.as_ptr().cast(),
            vertices.len(),
            mem::size_of::<T>(),
            remap.as_ptr(),
        );
    }
    result
}

/// Generates a remap table that maps all vertices with the same position to the same (existing) index.
///
/// Similarly to `generate_shadow_indices`, this can be helpful to pre-process meshes for position-only rendering.
/// This can also be used to implement algorithms that require positional-only connectivity, such as hierarchical simplification.
pub fn generate_position_remap(vertices: &VertexDataAdapter<'_>) -> Vec<u32> {
    let mut remap: Vec<u32> = vec![0; vertices.vertex_count];
    unsafe {
        ffi::meshopt_generatePositionRemap(
            remap.as_mut_ptr(),
            vertices.pos_ptr(),
            vertices.vertex_count,
            vertices.vertex_stride,
        );
    }
    remap
}

/// Generates a remap table that maps all vertices with the same position to the same (existing) index.
///
/// Similarly to `generate_shadow_indices`, this can be helpful to pre-process meshes for position-only rendering.
/// This can also be used to implement algorithms that require positional-only connectivity, such as hierarchical simplification.
pub fn generate_position_remap_decoder<T: DecodePosition>(vertices: &[T]) -> Vec<u32> {
    let positions = vertices
        .iter()
        .map(|vertex| vertex.decode_position())
        .collect::<Vec<[f32; 3]>>();
    let mut remap: Vec<u32> = vec![0; positions.len()];
    unsafe {
        ffi::meshopt_generatePositionRemap(
            remap.as_mut_ptr(),
            positions.as_ptr().cast(),
            positions.len(),
            mem::size_of::<f32>() * 3,
        );
    }
    remap
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::typed_to_bytes;

    #[test]
    fn test_generate_position_remap() {
        let vertices: &[f32] = &[
            0.0, 0.0, 0.0, // vertex 0
            1.0, 0.0, 0.0, // vertex 1
            0.0, 0.0, 0.0, // vertex 2 (duplicate of 0)
            1.0, 0.0, -0.0, // vertex 3 (duplicate of 1, -0 == 0)
            2.0, 0.0, 0.0, // vertex 4
        ];

        let vertices_adapter =
            VertexDataAdapter::new(typed_to_bytes(vertices), 3 * mem::size_of::<f32>(), 0).unwrap();

        let remap = generate_position_remap(&vertices_adapter);

        let expected = vec![0, 1, 0, 1, 4];
        assert_eq!(remap, expected);
    }
}