use criterion::{Criterion, criterion_group, criterion_main};
use glam::Vec3;
use landmark::{RecastBuilder, RecastConfig};
use landmark_common::TriMesh;
use waymark::{NavMesh, NavMeshFlags, NavMeshParams, NavMeshQuery, PolyRef, QueryFilter};
const TEST_DATA: &str = "../../test-data/meshes";
fn build_navmesh(name: &str) -> NavMesh {
let path = format!("{TEST_DATA}/{name}");
let mesh = TriMesh::from_obj(&path).expect("failed to load test mesh");
let (bmin, bmax) = mesh.calculate_bounds();
let mut config = RecastConfig::default();
config.cs = 0.3;
config.ch = 0.2;
config.walkable_slope_angle = 45.0;
config.walkable_height = 2;
config.walkable_climb = 1;
config.walkable_radius = 1;
config.max_edge_len = 12;
config.max_simplification_error = 1.3;
config.min_region_area = 8;
config.merge_region_area = 20;
config.max_vertices_per_polygon = 6;
config.detail_sample_dist = 6.0;
config.detail_sample_max_error = 1.0;
config.calculate_grid_size(bmin, bmax);
let builder = RecastBuilder::new(config);
let (poly_mesh, detail_mesh) = builder
.build_mesh(&mesh.vertices, &mesh.indices)
.expect("failed to build mesh");
let mut params = NavMeshParams::default();
params.origin = [bmin.x, bmin.y, bmin.z];
params.tile_width = bmax.x - bmin.x;
params.tile_height = bmax.z - bmin.z;
params.max_tiles = 1;
params.max_polys_per_tile = poly_mesh.poly_count() as i32;
NavMesh::build_from_recast(params, &poly_mesh, &detail_mesh, NavMeshFlags::empty())
.expect("failed to build navmesh")
}
fn snap_to_navmesh(nav_mesh: &NavMesh, pos: [f32; 3]) -> (PolyRef, [f32; 3]) {
let query = NavMeshQuery::new(nav_mesh);
let filter = QueryFilter::default();
let extent = Vec3::new(5.0, 10.0, 5.0);
let (poly_ref, snapped) = query
.find_nearest_poly(Vec3::from(pos), extent, &filter)
.expect("position not on navmesh");
(poly_ref, snapped.to_array())
}
fn bench_find_path(c: &mut Criterion) {
let mut group = c.benchmark_group("find_path");
let nav_mesh = build_navmesh("nav_test.obj");
let (start_ref, start_pos) = snap_to_navmesh(&nav_mesh, [0.0, 0.0, 0.0]);
let (end_ref, end_pos) = snap_to_navmesh(&nav_mesh, [16.0, 0.0, -7.0]);
group.bench_function("nav_test", |b| {
let mut query = NavMeshQuery::new(&nav_mesh);
let filter = QueryFilter::default();
b.iter(|| {
query
.find_path(
start_ref,
end_ref,
Vec3::from(start_pos),
Vec3::from(end_pos),
&filter,
)
.unwrap()
});
});
group.finish();
}
fn bench_find_straight_path(c: &mut Criterion) {
let mut group = c.benchmark_group("find_straight_path");
let nav_mesh = build_navmesh("nav_test.obj");
let (start_ref, start_pos) = snap_to_navmesh(&nav_mesh, [0.0, 0.0, 0.0]);
let (end_ref, end_pos) = snap_to_navmesh(&nav_mesh, [16.0, 0.0, -7.0]);
let mut query = NavMeshQuery::new(&nav_mesh);
let filter = QueryFilter::default();
let poly_path = query
.find_path(
start_ref,
end_ref,
Vec3::from(start_pos),
Vec3::from(end_pos),
&filter,
)
.expect("failed to find path");
group.bench_function("nav_test", |b| {
let query = NavMeshQuery::new(&nav_mesh);
b.iter(|| {
query
.find_straight_path(Vec3::from(start_pos), Vec3::from(end_pos), &poly_path)
.unwrap()
});
});
group.finish();
}
fn bench_sliced_find_path(c: &mut Criterion) {
let mut group = c.benchmark_group("sliced_find_path");
let nav_mesh = build_navmesh("nav_test.obj");
let (start_ref, start_pos) = snap_to_navmesh(&nav_mesh, [0.0, 0.0, 0.0]);
let (end_ref, end_pos) = snap_to_navmesh(&nav_mesh, [16.0, 0.0, -7.0]);
group.bench_function("nav_test", |b| {
let mut query = NavMeshQuery::new(&nav_mesh);
let filter = QueryFilter::default();
b.iter(|| {
query
.init_sliced_find_path(
start_ref,
end_ref,
Vec3::from(start_pos),
Vec3::from(end_pos),
&filter,
0,
)
.unwrap();
loop {
let (_, state) = query.update_sliced_find_path(100).unwrap();
if state != waymark::SlicedPathState::InProgress {
break;
}
}
query.finalize_sliced_find_path(256).unwrap()
});
});
group.finish();
}
criterion_group!(
benches,
bench_find_path,
bench_find_straight_path,
bench_sliced_find_path
);
criterion_main!(benches);