Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ If you want to use the original C++ implementation from Rust, check out the [mes

## Features

`meshoptimizer` v0.16 feature level is the current support target. Planned but currently missing features:
`meshoptimizer` v0.17 feature level is the current support target. Planned but currently missing features:
- [SIMD support](https://github.com/yzsolt/meshopt-rs/issues/1)
- [WASM support](https://github.com/yzsolt/meshopt-rs/issues/2)

Expand Down
8 changes: 0 additions & 8 deletions benches/with_input.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};

use meshopt_rs::Stream;
#[cfg(feature = "experimental")]
use meshopt_rs::cluster::{Meshlet, build_meshlets, build_meshlets_bound, compute_meshlet_bounds};
use meshopt_rs::index::IndexEncodingVersion;
use meshopt_rs::index::buffer::{decode_index_buffer, encode_index_buffer, encode_index_buffer_bound};
use meshopt_rs::index::generator::{
generate_shadow_index_buffer, generate_vertex_remap, remap_index_buffer, remap_vertex_buffer,
};
#[cfg(feature = "experimental")]
use meshopt_rs::index::sequence::{decode_index_sequence, encode_index_sequence, encode_index_sequence_bound};
use meshopt_rs::overdraw::optimize_overdraw;
#[cfg(feature = "experimental")]
use meshopt_rs::stripify::{stripify, stripify_bound};
use meshopt_rs::vertex::Position;
use meshopt_rs::vertex::cache::{optimize_vertex_cache, optimize_vertex_cache_fifo, optimize_vertex_cache_strip};
Expand Down Expand Up @@ -180,9 +177,6 @@ fn with_input(c: &mut Criterion) {
let copystrip_vertices = copystrip.vertices.clone();
optimize_vertex_fetch(&mut copystrip.vertices, &mut copystrip.indices, &copystrip_vertices);

// stripify

#[cfg(feature = "experimental")]
c.bench_with_input(BenchmarkId::new("build_meshlets", input_name), &copy, |b, mesh| {
const MAX_VERTICES: usize = 64;
const MAX_TRIANGLES: usize = 124;
Expand All @@ -207,7 +201,6 @@ fn with_input(c: &mut Criterion) {
});
});

#[cfg(feature = "experimental")]
c.bench_with_input(
BenchmarkId::new("compute_meshlet_bounds", input_name),
&copy,
Expand Down Expand Up @@ -286,7 +279,6 @@ fn with_input(c: &mut Criterion) {
}
group.finish();

#[cfg(feature = "experimental")]
{
let mut group = c.benchmark_group("index-sequence-encoding");

Expand Down
8 changes: 4 additions & 4 deletions bin/codec_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,19 +145,19 @@ fn bench_filters(
for _ in 0..10 {
let t0 = Instant::now();

decode_filter_oct_8(&mut d4);
decode_filter_oct_8(d4.iter_mut());

let t1 = Instant::now();

decode_filter_oct_16(&mut d8);
decode_filter_oct_16(d8.iter_mut());

let t2 = Instant::now();

decode_filter_quat(&mut d8);
decode_filter_quat(d8.iter_mut());

let t3 = Instant::now();

decode_filter_exp(&mut d32);
decode_filter_exp(d32.iter_mut());

let t4 = Instant::now();

Expand Down
19 changes: 6 additions & 13 deletions examples/demo.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#![allow(clippy::identity_op)]

use meshopt_rs::cluster::*;
use meshopt_rs::index::buffer::*;
use meshopt_rs::index::generator::*;
use meshopt_rs::index::sequence::*;
use meshopt_rs::index::*;
use meshopt_rs::overdraw::*;
use meshopt_rs::quantize::*;
Expand All @@ -13,7 +15,7 @@ use meshopt_rs::vertex::*;
use meshopt_rs::{INVALID_INDEX, Stream};

#[cfg(feature = "experimental")]
use meshopt_rs::{cluster::*, index::sequence::*, simplify::*, spatial_order::*};
use meshopt_rs::{simplify::*, spatial_order::*};

use std::env;
use std::fmt::Debug;
Expand Down Expand Up @@ -465,7 +467,6 @@ fn encode_index(mesh: &Mesh, desc: char) {
);
}

#[cfg(feature = "experimental")]
fn encode_index_sequence1(data: &[u32], vertex_count: usize, desc: char) {
// allocate result outside of the timing loop to exclude memset() from decode timing
let mut result = vec![0; data.len()];
Expand Down Expand Up @@ -885,7 +886,6 @@ fn shadow(mesh: &Mesh) {
);
}

#[cfg(feature = "experimental")]
fn meshlets(mesh: &Mesh, scan: bool) {
const MAX_VERTICES: usize = 64;
const MAX_TRIANGLES: usize = 124; // NVidia-recommended 126, rounded down to a multiple of 4
Expand Down Expand Up @@ -1222,7 +1222,6 @@ where
Ok(())
}

#[cfg(feature = "experimental")]
fn tessellation_adjacency(mesh: &Mesh) {
let start = Instant::now();

Expand Down Expand Up @@ -1277,14 +1276,10 @@ fn process(mesh: &Mesh) {
stripify_mesh(&copy, true, 'R');
stripify_mesh(&copystrip, true, 'S');

#[cfg(feature = "experimental")]
{
meshlets(&copy, false);
meshlets(&copy, true);
}
meshlets(&copy, false);
meshlets(&copy, true);

shadow(&copy);
#[cfg(feature = "experimental")]
tessellation_adjacency(&copy);

encode_index(&copy, ' ');
Expand All @@ -1294,7 +1289,6 @@ fn process(mesh: &Mesh) {
let size = stripify(&mut strip, &copystrip.indices, copystrip.vertices.len(), 0);
strip.resize(size, 0);

#[cfg(feature = "experimental")]
encode_index_sequence1(&strip, copystrip.vertices.len(), 'D');

pack_vertex(&copy);
Expand All @@ -1313,8 +1307,7 @@ fn process(mesh: &Mesh) {
}
}

fn process_dev(#[allow(unused)] mesh: &Mesh) {
#[cfg(feature = "experimental")]
fn process_dev(mesh: &Mesh) {
tessellation_adjacency(mesh);
}

Expand Down
2 changes: 1 addition & 1 deletion src/cluster.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! **Experimental** meshlet building and cluster bounds generation
//! Meshlet building and cluster bounds generation

use crate::quantize::quantize_snorm;
use crate::util::zero_inverse;
Expand Down
19 changes: 19 additions & 0 deletions src/hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use std::hash::{BuildHasherDefault, Hasher};

#[derive(Default)]
pub struct NoopHasher {
state: u32,
}

impl Hasher for NoopHasher {
fn write(&mut self, bytes: &[u8]) {
debug_assert_eq!(bytes.len(), 4);
self.state = u32::from_ne_bytes(bytes.try_into().unwrap());
}

fn finish(&self) -> u64 {
self.state as u64
}
}

pub type BuildNoopHasher = BuildHasherDefault<NoopHasher>;
84 changes: 27 additions & 57 deletions src/index/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
// This work is based on:
// John McDonald, Mark Kilgard. Crack-Free Point-Normal Triangles using Adjacent Edge Normals. 2010

use crate::hash::BuildNoopHasher;
use crate::{INVALID_INDEX, Stream};

use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::hash::{BuildHasherDefault, Hasher};
use std::hash::{BuildHasherDefault, Hash, Hasher};

#[derive(Default)]
struct VertexHasher {
Expand All @@ -22,8 +23,8 @@ impl Hasher for VertexHasher {

let mut h = self.state;

for k4 in bytes.chunks_exact(4) {
let mut k = u32::from_ne_bytes(k4.try_into().unwrap());
for k4 in bytes.as_chunks().0 {
let mut k = u32::from_ne_bytes(*k4);

k = k.wrapping_mul(M);
k ^= k >> R;
Expand All @@ -43,53 +44,29 @@ impl Hasher for VertexHasher {

type BuildVertexHasher = BuildHasherDefault<VertexHasher>;

#[cfg(feature = "experimental")]
mod experimental {
use super::*;
use std::hash::Hash;

#[derive(PartialEq, Eq)]
pub struct Edge(pub (u32, u32));

impl Hash for Edge {
fn hash<H: Hasher>(&self, state: &mut H) {
const M: u32 = 0x5bd1e995;

let edge = self.0;
let mut h1 = edge.0;
let mut h2 = edge.1;

// MurmurHash64B finalizer
h1 ^= h2 >> 18;
h1 = h1.wrapping_mul(M);
h2 ^= h1 >> 22;
h2 = h2.wrapping_mul(M);
h1 ^= h2 >> 17;
h1 = h1.wrapping_mul(M);
h2 ^= h1 >> 19;
h2 = h2.wrapping_mul(M);

state.write_u32(h2);
}
}
#[derive(PartialEq, Eq)]
pub struct Edge(pub (u32, u32));

#[derive(Default)]
pub struct NoopEdgeHasher {
state: u32,
}

impl Hasher for NoopEdgeHasher {
fn write(&mut self, bytes: &[u8]) {
debug_assert_eq!(bytes.len(), 4);
self.state = u32::from_ne_bytes(bytes.try_into().unwrap());
}
impl Hash for Edge {
fn hash<H: Hasher>(&self, state: &mut H) {
const M: u32 = 0x5bd1e995;

fn finish(&self) -> u64 {
self.state as u64
}
let edge = self.0;
let mut h1 = edge.0;
let mut h2 = edge.1;

// MurmurHash64B finalizer
h1 ^= h2 >> 18;
h1 = h1.wrapping_mul(M);
h2 ^= h1 >> 22;
h2 = h2.wrapping_mul(M);
h1 ^= h2 >> 17;
h1 = h1.wrapping_mul(M);
h2 ^= h1 >> 19;
h2 = h2.wrapping_mul(M);

state.write_u32(h2);
}

pub type BuildNoopEdgeHasher = BuildHasherDefault<NoopEdgeHasher>;
}

fn generate_vertex_remap_inner<Vertex, Lookup>(
Expand Down Expand Up @@ -298,7 +275,6 @@ pub fn generate_shadow_index_buffer_multi(destination: &mut [u32], indices: &[u3
})
}

#[cfg(feature = "experimental")]
fn build_position_remap(indices: &[u32], vertices: &Stream) -> Vec<u32> {
let mut table = HashMap::with_capacity_and_hasher(vertices.len(), BuildVertexHasher::default());
let mut remap = vec![INVALID_INDEX; vertices.len()];
Expand Down Expand Up @@ -331,10 +307,7 @@ fn build_position_remap(indices: &[u32], vertices: &Stream) -> Vec<u32> {
/// # Arguments
///
/// * `destination`: must contain enough space for the resulting index buffer (`indices.len() * 4` elements)
#[cfg(feature = "experimental")]
pub fn generate_tessellation_index_buffer(destination: &mut [u32], indices: &[u32], vertices: &Stream) {
use experimental::*;

assert_eq!(indices.len() % 3, 0);
assert!(destination.len() >= indices.len() * 4);

Expand All @@ -344,7 +317,7 @@ pub fn generate_tessellation_index_buffer(destination: &mut [u32], indices: &[u3
let remap = build_position_remap(indices, vertices);

// build edge set; this stores all triangle edges but we can look these up by any other wedge
let mut edge_table = HashMap::with_capacity_and_hasher(indices.len(), BuildNoopEdgeHasher::default());
let mut edge_table = HashMap::with_capacity_and_hasher(indices.len(), BuildNoopHasher::default());

for i in indices.chunks_exact(3) {
for e in 0..3 {
Expand Down Expand Up @@ -400,10 +373,7 @@ pub fn generate_tessellation_index_buffer(destination: &mut [u32], indices: &[u3
/// # Arguments
///
/// * `destination`: must contain enough space for the resulting index buffer (`indices.len() * 2` elements)
#[cfg(feature = "experimental")]
pub fn generate_adjacency_index_buffer(destination: &mut [u32], indices: &[u32], vertices: &Stream) {
use experimental::*;

assert_eq!(indices.len() % 3, 0);
assert!(destination.len() >= indices.len() * 2);

Expand All @@ -413,7 +383,7 @@ pub fn generate_adjacency_index_buffer(destination: &mut [u32], indices: &[u32],
let remap = build_position_remap(indices, vertices);

// build edge set; this stores all triangle edges but we can look these up by any other wedge
let mut edge_vertex_table = HashMap::with_capacity_and_hasher(indices.len(), BuildNoopEdgeHasher::default());
let mut edge_vertex_table = HashMap::with_capacity_and_hasher(indices.len(), BuildNoopHasher::default());

for i in indices.chunks_exact(3) {
for e in 0..3 {
Expand Down Expand Up @@ -450,7 +420,7 @@ pub fn generate_adjacency_index_buffer(destination: &mut [u32], indices: &[u32],
}
}

#[cfg(all(test, feature = "experimental"))]
#[cfg(test)]
mod test {
use super::*;

Expand Down
19 changes: 2 additions & 17 deletions src/index/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
pub mod buffer;
pub mod generator;
#[cfg(feature = "experimental")]
pub mod sequence;

use crate::util::{read_byte, write_byte};
Expand All @@ -15,33 +14,19 @@ pub enum DecodeError {
UnexpectedEof,
}

#[derive(Default)]
pub enum IndexEncodingVersion {
/// Decodable by all versions
V0,
/// Decodable by 0.14+
#[cfg(feature = "experimental")]
#[default]
V1,
}

#[allow(clippy::derivable_impls)]
impl Default for IndexEncodingVersion {
fn default() -> Self {
#[cfg(not(feature = "experimental"))]
{
Self::V0
}
#[cfg(feature = "experimental")]
{
Self::V1
}
}
}

impl From<IndexEncodingVersion> for u8 {
fn from(value: IndexEncodingVersion) -> u8 {
match value {
IndexEncodingVersion::V0 => 0,
#[cfg(feature = "experimental")]
IndexEncodingVersion::V1 => 1,
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/index/sequence.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! **Experimental** index sequence encoding and decoding
//! Index sequence encoding and decoding

use std::io::Write;

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
#![allow(clippy::identity_op)]
#![allow(clippy::erasing_op)]

#[cfg(feature = "experimental")]
pub mod cluster;
mod hash;
pub mod index;
pub mod overdraw;
pub mod quantize;
Expand Down
Loading