1#![cfg_attr(test, allow(unused, unused_comparisons))]
9
10#[cfg(feature = "serialization")]
11pub mod binary_format;
12pub mod bvh_tree;
13pub mod detour_common;
14pub mod detour_math;
15pub mod dt_status;
16pub mod error;
17mod hierarchical_pathfinding;
18pub mod nav_mesh;
19mod nav_mesh_builder;
20mod nav_mesh_query;
21pub mod node_pool;
22mod path_queue;
23pub mod poly_query;
24pub mod raycast_hit;
25mod sliced_pathfinding;
26mod status;
27
28#[cfg(test)]
29mod advanced_navigation_function_tests;
30#[cfg(test)]
31mod concurrency_safety_tests;
32#[cfg(all(test, feature = "serialization"))]
33mod cross_platform_serialization_tests;
34#[cfg(test)]
35mod detour_common_geometry_tests;
36#[cfg(test)]
37mod detour_edge_cases_tests;
38#[cfg(test)]
39mod detour_math_operations_tests;
40#[cfg(test)]
41mod detour_memory_scalability_tests;
42#[cfg(test)]
43mod detour_multi_tile_tests;
44#[cfg(test)]
45mod detour_off_mesh_connection_tests;
46#[cfg(test)]
47mod detour_parameter_validation_tests;
48#[cfg(test)]
49mod detour_query_filter_tests;
50#[cfg(test)]
51mod detour_raycast_comprehensive_tests;
52#[cfg(test)]
53mod detour_spatial_query_tests;
54#[cfg(test)]
55mod platform_compatibility_tests;
56#[cfg(test)]
57mod test_mesh_helpers;
58
59use glam::Vec3;
60
61pub use dt_status::{
62 DtStatus, dt_status_detail, dt_status_failed, dt_status_in_progress, dt_status_succeed,
63};
64pub use error::DetourError;
65pub use hierarchical_pathfinding::{
66 Cluster, ClusterConnection, HierarchicalConfig, HierarchicalPath, HierarchicalPathfinder,
67 Portal,
68};
69pub use nav_mesh::{
70 BVNode, Link, MeshTile, NavMesh, OffMeshConnection, Poly, PolyDetail, TileHeader,
71 decode_poly_ref, encode_poly_ref,
72};
73pub use nav_mesh_builder::{ExternalLinkRequest, NavMeshBuilder};
74pub use nav_mesh_query::{MoveAlongSurfaceResult, NavMeshQuery, Node, NodeState};
75pub use node_pool::{DT_NULL_IDX, DtNode, DtNodePool, DtNodeQueue, NodeFlags, NodeIndex};
76pub use path_queue::{DT_PATHQ_INVALID, DtPathQueue, DtPathQueueRef};
77pub use poly_query::{CollectPolysQuery, FindNearestPolyQuery, PolyQuery};
78pub use raycast_hit::{RaycastHit, RaycastOptions, RaycastResult};
79pub use sliced_pathfinding::{
80 SlicedPathConfig, SlicedPathState, SlicedPathfindingQuery, find_path_sliced,
81};
82pub use status::Status;
83
84pub const DT_EXT_LINK: u16 = 0x8000;
86
87#[derive(Debug, Clone)]
89#[non_exhaustive]
90#[cfg_attr(
91 feature = "serialization",
92 derive(serde::Serialize, serde::Deserialize)
93)]
94pub struct NavMeshParams {
95 pub origin: [f32; 3],
97 pub tile_width: f32,
99 pub tile_height: f32,
100 pub max_tiles: i32,
102 pub max_polys_per_tile: i32,
104}
105
106#[derive(Debug, Clone)]
108#[non_exhaustive]
109#[cfg_attr(
110 feature = "serialization",
111 derive(serde::Serialize, serde::Deserialize)
112)]
113pub struct NavMeshCreateParams {
114 pub nav_mesh_params: NavMeshParams,
116 pub verts: Vec<f32>,
118 pub vert_count: i32,
120 pub polys: Vec<u16>,
122 pub poly_flags: Vec<PolyFlags>,
124 pub poly_areas: Vec<u8>,
126 pub poly_count: i32,
128 pub nvp: i32,
130 pub detail_meshes: Vec<u32>,
132 pub detail_verts: Vec<f32>,
134 pub detail_vert_count: i32,
136 pub detail_tris: Vec<u8>,
138 pub detail_tri_count: i32,
140
141 pub off_mesh_con_verts: Vec<f32>,
144 pub off_mesh_con_rad: Vec<f32>,
146 pub off_mesh_con_flags: Vec<PolyFlags>,
148 pub off_mesh_con_areas: Vec<u8>,
150 pub off_mesh_con_dir: Vec<u8>,
152 pub off_mesh_con_user_id: Vec<u32>,
154 pub off_mesh_con_count: i32,
156
157 pub bmin: [f32; 3],
159 pub bmax: [f32; 3],
160 pub walkable_height: f32,
162 pub walkable_radius: f32,
164 pub walkable_climb: f32,
166 pub cs: f32,
168 pub ch: f32,
170 pub build_bv_tree: bool,
172}
173
174impl Default for NavMeshParams {
175 fn default() -> Self {
176 Self {
177 origin: [0.0, 0.0, 0.0],
178 tile_width: 0.0,
179 tile_height: 0.0,
180 max_tiles: 0,
181 max_polys_per_tile: 0,
182 }
183 }
184}
185
186impl Default for NavMeshCreateParams {
187 fn default() -> Self {
188 Self {
189 nav_mesh_params: NavMeshParams::default(),
190 verts: Vec::new(),
191 vert_count: 0,
192 polys: Vec::new(),
193 poly_flags: Vec::new(),
194 poly_areas: Vec::new(),
195 poly_count: 0,
196 nvp: 6,
197 detail_meshes: Vec::new(),
198 detail_verts: Vec::new(),
199 detail_vert_count: 0,
200 detail_tris: Vec::new(),
201 detail_tri_count: 0,
202 off_mesh_con_verts: Vec::new(),
203 off_mesh_con_rad: Vec::new(),
204 off_mesh_con_flags: Vec::new(),
205 off_mesh_con_areas: Vec::new(),
206 off_mesh_con_dir: Vec::new(),
207 off_mesh_con_user_id: Vec::new(),
208 off_mesh_con_count: 0,
209 bmin: [0.0, 0.0, 0.0],
210 bmax: [0.0, 0.0, 0.0],
211 walkable_height: 0.0,
212 walkable_radius: 0.0,
213 walkable_climb: 0.0,
214 cs: 0.3,
215 ch: 0.2,
216 build_bv_tree: true,
217 }
218 }
219}
220
221impl NavMeshCreateParams {
222 pub fn with_walkable_height(mut self, height: f32) -> Self {
224 self.walkable_height = height;
225 self
226 }
227
228 pub fn with_walkable_radius(mut self, radius: f32) -> Self {
230 self.walkable_radius = radius;
231 self
232 }
233
234 pub fn with_walkable_climb(mut self, climb: f32) -> Self {
236 self.walkable_climb = climb;
237 self
238 }
239
240 pub fn with_cell_size(mut self, cs: f32) -> Self {
242 self.cs = cs;
243 self
244 }
245
246 pub fn with_cell_height(mut self, ch: f32) -> Self {
248 self.ch = ch;
249 self
250 }
251
252 pub fn with_bounds(mut self, bmin: [f32; 3], bmax: [f32; 3]) -> Self {
254 self.bmin = bmin;
255 self.bmax = bmax;
256 self
257 }
258
259 pub fn with_build_bv_tree(mut self, build: bool) -> Self {
261 self.build_bv_tree = build;
262 self
263 }
264
265 pub fn with_nav_mesh_params(mut self, params: NavMeshParams) -> Self {
267 self.nav_mesh_params = params;
268 self
269 }
270
271 pub fn build(self) -> Result<Self, DetourError> {
277 self.validate()?;
278 Ok(self)
279 }
280
281 pub fn validate(&self) -> Result<(), DetourError> {
283 if self.cs <= 0.0 || self.ch <= 0.0 {
284 return Err(DetourError::InvalidParam);
285 }
286
287 if self.vert_count > 0 && self.verts.len() != self.vert_count as usize * 3 {
288 return Err(DetourError::InvalidParam);
289 }
290
291 if self.poly_count > 0 {
292 let expected_poly_data = self.poly_count as usize * 2 * self.nvp as usize;
293 if self.polys.len() < expected_poly_data {
294 return Err(DetourError::InvalidParam);
295 }
296 }
297
298 if self.detail_tri_count > 0 && self.detail_tris.len() < self.detail_tri_count as usize * 4
299 {
300 return Err(DetourError::InvalidParam);
301 }
302
303 if self.off_mesh_con_count > 0 {
304 let n = self.off_mesh_con_count as usize;
305 if self.off_mesh_con_verts.len() < n * 6 || self.off_mesh_con_rad.len() < n {
306 return Err(DetourError::InvalidParam);
307 }
308 }
309
310 Ok(())
311 }
312}
313
314bitflags::bitflags! {
315 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
317 #[cfg_attr(feature = "serialization", derive(serde::Serialize, serde::Deserialize))]
318 pub struct NavMeshFlags: u16 {
319 const OFF_MESH_CONNECTIONS = 0x01;
321 }
322}
323
324#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
326#[cfg_attr(
327 feature = "serialization",
328 derive(serde::Serialize, serde::Deserialize)
329)]
330#[repr(u8)]
331pub enum PolyType {
332 Ground = 0,
334 OffMeshConnection = 1,
336}
337
338bitflags::bitflags! {
339 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
341 #[cfg_attr(feature = "serialization", derive(serde::Serialize, serde::Deserialize))]
342 pub struct PolyFlags: u16 {
343 const WALK = 0x01;
345 const SWIM = 0x02;
347 const DOOR = 0x04;
349 const JUMP = 0x08;
351 const DISABLED = 0x10;
353 const CLIMB = 0x20;
355 }
356}
357
358bitflags::bitflags! {
359 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
361 #[cfg_attr(feature = "serialization", derive(serde::Serialize, serde::Deserialize))]
362 pub struct QueryFilterFlags: u16 {
363 const WALK = 0x01;
365 const SWIM = 0x02;
367 const DOOR = 0x04;
369 const JUMP = 0x08;
371 const DISABLED = 0x10;
373 const CLIMB = 0x20;
375 }
376}
377
378#[derive(Debug, Clone)]
380#[cfg_attr(
381 feature = "serialization",
382 derive(serde::Serialize, serde::Deserialize)
383)]
384pub struct QueryFilter {
385 pub flags: QueryFilterFlags,
387 pub area_cost: [f32; 32],
389 pub include_flags: PolyFlags,
391 pub exclude_flags: PolyFlags,
393}
394
395impl Default for QueryFilter {
396 fn default() -> Self {
397 let mut area_cost = [1.0; 32];
398
399 area_cost[0] = 1.0; area_cost[1] = 2.0; area_cost[2] = 5.0; Self {
406 flags: QueryFilterFlags::WALK,
407 area_cost,
408 include_flags: PolyFlags::WALK,
409 exclude_flags: PolyFlags::DISABLED,
410 }
411 }
412}
413
414impl QueryFilter {
415 pub fn pass_filter(
417 &self,
418 _poly_ref: PolyRef,
419 _tile: &nav_mesh::MeshTile,
420 poly: &nav_mesh::Poly,
421 ) -> bool {
422 let include = if self.include_flags.is_empty() {
424 true
425 } else {
426 (poly.flags & self.include_flags) != PolyFlags::empty()
427 };
428
429 let exclude = (poly.flags & self.exclude_flags) != PolyFlags::empty();
431
432 include && !exclude
434 }
435
436 #[allow(clippy::too_many_arguments)]
438 pub fn get_cost(
439 &self,
440 pa: &[f32; 3],
441 pb: &[f32; 3],
442 _prev_ref: PolyRef,
443 _prev_tile: Option<&nav_mesh::MeshTile>,
444 _prev_poly: Option<&nav_mesh::Poly>,
445 _cur_ref: PolyRef,
446 _cur_tile: &nav_mesh::MeshTile,
447 cur_poly: &nav_mesh::Poly,
448 _next_ref: PolyRef,
449 _next_tile: Option<&nav_mesh::MeshTile>,
450 _next_poly: Option<&nav_mesh::Poly>,
451 ) -> f32 {
452 let dx = pb[0] - pa[0];
454 let dy = pb[1] - pa[1];
455 let dz = pb[2] - pa[2];
456 let dist = (dx * dx + dy * dy + dz * dz).sqrt();
457
458 let area = cur_poly.area.min(31) as usize;
460
461 dist * self.area_cost[area]
463 }
464}
465
466#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
468#[cfg_attr(
469 feature = "serialization",
470 derive(serde::Serialize, serde::Deserialize)
471)]
472pub struct PolyRef(u32);
473
474impl PolyRef {
475 pub fn new(id: u32) -> Self {
477 Self(id)
478 }
479
480 pub fn id(&self) -> u32 {
482 self.0
483 }
484
485 pub fn is_valid(&self) -> bool {
487 self.0 != 0
488 }
489}
490
491impl From<u32> for PolyRef {
492 fn from(id: u32) -> Self {
493 Self(id)
494 }
495}
496
497impl From<PolyRef> for u32 {
498 fn from(reference: PolyRef) -> Self {
499 reference.0
500 }
501}
502
503pub const MAX_VERTS_PER_POLY: usize = 6;
505
506#[derive(Debug, Clone)]
508#[cfg_attr(
509 feature = "serialization",
510 derive(serde::Serialize, serde::Deserialize)
511)]
512pub struct Path {
513 pub poly_refs: Vec<PolyRef>,
515 pub waypoints: Vec<Vec3>,
517}
518
519impl Default for Path {
520 fn default() -> Self {
521 Self::new()
522 }
523}
524
525impl Path {
526 pub fn new() -> Self {
528 Self {
529 poly_refs: Vec::new(),
530 waypoints: Vec::new(),
531 }
532 }
533
534 pub fn poly_count(&self) -> usize {
536 self.poly_refs.len()
537 }
538
539 pub fn waypoint_count(&self) -> usize {
541 self.waypoints.len()
542 }
543
544 pub fn is_empty(&self) -> bool {
546 self.poly_refs.is_empty()
547 }
548
549 pub fn clear(&mut self) {
551 self.poly_refs.clear();
552 self.waypoints.clear();
553 }
554}