1use std::cmp::Ordering;
7use std::collections::BinaryHeap;
8use std::f32;
9
10use super::nav_mesh::{MeshTile, Poly, decode_poly_id};
11use super::raycast_hit::{RaycastHit, RaycastOptions, RaycastResult};
12use super::sliced_pathfinding::SlicedPathState;
13use super::{NavMesh, Path, PolyFlags, PolyRef, PolyType, QueryFilter};
14use crate::error::DetourError;
15use glam::Vec3;
16
17const DT_MAX_NODES: usize = 4096;
19
20#[derive(Debug, Clone)]
22pub struct Node {
23 pub poly: PolyRef,
25 pub parent: Option<usize>,
27 pub g: f32,
29 pub h: f32,
31 pub f: f32,
33 pub state: NodeState,
35 #[allow(dead_code)]
37 pub index: usize,
38}
39
40impl Node {
41 fn new(poly: PolyRef, index: usize) -> Self {
43 Self {
44 poly,
45 parent: None,
46 g: 0.0,
47 h: 0.0,
48 f: 0.0,
49 state: NodeState::New,
50 index,
51 }
52 }
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum NodeState {
58 New,
60 Open,
62 Closed,
64}
65
66#[derive(Debug, Clone)]
68pub struct MoveAlongSurfaceResult {
69 pub position: Vec3,
71 pub visited: Vec<PolyRef>,
73}
74
75#[derive(Debug, Clone, Copy)]
77struct HeapNode {
78 index: usize,
80 f: f32,
82}
83
84impl PartialEq for HeapNode {
85 fn eq(&self, other: &Self) -> bool {
86 self.f == other.f
87 }
88}
89
90impl Eq for HeapNode {}
91
92impl PartialOrd for HeapNode {
93 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
94 Some(self.cmp(other))
95 }
96}
97
98impl Ord for HeapNode {
99 fn cmp(&self, other: &Self) -> Ordering {
100 match other.f.partial_cmp(&self.f) {
103 Some(ordering) => ordering,
104 None => {
105 if other.f.is_nan() && !self.f.is_nan() {
107 Ordering::Less
108 } else if !other.f.is_nan() && self.f.is_nan() {
109 Ordering::Greater
110 } else {
111 Ordering::Equal
112 }
113 }
114 }
115 }
116}
117
118#[derive(Debug)]
120pub struct NavMeshQuery<'a> {
121 nav_mesh: &'a NavMesh,
123 node_pool: Vec<Node>,
125 open_list: BinaryHeap<HeapNode>,
127 query_extent: [f32; 3],
129 random_seed: u32,
131 sliced_state: SlicedPathfindingState,
133}
134
135#[derive(Debug)]
137struct SlicedPathfindingState {
138 active: bool,
140 state: SlicedPathState,
142 start_ref: PolyRef,
144 end_ref: PolyRef,
146 start_pos: [f32; 3],
148 end_pos: [f32; 3],
150 filter: QueryFilter,
152 current_path: Vec<PolyRef>,
154 best_node_idx: usize,
156 best_node_cost: f32,
158}
159
160impl Default for SlicedPathfindingState {
161 fn default() -> Self {
162 Self {
163 active: false,
164 state: SlicedPathState::InProgress,
165 start_ref: PolyRef::new(0),
166 end_ref: PolyRef::new(0),
167 start_pos: [0.0; 3],
168 end_pos: [0.0; 3],
169 filter: QueryFilter::default(),
170 current_path: Vec::new(),
171 best_node_idx: 0,
172 best_node_cost: f32::MAX,
173 }
174 }
175}
176
177impl<'a> NavMeshQuery<'a> {
178 pub fn new(nav_mesh: &'a NavMesh) -> Self {
180 let mut node_pool = Vec::with_capacity(DT_MAX_NODES);
181 for i in 0..DT_MAX_NODES {
182 node_pool.push(Node::new(PolyRef::new(0), i));
183 }
184
185 Self {
186 nav_mesh,
187 node_pool,
188 open_list: BinaryHeap::new(),
189 query_extent: [2.0, 4.0, 2.0], random_seed: 1,
191 sliced_state: SlicedPathfindingState::default(),
192 }
193 }
194
195 pub fn set_query_extent(&mut self, extent: Vec3) {
196 self.query_extent = extent.to_array();
197 }
198
199 pub fn get_query_extent(&self) -> Vec3 {
200 Vec3::from(self.query_extent)
201 }
202
203 pub fn set_random_seed(&mut self, seed: u32) {
204 self.random_seed = seed;
205 }
206
207 fn next_random(&mut self) -> u32 {
209 self.random_seed = self
210 .random_seed
211 .wrapping_mul(1103515245)
212 .wrapping_add(12345);
213 self.random_seed
214 }
215
216 fn random_f32(&mut self) -> f32 {
218 (self.next_random() & 0x7FFFFFFF) as f32 / 2147483647.0
219 }
220
221 pub fn nav_mesh(&self) -> &NavMesh {
223 self.nav_mesh
224 }
225
226 pub fn find_nearest_poly(
228 &self,
229 center: Vec3,
230 half_extents: Vec3,
231 filter: &QueryFilter,
232 ) -> Result<(PolyRef, Vec3), DetourError> {
233 let center = center.to_array();
234 let half_extents = half_extents.to_array();
235 let bmin = [
237 center[0] - half_extents[0],
238 center[1] - half_extents[1],
239 center[2] - half_extents[2],
240 ];
241 let bmax = [
242 center[0] + half_extents[0],
243 center[1] + half_extents[1],
244 center[2] + half_extents[2],
245 ];
246
247 let polys = self.nav_mesh.query_polygons(&bmin, &bmax, filter)?;
249
250 let mut nearest_ref = PolyRef::new(0);
251 let mut nearest_point = [0.0; 3];
252 let mut nearest_distance_sqr = f32::MAX;
253
254 for poly_ref in polys {
255 let (closest_pt, is_over_poly) =
256 self.closest_point_on_poly(poly_ref, Vec3::from(center))?;
257 let closest_arr = closest_pt.to_array();
258
259 let diff = [
261 center[0] - closest_arr[0],
262 center[1] - closest_arr[1],
263 center[2] - closest_arr[2],
264 ];
265
266 let d = if is_over_poly {
267 let (_tile, _poly) = self.nav_mesh.get_tile_and_poly_by_ref(poly_ref)?;
270 let climb_height = 0.25; let height_diff = diff[1].abs() - climb_height;
274 if height_diff > 0.0 {
275 height_diff * height_diff
276 } else {
277 0.0
278 }
279 } else {
280 diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2]
282 };
283
284 if d < nearest_distance_sqr {
285 nearest_point = closest_arr;
286 nearest_distance_sqr = d;
287 nearest_ref = poly_ref;
288 }
289 }
290
291 if !nearest_ref.is_valid() {
292 return Err(DetourError::PathNotFound);
293 }
294
295 Ok((nearest_ref, Vec3::from(nearest_point)))
296 }
297
298 pub fn find_nearest_poly_extended(
313 &self,
314 center: Vec3,
315 half_extents: Vec3,
316 filter: &QueryFilter,
317 ) -> Result<(PolyRef, Vec3, bool), DetourError> {
318 let center = center.to_array();
319 let half_extents = half_extents.to_array();
320 let bmin = [
322 center[0] - half_extents[0],
323 center[1] - half_extents[1],
324 center[2] - half_extents[2],
325 ];
326 let bmax = [
327 center[0] + half_extents[0],
328 center[1] + half_extents[1],
329 center[2] + half_extents[2],
330 ];
331
332 let polys = self.nav_mesh.query_polygons(&bmin, &bmax, filter)?;
334
335 let mut nearest_ref = PolyRef::new(0);
336 let mut nearest_point = [0.0; 3];
337 let mut nearest_distance_sqr = f32::MAX;
338 let mut nearest_is_over_poly = false;
339
340 for poly_ref in polys {
341 let (closest_pt, is_over_poly) =
342 self.closest_point_on_poly(poly_ref, Vec3::from(center))?;
343 let closest_arr = closest_pt.to_array();
344
345 let diff = [
347 center[0] - closest_arr[0],
348 center[1] - closest_arr[1],
349 center[2] - closest_arr[2],
350 ];
351
352 let d = if is_over_poly {
353 let (_tile, _poly) = self.nav_mesh.get_tile_and_poly_by_ref(poly_ref)?;
356 let climb_height = 0.25; let height_diff = diff[1].abs() - climb_height;
360 if height_diff > 0.0 {
361 height_diff * height_diff
362 } else {
363 0.0
364 }
365 } else {
366 diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2]
368 };
369
370 if d < nearest_distance_sqr {
371 nearest_point = closest_arr;
372 nearest_distance_sqr = d;
373 nearest_ref = poly_ref;
374 nearest_is_over_poly = is_over_poly;
375 }
376 }
377
378 if !nearest_ref.is_valid() {
379 return Err(DetourError::PathNotFound);
380 }
381
382 Ok((nearest_ref, Vec3::from(nearest_point), nearest_is_over_poly))
383 }
384
385 pub fn find_path(
387 &mut self,
388 start_ref: PolyRef,
389 end_ref: PolyRef,
390 start_pos: Vec3,
391 end_pos: Vec3,
392 filter: &QueryFilter,
393 ) -> Result<Vec<PolyRef>, DetourError> {
394 let start_pos = start_pos.to_array();
395 let end_pos = end_pos.to_array();
396 if !self.nav_mesh.is_valid_poly_ref(start_ref) || !self.nav_mesh.is_valid_poly_ref(end_ref)
397 {
398 return Err(DetourError::InvalidParam);
399 }
400
401 if start_ref == end_ref {
403 return Ok(vec![start_ref]);
404 }
405
406 for node in &mut self.node_pool {
407 node.parent = None;
408 node.state = NodeState::New;
409 node.poly = PolyRef::new(0);
410 node.g = 0.0;
411 node.h = 0.0;
412 node.f = 0.0;
413 }
414 self.open_list.clear();
415
416 let start_node_idx = 0;
417 let start_h = self.heuristic(&start_pos, &end_pos);
418 {
419 let start_node = &mut self.node_pool[start_node_idx];
420 start_node.poly = start_ref;
421 start_node.state = NodeState::Open;
422 start_node.g = 0.0;
423 start_node.h = start_h;
424 start_node.f = start_h;
425 }
426
427 let start_f = self.node_pool[start_node_idx].f;
429 self.open_list.push(HeapNode {
430 index: start_node_idx,
431 f: start_f,
432 });
433
434 let goal_node_idx = 1;
436 self.node_pool[goal_node_idx].poly = end_ref;
437
438 let mut best_node_idx = start_node_idx;
440 let mut best_node_cost = self.node_pool[start_node_idx].h;
441
442 let mut _iterations = 0;
443 while let Some(HeapNode {
444 index: current_idx, ..
445 }) = self.open_list.pop()
446 {
447 _iterations += 1;
448 let current_node = &mut self.node_pool[current_idx];
449 current_node.state = NodeState::Closed;
450
451 if current_node.poly == end_ref {
453 best_node_idx = current_idx;
454 break;
455 }
456
457 if current_node.h < best_node_cost {
459 best_node_cost = current_node.h;
460 best_node_idx = current_idx;
461 }
462
463 self.expand_neighbors(current_idx, goal_node_idx, &end_pos, filter)?;
465 }
466
467 let mut path = Vec::new();
469 let mut current_idx = best_node_idx;
470
471 while current_idx != start_node_idx {
472 let current_node = &self.node_pool[current_idx];
473 path.push(current_node.poly);
474
475 if let Some(parent_idx) = current_node.parent {
476 current_idx = parent_idx;
477 } else {
478 return Err(DetourError::PathNotFound);
480 }
481 }
482
483 path.push(start_ref);
485
486 path.reverse();
488
489 Ok(path)
490 }
491
492 fn expand_neighbors(
494 &mut self,
495 current_idx: usize,
496 goal_idx: usize,
497 end_pos: &[f32; 3],
498 filter: &QueryFilter,
499 ) -> Result<(), DetourError> {
500 let current_poly = self.node_pool[current_idx].poly;
501 let current_parent = self.node_pool[current_idx].parent;
502
503 let (tile, poly) = self.nav_mesh.get_tile_and_poly_by_ref(current_poly)?;
505
506 let goal_poly = self.node_pool[goal_idx].poly;
508
509 let mut _neighbors_found = 0;
511
512 let mut link_idx = poly.first_link;
514 while let Some(idx) = link_idx {
515 let link = &tile.links[idx];
516 link_idx = link.next.map(|n| n as usize);
517
518 let neighbor_ref = link.reference;
519
520 if !self.nav_mesh.is_valid_poly_ref(neighbor_ref) {
522 continue;
523 }
524
525 if let Some(parent_idx) = current_parent {
527 if self.node_pool[parent_idx].poly == neighbor_ref {
528 continue;
529 }
530 }
531
532 if !self.pass_filter(neighbor_ref, tile, poly, filter)? {
534 continue;
535 }
536
537 _neighbors_found += 1;
538
539 let mut neighbor_idx = 0;
541 while neighbor_idx < DT_MAX_NODES {
542 if self.node_pool[neighbor_idx].state == NodeState::New
543 || self.node_pool[neighbor_idx].poly == neighbor_ref
544 {
545 break;
546 }
547 neighbor_idx += 1;
548 }
549
550 if neighbor_idx == DT_MAX_NODES {
552 return Err(DetourError::BufferTooSmall);
553 }
554
555 let neighbor_state = self.node_pool[neighbor_idx].state;
557 let neighbor_g = self.node_pool[neighbor_idx].g;
558 let neighbor_h = self.node_pool[neighbor_idx].h;
559
560 if neighbor_state == NodeState::New {
562 self.node_pool[neighbor_idx].poly = neighbor_ref;
563 }
564
565 if neighbor_state == NodeState::Closed {
567 continue;
568 }
569
570 let current_center = self.get_poly_center(current_poly)?.to_array();
572 let neighbor_center = match self.get_portal_points(current_poly, neighbor_ref) {
573 Ok((left, right)) => {
574 ((left + right) * 0.5).to_array()
576 }
577 Err(_) => {
578 self.get_poly_center(neighbor_ref)?.to_array()
580 }
581 };
582
583 let cost = self.get_edge_cost(
585 current_poly,
586 neighbor_ref,
587 ¤t_center,
588 &neighbor_center,
589 filter,
590 )?;
591
592 let current_g = self.node_pool[current_idx].g;
594 let new_g = current_g + cost;
595
596 if neighbor_state == NodeState::New || new_g < neighbor_g {
597 let h = if neighbor_state == NodeState::New {
599 if neighbor_ref == goal_poly {
600 0.0
601 } else {
602 let poly_center = self.get_poly_center(neighbor_ref)?.to_array();
603 self.heuristic(&poly_center, end_pos)
604 }
605 } else {
606 neighbor_h
607 };
608
609 {
611 let neighbor_node = &mut self.node_pool[neighbor_idx];
612 neighbor_node.parent = Some(current_idx);
613 neighbor_node.g = new_g;
614 neighbor_node.h = h;
615 neighbor_node.f = new_g + h;
616
617 if neighbor_state == NodeState::New {
619 neighbor_node.state = NodeState::Open;
620 }
621 }
622
623 if neighbor_state == NodeState::New {
625 self.open_list.push(HeapNode {
626 index: neighbor_idx,
627 f: new_g + h,
628 });
629 }
630 }
631 }
632
633 Ok(())
634 }
635
636 #[allow(dead_code)]
638 fn expand_off_mesh_connections(
639 &mut self,
640 current_idx: usize,
641 goal_idx: usize,
642 end_pos: &[f32; 3],
643 filter: &QueryFilter,
644 ) -> Result<(), DetourError> {
645 let current_node = &self.node_pool[current_idx];
646 let current_poly = current_node.poly;
647
648 let (tile, poly) = self.nav_mesh.get_tile_and_poly_by_ref(current_poly)?;
650 let goal_poly = self.node_pool[goal_idx].poly;
651
652 let all_tiles = self.nav_mesh.get_all_tiles();
654 for check_tile in all_tiles {
655 for connection in &check_tile.off_mesh_connections {
657 if !filter.include_flags.intersects(connection.flags)
659 || filter.exclude_flags.intersects(connection.flags)
660 {
661 continue;
662 }
663
664 let connection_ref = connection.poly;
665
666 if connection_ref == current_poly {
668 continue;
669 }
670
671 let start_pos = connection.start_pos();
673 let end_pos_conn = connection.end_pos();
674
675 let mut can_use_connection = false;
677
678 if self
682 .is_point_in_polygon(tile, poly, &start_pos)
683 .unwrap_or(false)
684 {
685 can_use_connection = connection.allows_start_to_end();
686 }
687 else if self
689 .is_point_in_polygon(tile, poly, &end_pos_conn)
690 .unwrap_or(false)
691 {
692 can_use_connection = connection.allows_end_to_start();
693 }
694 else {
696 for i in 0..poly.vert_count as usize {
697 let v_idx = poly.verts[i] as usize;
698 let vert = [
699 tile.verts[v_idx * 3],
700 tile.verts[v_idx * 3 + 1],
701 tile.verts[v_idx * 3 + 2],
702 ];
703
704 let dist_to_start = self.distance_3d(vert, start_pos);
705 let dist_to_end = self.distance_3d(vert, end_pos_conn);
706
707 if (dist_to_start <= connection.radius && connection.allows_start_to_end())
708 || (dist_to_end <= connection.radius
709 && connection.allows_end_to_start())
710 {
711 can_use_connection = true;
712 break;
713 }
714 }
715 }
716
717 if !can_use_connection {
718 continue;
719 }
720
721 let mut neighbor_idx = 0;
723 while neighbor_idx < DT_MAX_NODES {
724 if self.node_pool[neighbor_idx].state == NodeState::New
725 || self.node_pool[neighbor_idx].poly == connection_ref
726 {
727 break;
728 }
729 neighbor_idx += 1;
730 }
731
732 if neighbor_idx == DT_MAX_NODES {
734 continue;
735 }
736
737 let neighbor_state = self.node_pool[neighbor_idx].state;
739 let neighbor_g = self.node_pool[neighbor_idx].g;
740 let neighbor_h = self.node_pool[neighbor_idx].h;
741
742 if neighbor_state == NodeState::New {
744 self.node_pool[neighbor_idx].poly = connection_ref;
745 }
746
747 if neighbor_state == NodeState::Closed {
749 continue;
750 }
751
752 let (_from_pos, to_pos) = if can_use_connection && connection.allows_start_to_end()
754 {
755 (start_pos, end_pos_conn)
756 } else {
757 (end_pos_conn, start_pos)
758 };
759
760 let current_pos = self.get_poly_center(current_poly)?.to_array();
762
763 let cost = self.get_off_mesh_connection_cost(
765 current_poly,
766 connection_ref,
767 ¤t_pos,
768 &to_pos,
769 filter,
770 )?;
771
772 let current_g = self.node_pool[current_idx].g;
774 let new_g = current_g + cost;
775
776 if neighbor_state == NodeState::New || new_g < neighbor_g {
777 let h = if neighbor_state == NodeState::New {
779 if connection_ref == goal_poly {
780 0.0
781 } else {
782 self.heuristic(&to_pos, end_pos)
783 }
784 } else {
785 neighbor_h
786 };
787
788 {
790 let neighbor_node = &mut self.node_pool[neighbor_idx];
791 neighbor_node.parent = Some(current_idx);
792 neighbor_node.g = new_g;
793 neighbor_node.h = h;
794 neighbor_node.f = new_g + h;
795
796 if neighbor_state == NodeState::New {
798 neighbor_node.state = NodeState::Open;
799 }
800 }
801
802 if neighbor_state == NodeState::New {
804 self.open_list.push(HeapNode {
805 index: neighbor_idx,
806 f: new_g + h,
807 });
808 }
809 }
810 } } Ok(())
814 }
815
816 fn distance_3d(&self, a: [f32; 3], b: [f32; 3]) -> f32 {
818 let dx = b[0] - a[0];
819 let dy = b[1] - a[1];
820 let dz = b[2] - a[2];
821 (dx * dx + dy * dy + dz * dz).sqrt()
822 }
823
824 fn heuristic(&self, start: &[f32; 3], end: &[f32; 3]) -> f32 {
827 const H_SCALE: f32 = 0.999; let dx = end[0] - start[0];
829 let dy = end[1] - start[1];
830 let dz = end[2] - start[2];
831
832 (dx * dx + dy * dy + dz * dz).sqrt() * H_SCALE
833 }
834
835 fn get_edge_cost(
836 &self,
837 from_ref: PolyRef,
838 to_ref: PolyRef,
839 from_pos: &[f32; 3],
840 to_pos: &[f32; 3],
841 filter: &QueryFilter,
842 ) -> Result<f32, DetourError> {
843 if self.nav_mesh.is_off_mesh_connection(to_ref)
845 || self.nav_mesh.is_off_mesh_connection(from_ref)
846 {
847 return self.get_off_mesh_connection_cost(from_ref, to_ref, from_pos, to_pos, filter);
848 }
849
850 let (from_tile, from_poly) = self.nav_mesh.get_tile_and_poly_by_ref(from_ref)?;
852 let (to_tile, to_poly) = self.nav_mesh.get_tile_and_poly_by_ref(to_ref)?;
853
854 let cost = filter.get_cost(
856 from_pos,
857 to_pos,
858 PolyRef::new(0),
859 None,
860 None, from_ref,
862 from_tile,
863 from_poly, to_ref,
865 Some(to_tile),
866 Some(to_poly), );
868
869 Ok(cost)
870 }
871
872 fn get_off_mesh_connection_cost(
874 &self,
875 from_ref: PolyRef,
876 to_ref: PolyRef,
877 from_pos: &[f32; 3],
878 to_pos: &[f32; 3],
879 filter: &QueryFilter,
880 ) -> Result<f32, DetourError> {
881 let dx = to_pos[0] - from_pos[0];
883 let dy = to_pos[1] - from_pos[1];
884 let dz = to_pos[2] - from_pos[2];
885 let base_cost = (dx * dx + dy * dy + dz * dz).sqrt();
886
887 let area = if self.nav_mesh.is_off_mesh_connection(to_ref) {
889 let connection = self.nav_mesh.get_off_mesh_connection(to_ref)?;
890 connection.area
891 } else if self.nav_mesh.is_off_mesh_connection(from_ref) {
892 let connection = self.nav_mesh.get_off_mesh_connection(from_ref)?;
893 connection.area
894 } else {
895 0 };
897
898 let area_index = (area as usize).min(31);
900 let area_cost = filter.area_cost[area_index];
901
902 Ok(base_cost * area_cost)
903 }
904
905 fn pass_filter(
907 &self,
908 poly_ref: PolyRef,
909 _tile: &MeshTile,
910 _poly: &Poly,
911 filter: &QueryFilter,
912 ) -> Result<bool, DetourError> {
913 let (_, neighbor_poly) = self.nav_mesh.get_tile_and_poly_by_ref(poly_ref)?;
915
916 let include = (neighbor_poly.flags & filter.include_flags) != PolyFlags::empty();
918
919 let exclude = (neighbor_poly.flags & filter.exclude_flags) != PolyFlags::empty();
921
922 Ok(include && !exclude)
924 }
925
926 pub fn get_poly_center(&self, poly_ref: PolyRef) -> Result<Vec3, DetourError> {
927 let (tile, poly) = self.nav_mesh.get_tile_and_poly_by_ref(poly_ref)?;
929
930 let mut center = [0.0; 3];
931 let mut count = 0;
932
933 for i in 0..poly.vert_count as usize {
935 let vert_idx = poly.verts[i] as usize;
936
937 center[0] += tile.verts[vert_idx * 3];
938 center[1] += tile.verts[vert_idx * 3 + 1];
939 center[2] += tile.verts[vert_idx * 3 + 2];
940
941 count += 1;
942 }
943
944 if count > 0 {
945 center[0] /= count as f32;
946 center[1] /= count as f32;
947 center[2] /= count as f32;
948 }
949
950 Ok(Vec3::from(center))
951 }
952
953 pub fn closest_point_on_poly(
955 &self,
956 poly_ref: PolyRef,
957 pos: Vec3,
958 ) -> Result<(Vec3, bool), DetourError> {
959 let pos = pos.to_array();
960
961 if self.nav_mesh.is_off_mesh_connection(poly_ref) {
963 let (pt, over) = self.closest_point_on_off_mesh_connection(poly_ref, &pos)?;
964 return Ok((Vec3::from(pt), over));
965 }
966
967 let (tile, poly) = self.nav_mesh.get_tile_and_poly_by_ref(poly_ref)?;
969 let poly_idx = decode_poly_id(poly_ref) as usize;
970
971 let mut closest = pos;
972 let mut is_over_poly = false;
973
974 if let Some(height) = self.nav_mesh.get_poly_height(tile, poly_idx, &pos)? {
976 closest[1] = height;
977 is_over_poly = true;
978 return Ok((Vec3::from(closest), is_over_poly));
979 }
980
981 if poly.poly_type == PolyType::OffMeshConnection {
983 return Ok((Vec3::from(pos), false));
984 }
985
986 let mut closest_dist_sqr = f32::MAX;
988
989 for i in 0..poly.vert_count as usize {
991 let j = (i + 1) % poly.vert_count as usize;
992
993 let vi_idx = poly.verts[i] as usize;
994 let vj_idx = poly.verts[j] as usize;
995
996 let vi = [
997 tile.verts[vi_idx * 3],
998 tile.verts[vi_idx * 3 + 1],
999 tile.verts[vi_idx * 3 + 2],
1000 ];
1001 let vj = [
1002 tile.verts[vj_idx * 3],
1003 tile.verts[vj_idx * 3 + 1],
1004 tile.verts[vj_idx * 3 + 2],
1005 ];
1006
1007 let vi_pt = Vec3::new(vi[0], vi[1], vi[2]);
1008 let vj_pt = Vec3::new(vj[0], vj[1], vj[2]);
1009 let pos_pt = Vec3::new(pos[0], pos[1], pos[2]);
1010
1011 let edge_closest_pt =
1012 landmark_common::closest_point_on_segment(&pos_pt, &vi_pt, &vj_pt);
1013 let edge_closest = [edge_closest_pt.x, edge_closest_pt.y, edge_closest_pt.z];
1014
1015 let dx = edge_closest[0] - pos[0];
1016 let dy = edge_closest[1] - pos[1];
1017 let dz = edge_closest[2] - pos[2];
1018 let dist_sqr = dx * dx + dy * dy + dz * dz;
1019
1020 if dist_sqr < closest_dist_sqr {
1021 closest_dist_sqr = dist_sqr;
1022 closest = edge_closest;
1023 }
1024 }
1025
1026 Ok((Vec3::from(closest), is_over_poly))
1027 }
1028
1029 fn closest_point_on_off_mesh_connection(
1031 &self,
1032 connection_ref: PolyRef,
1033 pos: &[f32; 3],
1034 ) -> Result<([f32; 3], bool), DetourError> {
1035 let connection = self.nav_mesh.get_off_mesh_connection(connection_ref)?;
1036
1037 let start_pos = connection.start_pos();
1038 let end_pos = connection.end_pos();
1039
1040 let start_pt = Vec3::new(start_pos[0], start_pos[1], start_pos[2]);
1042 let end_pt = Vec3::new(end_pos[0], end_pos[1], end_pos[2]);
1043 let pos_pt = Vec3::new(pos[0], pos[1], pos[2]);
1044
1045 let closest_pt = landmark_common::closest_point_on_segment(&pos_pt, &start_pt, &end_pt);
1046 let closest_arr = [closest_pt.x, closest_pt.y, closest_pt.z];
1047
1048 let dist_to_closest = {
1050 let dx = pos[0] - closest_arr[0];
1051 let dy = pos[1] - closest_arr[1];
1052 let dz = pos[2] - closest_arr[2];
1053 (dx * dx + dy * dy + dz * dz).sqrt()
1054 };
1055
1056 let is_over_connection = dist_to_closest <= connection.radius;
1057
1058 Ok((closest_arr, is_over_connection))
1059 }
1060
1061 pub fn get_off_mesh_connection_endpoint(
1062 &self,
1063 connection_ref: PolyRef,
1064 start_pos: Vec3,
1065 ) -> Result<Vec3, DetourError> {
1066 let start_pos = start_pos.to_array();
1067 let connection = self.nav_mesh.get_off_mesh_connection(connection_ref)?;
1068
1069 let conn_start = connection.start_pos();
1070 let conn_end = connection.end_pos();
1071
1072 let dist_to_start = {
1074 let dx = start_pos[0] - conn_start[0];
1075 let dy = start_pos[1] - conn_start[1];
1076 let dz = start_pos[2] - conn_start[2];
1077 dx * dx + dy * dy + dz * dz
1078 };
1079
1080 let dist_to_end = {
1081 let dx = start_pos[0] - conn_end[0];
1082 let dy = start_pos[1] - conn_end[1];
1083 let dz = start_pos[2] - conn_end[2];
1084 dx * dx + dy * dy + dz * dz
1085 };
1086
1087 if dist_to_start < dist_to_end {
1089 if connection.allows_start_to_end() {
1091 Ok(Vec3::from(conn_end))
1092 } else {
1093 Err(DetourError::InvalidParam)
1094 }
1095 } else {
1096 if connection.allows_end_to_start() {
1098 Ok(Vec3::from(conn_start))
1099 } else {
1100 Err(DetourError::InvalidParam)
1101 }
1102 }
1103 }
1104
1105 pub fn get_off_mesh_connection_poly_end_points(
1117 &self,
1118 prev_ref: PolyRef,
1119 connection_ref: PolyRef,
1120 ) -> Result<(Vec3, Vec3), DetourError> {
1121 let connection = self.nav_mesh.get_off_mesh_connection(connection_ref)?;
1122
1123 let conn_start = connection.start_pos();
1124 let conn_end = connection.end_pos();
1125
1126 if !prev_ref.is_valid() {
1128 return Ok((Vec3::from(conn_start), Vec3::from(conn_end)));
1129 }
1130
1131 match self.nav_mesh.get_tile_and_poly_by_ref(prev_ref) {
1133 Ok((tile, poly)) => {
1134 let prev_center = self.calculate_poly_center(tile, poly)?;
1136
1137 let dist_to_start = {
1139 let dx = prev_center[0] - conn_start[0];
1140 let dy = prev_center[1] - conn_start[1];
1141 let dz = prev_center[2] - conn_start[2];
1142 dx * dx + dy * dy + dz * dz
1143 };
1144
1145 let dist_to_end = {
1146 let dx = prev_center[0] - conn_end[0];
1147 let dy = prev_center[1] - conn_end[1];
1148 let dz = prev_center[2] - conn_end[2];
1149 dx * dx + dy * dy + dz * dz
1150 };
1151
1152 if dist_to_start < dist_to_end {
1155 if connection.allows_start_to_end() {
1157 Ok((Vec3::from(conn_start), Vec3::from(conn_end)))
1158 } else {
1159 Err(DetourError::InvalidParam)
1160 }
1161 } else {
1162 if connection.allows_end_to_start() {
1164 Ok((Vec3::from(conn_end), Vec3::from(conn_start)))
1165 } else {
1166 Err(DetourError::InvalidParam)
1167 }
1168 }
1169 }
1170 Err(_) => {
1171 Ok((Vec3::from(conn_start), Vec3::from(conn_end)))
1173 }
1174 }
1175 }
1176
1177 fn calculate_poly_center(&self, tile: &MeshTile, poly: &Poly) -> Result<[f32; 3], DetourError> {
1179 if poly.vert_count == 0 {
1180 return Err(DetourError::InvalidParam);
1181 }
1182
1183 let mut center = [0.0f32; 3];
1184 let vert_count = poly.vert_count as usize;
1185
1186 for i in 0..vert_count {
1188 let vert_idx = poly.verts[i] as usize;
1189 if vert_idx * 3 + 2 >= tile.verts.len() {
1190 return Err(DetourError::InvalidParam);
1191 }
1192
1193 center[0] += tile.verts[vert_idx * 3];
1194 center[1] += tile.verts[vert_idx * 3 + 1];
1195 center[2] += tile.verts[vert_idx * 3 + 2];
1196 }
1197
1198 let count = vert_count as f32;
1200 center[0] /= count;
1201 center[1] /= count;
1202 center[2] /= count;
1203
1204 Ok(center)
1205 }
1206
1207 pub fn move_along_surface(
1223 &self,
1224 start_ref: PolyRef,
1225 start_pos: Vec3,
1226 end_pos: Vec3,
1227 filter: &QueryFilter,
1228 ) -> Result<MoveAlongSurfaceResult, DetourError> {
1229 use landmark_common::{dist_point_segment_sqr_2d_with_t, point_in_polygon_2d};
1230 use std::collections::VecDeque;
1231
1232 let start_pos = start_pos.to_array();
1233 let end_pos = end_pos.to_array();
1234 if !self.nav_mesh.is_valid_poly_ref(start_ref) {
1235 return Err(DetourError::InvalidParam);
1236 }
1237
1238 const MAX_STACK: usize = 48;
1240 const MAX_VISITED: usize = 16;
1241
1242 struct SurfaceNode {
1245 poly_ref: PolyRef,
1246 parent_idx: Option<usize>,
1247 }
1248
1249 let mut nodes = Vec::new();
1250 let mut stack = VecDeque::new();
1252
1253 let start_node = SurfaceNode {
1254 poly_ref: start_ref,
1255 parent_idx: None,
1256 };
1257 nodes.push(start_node);
1258 stack.push_back(0); let mut best_pos = start_pos;
1261 let mut best_dist = f32::MAX;
1262 let mut best_node_idx = 0;
1263
1264 let search_pos = [
1266 (start_pos[0] + end_pos[0]) * 0.5,
1267 (start_pos[1] + end_pos[1]) * 0.5,
1268 (start_pos[2] + end_pos[2]) * 0.5,
1269 ];
1270
1271 let dx = start_pos[0] - end_pos[0];
1272 let dy = start_pos[1] - end_pos[1];
1273 let dz = start_pos[2] - end_pos[2];
1274 let search_rad_sqr = (dx * dx + dy * dy + dz * dz) * 0.25 + 0.001;
1278
1279 while nodes.len() < MAX_VISITED {
1280 let Some(cur_node_idx) = stack.pop_front() else {
1282 break;
1283 };
1284 let cur_poly_ref = nodes[cur_node_idx].poly_ref;
1285
1286 let (cur_tile, cur_poly) = self.nav_mesh.get_tile_and_poly_by_ref(cur_poly_ref)?;
1288
1289 let mut verts = Vec::new();
1291 for i in 0..cur_poly.vert_count as usize {
1292 let idx = cur_poly.verts[i] as usize * 3;
1293 verts.push(cur_tile.verts[idx]);
1294 verts.push(cur_tile.verts[idx + 1]);
1295 verts.push(cur_tile.verts[idx + 2]);
1296 }
1297
1298 if point_in_polygon_2d(&end_pos, &verts, cur_poly.vert_count as usize) {
1300 best_node_idx = cur_node_idx;
1301 best_pos = end_pos;
1302 break;
1303 }
1304
1305 for i in 0..cur_poly.vert_count as usize {
1307 let j = if i == 0 {
1308 cur_poly.vert_count as usize - 1
1309 } else {
1310 i - 1
1311 };
1312
1313 let mut neighbors = Vec::new();
1315
1316 if let Some(link) = self.nav_mesh.find_link(cur_tile, cur_poly, j as u8) {
1317 if link.reference.is_valid() {
1318 if let Ok((nei_tile, nei_poly)) =
1319 self.nav_mesh.get_tile_and_poly_by_ref(link.reference)
1320 {
1321 if filter.pass_filter(link.reference, nei_tile, nei_poly) {
1322 neighbors.push(link.reference);
1323 }
1324 }
1325 }
1326 }
1327
1328 let vi_idx = cur_poly.verts[i] as usize * 3;
1329 let vj_idx = cur_poly.verts[j] as usize * 3;
1330 let vi = &cur_tile.verts[vi_idx..vi_idx + 3];
1331 let vj = &cur_tile.verts[vj_idx..vj_idx + 3];
1332
1333 if neighbors.is_empty() {
1335 let (dist_sqr, t) = dist_point_segment_sqr_2d_with_t(&end_pos, vj, vi);
1337 if dist_sqr < best_dist {
1338 best_pos = [
1340 vj[0] + (vi[0] - vj[0]) * t,
1341 vj[1] + (vi[1] - vj[1]) * t,
1342 vj[2] + (vi[2] - vj[2]) * t,
1343 ];
1344 best_dist = dist_sqr;
1345 best_node_idx = cur_node_idx;
1346 }
1347 } else {
1348 for &nei_ref in &neighbors {
1350 let already_visited = nodes.iter().any(|n| n.poly_ref == nei_ref);
1352 if already_visited {
1353 continue;
1354 }
1355
1356 let (dist_sqr, _) = dist_point_segment_sqr_2d_with_t(&search_pos, vj, vi);
1358 if dist_sqr > search_rad_sqr {
1359 continue;
1360 }
1361
1362 if stack.len() < MAX_STACK && nodes.len() < MAX_VISITED {
1364 let new_node = SurfaceNode {
1365 poly_ref: nei_ref,
1366 parent_idx: Some(cur_node_idx),
1367 };
1368 let new_idx = nodes.len();
1369 nodes.push(new_node);
1370 stack.push_back(new_idx);
1371 }
1372 }
1373 }
1374 }
1375 }
1376
1377 let mut path_indices = Vec::new();
1379 let mut current_idx = Some(best_node_idx);
1380
1381 while let Some(idx) = current_idx {
1382 path_indices.push(idx);
1383 current_idx = nodes[idx].parent_idx;
1384 }
1385
1386 path_indices.reverse();
1388
1389 let visited = path_indices
1391 .iter()
1392 .map(|&idx| nodes[idx].poly_ref)
1393 .collect();
1394
1395 Ok(MoveAlongSurfaceResult {
1396 position: Vec3::from(best_pos),
1397 visited,
1398 })
1399 }
1400
1401 #[allow(unused_assignments)]
1403 pub fn raycast(
1404 &self,
1405 start_ref: PolyRef,
1406 start_pos: Vec3,
1407 dir: Vec3,
1408 max_dist: f32,
1409 filter: &QueryFilter,
1410 ) -> Result<(PolyRef, Vec3, f32), DetourError> {
1411 let start_pos = start_pos.to_array();
1412 let dir = dir.to_array();
1413 if !self.nav_mesh.is_valid_poly_ref(start_ref) {
1414 return Err(DetourError::InvalidParam);
1415 }
1416
1417 if max_dist <= 0.0 {
1419 return Ok((start_ref, Vec3::from(start_pos), 0.0));
1420 }
1421
1422 let end_pos = [
1424 start_pos[0] + dir[0] * max_dist,
1425 start_pos[1] + dir[1] * max_dist,
1426 start_pos[2] + dir[2] * max_dist,
1427 ];
1428
1429 let mut cur_ref = start_ref;
1430 let mut cur_pos = start_pos;
1431 let mut _last_pos = start_pos;
1432
1433 const MAX_ITERATIONS: usize = 256;
1434
1435 for _ in 0..MAX_ITERATIONS {
1436 let (tile, poly) = self.nav_mesh.get_tile_and_poly_by_ref(cur_ref)?;
1438
1439 let dx = end_pos[0] - cur_pos[0];
1441 let dz = end_pos[2] - cur_pos[2];
1442 if dx * dx + dz * dz < 1e-6 {
1443 break;
1444 }
1445
1446 let mut next_ref = PolyRef::new(0);
1448 let mut next_pos = end_pos;
1449 let mut next_t = 1.0;
1450
1451 for i in 0..poly.vert_count as usize {
1453 let j = (i + 1) % poly.vert_count as usize;
1454
1455 let vi_idx = poly.verts[i] as usize;
1456 let vj_idx = poly.verts[j] as usize;
1457
1458 let vi = [
1459 tile.verts[vi_idx * 3],
1460 tile.verts[vi_idx * 3 + 1],
1461 tile.verts[vi_idx * 3 + 2],
1462 ];
1463 let vj = [
1464 tile.verts[vj_idx * 3],
1465 tile.verts[vj_idx * 3 + 1],
1466 tile.verts[vj_idx * 3 + 2],
1467 ];
1468
1469 if let Some((edge_t, seg_t)) =
1471 Self::intersect_ray_segment_2d(&cur_pos, &end_pos, &vi, &vj)
1472 {
1473 if edge_t > 0.0 && edge_t < next_t {
1474 if (0.0..=1.0).contains(&seg_t) {
1476 if let Some(link) = self.nav_mesh.find_link(tile, poly, i as u8) {
1478 if link.reference.is_valid()
1479 && filter.pass_filter(link.reference, tile, poly)
1480 {
1481 next_ref = link.reference;
1482 next_t = edge_t;
1483
1484 next_pos = [
1486 cur_pos[0] + (end_pos[0] - cur_pos[0]) * edge_t,
1487 cur_pos[1] + (end_pos[1] - cur_pos[1]) * edge_t,
1488 cur_pos[2] + (end_pos[2] - cur_pos[2]) * edge_t,
1489 ];
1490 } else {
1491 next_t = edge_t;
1493 next_pos = [
1494 cur_pos[0] + (end_pos[0] - cur_pos[0]) * edge_t,
1495 cur_pos[1] + (end_pos[1] - cur_pos[1]) * edge_t,
1496 cur_pos[2] + (end_pos[2] - cur_pos[2]) * edge_t,
1497 ];
1498 break; }
1501 } else {
1502 next_t = edge_t;
1504 next_pos = [
1505 cur_pos[0] + (end_pos[0] - cur_pos[0]) * edge_t,
1506 cur_pos[1] + (end_pos[1] - cur_pos[1]) * edge_t,
1507 cur_pos[2] + (end_pos[2] - cur_pos[2]) * edge_t,
1508 ];
1509 break; }
1512 } else {
1513 next_t = edge_t;
1516 next_pos = [
1517 cur_pos[0] + (end_pos[0] - cur_pos[0]) * edge_t,
1518 cur_pos[1] + (end_pos[1] - cur_pos[1]) * edge_t,
1519 cur_pos[2] + (end_pos[2] - cur_pos[2]) * edge_t,
1520 ];
1521 break; }
1524 }
1525 }
1526 }
1527
1528 if next_ref.is_valid() {
1530 next_ref = self.check_off_mesh_connections_for_raycast(
1531 &cur_pos,
1532 &end_pos,
1533 tile,
1534 filter,
1535 &mut next_pos,
1536 &mut next_t,
1537 )?;
1538 }
1539
1540 if !next_ref.is_valid() {
1542 if next_t >= 1.0 {
1545 return Ok((cur_ref, Vec3::from(end_pos), max_dist));
1546 }
1547
1548 let hit_dist = max_dist * next_t;
1550 return Ok((cur_ref, Vec3::from(next_pos), hit_dist));
1551 }
1552
1553 _last_pos = cur_pos;
1555 cur_pos = next_pos;
1556 cur_ref = next_ref;
1557 }
1558
1559 Ok((cur_ref, Vec3::from(end_pos), max_dist))
1561 }
1562
1563 fn check_off_mesh_connections_for_raycast(
1565 &self,
1566 cur_pos: &[f32; 3],
1567 end_pos: &[f32; 3],
1568 tile: &MeshTile,
1569 filter: &QueryFilter,
1570 next_pos: &mut [f32; 3],
1571 next_t: &mut f32,
1572 ) -> Result<PolyRef, DetourError> {
1573 let mut best_ref = PolyRef::new(0);
1574 let mut best_t = *next_t;
1575
1576 for connection in &tile.off_mesh_connections {
1578 if !filter.include_flags.intersects(connection.flags)
1580 || filter.exclude_flags.intersects(connection.flags)
1581 {
1582 continue;
1583 }
1584
1585 let start_conn = connection.start_pos();
1586 let end_conn = connection.end_pos();
1587
1588 let ray_dir = [
1590 end_pos[0] - cur_pos[0],
1591 end_pos[1] - cur_pos[1],
1592 end_pos[2] - cur_pos[2],
1593 ];
1594 let ray_len =
1595 (ray_dir[0] * ray_dir[0] + ray_dir[1] * ray_dir[1] + ray_dir[2] * ray_dir[2])
1596 .sqrt();
1597
1598 if ray_len < 1e-6 {
1599 continue;
1600 }
1601
1602 let ray_dir_norm = [
1603 ray_dir[0] / ray_len,
1604 ray_dir[1] / ray_len,
1605 ray_dir[2] / ray_len,
1606 ];
1607
1608 let to_start = [
1610 start_conn[0] - cur_pos[0],
1611 start_conn[1] - cur_pos[1],
1612 start_conn[2] - cur_pos[2],
1613 ];
1614 let proj_start = to_start[0] * ray_dir_norm[0]
1615 + to_start[1] * ray_dir_norm[1]
1616 + to_start[2] * ray_dir_norm[2];
1617
1618 let to_end = [
1620 end_conn[0] - cur_pos[0],
1621 end_conn[1] - cur_pos[1],
1622 end_conn[2] - cur_pos[2],
1623 ];
1624 let proj_end = to_end[0] * ray_dir_norm[0]
1625 + to_end[1] * ray_dir_norm[1]
1626 + to_end[2] * ray_dir_norm[2];
1627
1628 if proj_start > 0.0 && proj_start < ray_len && proj_start < best_t * ray_len {
1630 let closest_on_ray = [
1631 cur_pos[0] + ray_dir_norm[0] * proj_start,
1632 cur_pos[1] + ray_dir_norm[1] * proj_start,
1633 cur_pos[2] + ray_dir_norm[2] * proj_start,
1634 ];
1635
1636 let dist_to_start = self.distance_3d(closest_on_ray, start_conn);
1637 if dist_to_start <= connection.radius && connection.allows_start_to_end() {
1638 let t = proj_start / ray_len;
1639 if t < best_t {
1640 best_ref = connection.poly;
1641 best_t = t;
1642 *next_pos = end_conn; }
1644 }
1645 }
1646
1647 if proj_end > 0.0 && proj_end < ray_len && proj_end < best_t * ray_len {
1649 let closest_on_ray = [
1650 cur_pos[0] + ray_dir_norm[0] * proj_end,
1651 cur_pos[1] + ray_dir_norm[1] * proj_end,
1652 cur_pos[2] + ray_dir_norm[2] * proj_end,
1653 ];
1654
1655 let dist_to_end = self.distance_3d(closest_on_ray, end_conn);
1656 if dist_to_end <= connection.radius && connection.allows_end_to_start() {
1657 let t = proj_end / ray_len;
1658 if t < best_t {
1659 best_ref = connection.poly;
1660 best_t = t;
1661 *next_pos = start_conn; }
1663 }
1664 }
1665 }
1666
1667 if best_ref.is_valid() {
1668 *next_t = best_t;
1669 }
1670
1671 Ok(best_ref)
1672 }
1673
1674 fn intersect_ray_segment_2d(
1676 ray_start: &[f32; 3],
1677 ray_end: &[f32; 3],
1678 seg_start: &[f32; 3],
1679 seg_end: &[f32; 3],
1680 ) -> Option<(f32, f32)> {
1681 let dx = ray_end[0] - ray_start[0];
1682 let dz = ray_end[2] - ray_start[2];
1683 let seg_dx = seg_end[0] - seg_start[0];
1684 let seg_dz = seg_end[2] - seg_start[2];
1685
1686 let d = seg_dx * dz - seg_dz * dx;
1687 if d.abs() < 1e-6 {
1688 return None; }
1690
1691 let t = ((seg_start[0] - ray_start[0]) * dz - (seg_start[2] - ray_start[2]) * dx) / d;
1692 let s =
1693 ((seg_start[0] - ray_start[0]) * seg_dz - (seg_start[2] - ray_start[2]) * seg_dx) / d;
1694
1695 Some((s, t))
1696 }
1697
1698 pub fn raycast_enhanced(
1701 &self,
1702 start_ref: PolyRef,
1703 start_pos: Vec3,
1704 end_pos: Vec3,
1705 filter: &QueryFilter,
1706 options: &RaycastOptions,
1707 prev_ref: Option<PolyRef>,
1708 ) -> Result<RaycastResult, DetourError> {
1709 let start_pos = start_pos.to_array();
1710 let end_pos = end_pos.to_array();
1711 if !self.nav_mesh.is_valid_poly_ref(start_ref) {
1712 return Err(DetourError::InvalidParam);
1713 }
1714
1715 let dir = [
1717 end_pos[0] - start_pos[0],
1718 end_pos[1] - start_pos[1],
1719 end_pos[2] - start_pos[2],
1720 ];
1721 let max_dist = (dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]).sqrt();
1722
1723 let mut hit = RaycastHit::no_hit();
1724 let mut visited_path = Vec::new();
1725 let mut path_cost = 0.0;
1726
1727 if max_dist <= 0.0 {
1729 let result = RaycastResult::new(start_ref, Vec3::from(start_pos), hit);
1730 return Ok(result);
1731 }
1732
1733 let mut cur_ref = start_ref;
1735 let mut cur_pos = start_pos;
1736 let mut last_ref = prev_ref.unwrap_or(PolyRef::new(0));
1737
1738 if options.include_path {
1739 visited_path.push(start_ref);
1740 }
1741
1742 const MAX_ITERATIONS: usize = 256;
1743
1744 for iter in 0..MAX_ITERATIONS {
1745 let (tile, poly) = self.nav_mesh.get_tile_and_poly_by_ref(cur_ref)?;
1747
1748 let dx = end_pos[0] - cur_pos[0];
1750 let dz = end_pos[2] - cur_pos[2];
1751 if dx * dx + dz * dz < 1e-6 {
1752 break;
1753 }
1754
1755 let mut next_ref = PolyRef::new(0);
1757 let mut next_pos = end_pos;
1758 let mut edge_t = 1.0;
1759 let mut edge_index = -1;
1760
1761 for i in 0..poly.vert_count as usize {
1763 let j = (i + 1) % poly.vert_count as usize;
1764
1765 let vi_idx = poly.verts[i] as usize;
1766 let vj_idx = poly.verts[j] as usize;
1767
1768 let vi = [
1769 tile.verts[vi_idx * 3],
1770 tile.verts[vi_idx * 3 + 1],
1771 tile.verts[vi_idx * 3 + 2],
1772 ];
1773 let vj = [
1774 tile.verts[vj_idx * 3],
1775 tile.verts[vj_idx * 3 + 1],
1776 tile.verts[vj_idx * 3 + 2],
1777 ];
1778
1779 if let Some((ray_t, seg_t)) =
1781 Self::intersect_ray_segment_2d(&cur_pos, &end_pos, &vi, &vj)
1782 {
1783 if ray_t > 0.0 && ray_t < edge_t {
1784 if let Some(link) = self.nav_mesh.find_link(tile, poly, i as u8) {
1786 if link.reference.is_valid()
1787 && link.reference != last_ref
1788 && filter.pass_filter(link.reference, tile, poly)
1789 && (0.0..=1.0).contains(&seg_t)
1790 {
1791 next_ref = link.reference;
1793 edge_t = ray_t;
1794 edge_index = i as i32;
1795 next_pos = [
1796 cur_pos[0] + dir[0] * ray_t,
1797 cur_pos[1] + dir[1] * ray_t,
1798 cur_pos[2] + dir[2] * ray_t,
1799 ];
1800 }
1801 } else if (0.0..=1.0).contains(&seg_t) {
1802 edge_t = ray_t;
1804 edge_index = i as i32;
1805 next_pos = [
1806 cur_pos[0] + dir[0] * ray_t,
1807 cur_pos[1] + dir[1] * ray_t,
1808 cur_pos[2] + dir[2] * ray_t,
1809 ];
1810 let edge_dx = vj[0] - vi[0];
1812 let edge_dz = vj[2] - vi[2];
1813 let edge_len = (edge_dx * edge_dx + edge_dz * edge_dz).sqrt();
1814 if edge_len > 1e-6 {
1815 hit.hit_normal =
1816 Vec3::new(edge_dz / edge_len, 0.0, -edge_dx / edge_len);
1817 }
1818 next_ref = PolyRef::new(0);
1820 break;
1821 }
1822 }
1823 }
1824 }
1825
1826 if options.include_cost && next_ref.is_valid() {
1828 let edge_cost =
1829 self.get_edge_cost(cur_ref, next_ref, &cur_pos, &next_pos, filter)?;
1830 path_cost += edge_cost;
1831 }
1832
1833 if !next_ref.is_valid() {
1835 hit.t = edge_t * max_dist / max_dist; hit.hit_edge_index = edge_index;
1837
1838 if options.include_path {
1839 hit = hit.with_path(visited_path);
1840 }
1841 if options.include_cost {
1842 hit = hit.with_cost(path_cost);
1843 }
1844
1845 let result = RaycastResult::new(cur_ref, Vec3::from(next_pos), hit);
1846 return Ok(result);
1847 }
1848
1849 if options.include_path {
1851 visited_path.push(next_ref);
1852 }
1853 last_ref = cur_ref;
1854 cur_ref = next_ref;
1855 cur_pos = next_pos;
1856
1857 if iter >= MAX_ITERATIONS - 1 {
1859 break;
1860 }
1861 }
1862
1863 if options.include_path {
1865 hit = hit.with_path(visited_path);
1866 }
1867 if options.include_cost {
1868 hit = hit.with_cost(path_cost);
1869 }
1870
1871 let result = RaycastResult::new(cur_ref, Vec3::from(end_pos), hit);
1872 Ok(result)
1873 }
1874
1875 pub fn get_path_from_dijkstra_search(
1878 &self,
1879 end_ref: PolyRef,
1880 max_path: usize,
1881 ) -> Result<Vec<PolyRef>, DetourError> {
1882 if !self.nav_mesh.is_valid_poly_ref(end_ref) {
1883 return Err(DetourError::InvalidParam);
1884 }
1885
1886 let mut end_node_idx = None;
1888 for i in 0..self.node_pool.len() {
1889 if self.node_pool[i].poly == end_ref && self.node_pool[i].state == NodeState::Closed {
1890 end_node_idx = Some(i);
1891 break;
1892 }
1893 }
1894
1895 let end_idx = end_node_idx.ok_or_else(|| DetourError::NotFound)?;
1896
1897 let mut path = Vec::new();
1899 let mut current_idx = end_idx;
1900 let mut safety_counter = 0;
1901
1902 loop {
1903 if safety_counter >= max_path {
1904 break; }
1906 safety_counter += 1;
1907
1908 let node = &self.node_pool[current_idx];
1909 path.push(node.poly);
1910
1911 match node.parent {
1912 Some(parent_idx) => {
1913 current_idx = parent_idx;
1914 }
1915 None => {
1916 break;
1918 }
1919 }
1920 }
1921
1922 path.reverse();
1924
1925 if path.len() > max_path {
1927 path.truncate(max_path);
1928 }
1929
1930 Ok(path)
1931 }
1932
1933 #[allow(unused_assignments)]
1935 pub fn find_straight_path(
1936 &self,
1937 start_pos: Vec3,
1938 end_pos: Vec3,
1939 path: &[PolyRef],
1940 ) -> Result<Path, DetourError> {
1941 let start_pos = start_pos.to_array();
1942 let end_pos = end_pos.to_array();
1943 if path.is_empty() {
1944 return Err(DetourError::InvalidParam);
1945 }
1946
1947 let mut result = Path::new();
1948
1949 result.waypoints.push(Vec3::from(start_pos));
1951 result.poly_refs.push(path[0]);
1952
1953 if path.len() == 1 {
1955 result.waypoints.push(Vec3::from(end_pos));
1956 result.poly_refs.push(path[0]);
1957 return Ok(result);
1958 }
1959
1960 let mut portal_apex = start_pos;
1962 let mut portal_left = portal_apex;
1963 let mut portal_right = portal_apex;
1964 let mut apex_index = 0;
1965 let mut left_index = 0;
1966 let mut right_index = 0;
1967
1968 for i in 0..path.len() - 1 {
1969 let (left_v, right_v) = self.get_portal_points(path[i], path[i + 1])?;
1971 let left = left_v.to_array();
1972 let right = right_v.to_array();
1973
1974 if i == 0 {
1976 let dx = portal_apex[0] - left[0];
1977 let dz = portal_apex[2] - left[2];
1978 let dist_sqr = dx * dx + dz * dz;
1979 if dist_sqr < 0.001 * 0.001 {
1980 continue;
1981 }
1982 }
1983
1984 if Self::tri_area_2d(&portal_apex, &portal_right, &right) <= 0.0 {
1986 if Self::v_equal(&portal_apex, &portal_right)
1987 || Self::tri_area_2d(&portal_apex, &portal_left, &right) > 0.0
1988 {
1989 portal_right = right;
1990 right_index = i + 1;
1991 } else {
1992 result.waypoints.push(Vec3::from(portal_left));
1994 result.poly_refs.push(path[left_index]);
1995
1996 portal_apex = portal_left;
1998 apex_index = left_index;
1999 portal_left = portal_apex;
2000 portal_right = portal_apex;
2001 left_index = apex_index;
2002 right_index = apex_index;
2003 continue;
2004 }
2005 }
2006
2007 if Self::tri_area_2d(&portal_apex, &portal_left, &left) >= 0.0 {
2009 if Self::v_equal(&portal_apex, &portal_left)
2010 || Self::tri_area_2d(&portal_apex, &portal_right, &left) < 0.0
2011 {
2012 portal_left = left;
2013 left_index = i + 1;
2014 } else {
2015 result.waypoints.push(Vec3::from(portal_right));
2017 result.poly_refs.push(path[right_index]);
2018
2019 portal_apex = portal_right;
2021 apex_index = right_index;
2022 portal_left = portal_apex;
2023 portal_right = portal_apex;
2024 left_index = apex_index;
2025 right_index = apex_index;
2026 continue;
2027 }
2028 }
2029 }
2030
2031 result.waypoints.push(Vec3::from(end_pos));
2033 result.poly_refs.push(path[path.len() - 1]);
2034
2035 Ok(result)
2036 }
2037
2038 pub fn find_straight_path_with_options(
2051 &self,
2052 start_pos: Vec3,
2053 end_pos: Vec3,
2054 path: &[PolyRef],
2055 options: u32,
2056 ) -> Result<Path, DetourError> {
2057 let _ = options;
2060
2061 self.find_straight_path(start_pos, end_pos, path)
2063 }
2064
2065 pub fn get_portal_points(
2076 &self,
2077 from_ref: PolyRef,
2078 to_ref: PolyRef,
2079 ) -> Result<(Vec3, Vec3), DetourError> {
2080 let (from_tile, from_poly) = self.nav_mesh.get_tile_and_poly_by_ref(from_ref)?;
2081 let (_to_tile, to_poly) = self.nav_mesh.get_tile_and_poly_by_ref(to_ref)?;
2082
2083 if from_poly.poly_type == PolyType::OffMeshConnection {
2085 let mut link_idx = from_poly.first_link;
2088 while let Some(idx) = link_idx {
2089 if idx < from_tile.links.len() {
2090 let link = &from_tile.links[idx];
2091 if link.reference == to_ref {
2092 let v_idx = from_poly.verts[link.edge_index as usize] as usize;
2094 if v_idx * 3 + 2 < from_tile.verts.len() {
2095 let pos = [
2096 from_tile.verts[v_idx * 3],
2097 from_tile.verts[v_idx * 3 + 1],
2098 from_tile.verts[v_idx * 3 + 2],
2099 ];
2100 let pos = Vec3::from(pos);
2102 return Ok((pos, pos));
2103 }
2104 }
2105 link_idx = link.next.map(|n| n as usize);
2106 }
2107 }
2108 return Err(DetourError::InvalidParam);
2109 }
2110
2111 if to_poly.poly_type == PolyType::OffMeshConnection {
2112 let mut link_idx = to_poly.first_link;
2115 while let Some(idx) = link_idx {
2116 if idx < _to_tile.links.len() {
2117 let link = &_to_tile.links[idx];
2118 if link.reference == from_ref {
2119 let v_idx = to_poly.verts[link.edge_index as usize] as usize;
2121 if v_idx * 3 + 2 < _to_tile.verts.len() {
2122 let pos = [
2123 _to_tile.verts[v_idx * 3],
2124 _to_tile.verts[v_idx * 3 + 1],
2125 _to_tile.verts[v_idx * 3 + 2],
2126 ];
2127 let pos = Vec3::from(pos);
2129 return Ok((pos, pos));
2130 }
2131 }
2132 link_idx = link.next.map(|n| n as usize);
2133 }
2134 }
2135 return Err(DetourError::InvalidParam);
2136 }
2137
2138 for i in 0..from_poly.vert_count as usize {
2141 if let Some(link) = self.nav_mesh.find_link(from_tile, from_poly, i as u8) {
2143 if link.reference == to_ref {
2144 let j = (i + 1) % from_poly.vert_count as usize;
2145
2146 let vi_idx = from_poly.verts[i] as usize;
2147 let vj_idx = from_poly.verts[j] as usize;
2148
2149 let left = [
2150 from_tile.verts[vi_idx * 3],
2151 from_tile.verts[vi_idx * 3 + 1],
2152 from_tile.verts[vi_idx * 3 + 2],
2153 ];
2154 let right = [
2155 from_tile.verts[vj_idx * 3],
2156 from_tile.verts[vj_idx * 3 + 1],
2157 from_tile.verts[vj_idx * 3 + 2],
2158 ];
2159
2160 return Ok((Vec3::from(left), Vec3::from(right)));
2161 }
2162 }
2163 }
2164
2165 Err(DetourError::InvalidParam)
2167 }
2168
2169 fn tri_area_2d(a: &[f32; 3], b: &[f32; 3], c: &[f32; 3]) -> f32 {
2171 let abx = b[0] - a[0];
2172 let abz = b[2] - a[2];
2173 let acx = c[0] - a[0];
2174 let acz = c[2] - a[2];
2175 acx * abz - abx * acz
2176 }
2177
2178 fn v_equal(a: &[f32; 3], b: &[f32; 3]) -> bool {
2180 let dx = a[0] - b[0];
2181 let dz = a[2] - b[2];
2182 dx * dx + dz * dz < 0.00001
2183 }
2184
2185 fn distance_point_to_segment_2d_squared(
2188 &self,
2189 point: &[f32; 3],
2190 segment_start: &[f32; 3],
2191 segment_end: &[f32; 3],
2192 ) -> f32 {
2193 let px = point[0];
2194 let pz = point[2];
2195 let ax = segment_start[0];
2196 let az = segment_start[2];
2197 let bx = segment_end[0];
2198 let bz = segment_end[2];
2199
2200 let dx = bx - ax;
2201 let dz = bz - az;
2202 let d = dx * dx + dz * dz;
2203
2204 if d > 0.0 {
2205 let t = ((px - ax) * dx + (pz - az) * dz) / d;
2206 let t_clamped = t.clamp(0.0, 1.0);
2207
2208 let closest_x = ax + t_clamped * dx;
2209 let closest_z = az + t_clamped * dz;
2210
2211 let dist_x = px - closest_x;
2212 let dist_z = pz - closest_z;
2213
2214 dist_x * dist_x + dist_z * dist_z
2215 } else {
2216 let dist_x = px - ax;
2218 let dist_z = pz - az;
2219 dist_x * dist_x + dist_z * dist_z
2220 }
2221 }
2222
2223 pub fn find_polys_around_circle(
2225 &self,
2226 center_ref: PolyRef,
2227 center_pos: Vec3,
2228 radius: f32,
2229 filter: &QueryFilter,
2230 ) -> Result<Vec<PolyRef>, DetourError> {
2231 let center_pos = center_pos.to_array();
2232 if !self.nav_mesh.is_valid_poly_ref(center_ref) {
2233 return Err(DetourError::InvalidParam);
2234 }
2235
2236 let mut node_pool = std::collections::HashMap::new();
2237 let mut open_list = BinaryHeap::new();
2238 let mut result_polys = Vec::new();
2239
2240 let radius_squared = radius * radius;
2241
2242 let start_node = Node {
2244 poly: center_ref,
2245 parent: None,
2246 g: 0.0,
2247 h: 0.0,
2248 f: 0.0,
2249 state: NodeState::Open,
2250 index: 0,
2251 };
2252
2253 node_pool.insert(center_ref.id(), start_node);
2255 open_list.push(HeapNode {
2256 index: center_ref.id() as usize,
2257 f: 0.0,
2258 });
2259
2260 while let Some(heap_node) = open_list.pop() {
2262 let current_ref_id = heap_node.index as u32;
2263 let current_ref = PolyRef::new(current_ref_id);
2264
2265 let current_cost = {
2267 match node_pool.get_mut(¤t_ref_id) {
2268 Some(node) => {
2269 node.state = NodeState::Closed;
2270 node.g
2271 }
2272 None => continue,
2273 }
2274 };
2275
2276 let (current_tile, current_poly) =
2278 match self.nav_mesh.get_tile_and_poly_by_ref(current_ref) {
2279 Ok((tile, poly)) => (tile, poly),
2280 Err(_) => continue,
2281 };
2282
2283 if !filter.pass_filter(current_ref, current_tile, current_poly) {
2285 continue;
2286 }
2287
2288 result_polys.push(current_ref);
2290
2291 if let Some(first_link_idx) = current_poly.first_link {
2293 let mut link_idx = first_link_idx;
2294
2295 loop {
2296 if link_idx >= current_tile.links.len() {
2297 break;
2298 }
2299
2300 let link = ¤t_tile.links[link_idx];
2301 let neighbor_ref = link.reference;
2302
2303 if !neighbor_ref.is_valid() {
2305 if let Some(next_idx) = link.next {
2306 link_idx = next_idx as usize;
2307 continue;
2308 } else {
2309 break;
2310 }
2311 }
2312
2313 let (neighbor_tile, neighbor_poly) =
2315 match self.nav_mesh.get_tile_and_poly_by_ref(neighbor_ref) {
2316 Ok((tile, poly)) => (tile, poly),
2317 Err(_) => {
2318 if let Some(next_idx) = link.next {
2319 link_idx = next_idx as usize;
2320 continue;
2321 } else {
2322 break;
2323 }
2324 }
2325 };
2326
2327 if !filter.pass_filter(neighbor_ref, neighbor_tile, neighbor_poly) {
2329 if let Some(next_idx) = link.next {
2330 link_idx = next_idx as usize;
2331 continue;
2332 } else {
2333 break;
2334 }
2335 }
2336
2337 let portal_result = self.get_portal_points(current_ref, neighbor_ref);
2339 let within_circle = match portal_result {
2340 Ok((left_portal, right_portal)) => {
2341 let left_arr = left_portal.to_array();
2343 let right_arr = right_portal.to_array();
2344 let dist_squared = self.distance_point_to_segment_2d_squared(
2345 ¢er_pos,
2346 &left_arr,
2347 &right_arr,
2348 );
2349 dist_squared <= radius_squared
2350 }
2351 Err(_) => {
2352 if let Ok(neighbor_center) = self.get_poly_center(neighbor_ref) {
2354 let dx = neighbor_center[0] - center_pos[0];
2355 let dz = neighbor_center[2] - center_pos[2];
2356 let dist_squared = dx * dx + dz * dz;
2357 dist_squared <= radius_squared
2358 } else {
2359 false
2360 }
2361 }
2362 };
2363
2364 if !within_circle {
2366 if let Some(next_idx) = link.next {
2367 link_idx = next_idx as usize;
2368 continue;
2369 } else {
2370 break;
2371 }
2372 }
2373
2374 let neighbor_id = neighbor_ref.id();
2376 if let Some(existing_node) = node_pool.get(&neighbor_id) {
2377 if existing_node.state == NodeState::Closed {
2378 if let Some(next_idx) = link.next {
2380 link_idx = next_idx as usize;
2381 continue;
2382 } else {
2383 break;
2384 }
2385 }
2386 }
2387
2388 let cost = 1.0;
2390 let total_cost = current_cost + cost;
2391
2392 let should_add_to_open = match node_pool.get_mut(&neighbor_id) {
2394 Some(neighbor_node) => {
2395 if neighbor_node.state == NodeState::New || total_cost < neighbor_node.g
2396 {
2397 neighbor_node.g = total_cost;
2398 neighbor_node.f = total_cost;
2399 neighbor_node.parent = Some(current_ref_id as usize);
2400 neighbor_node.state = NodeState::Open;
2401 true
2402 } else {
2403 false
2404 }
2405 }
2406 None => {
2407 let new_node = Node {
2409 poly: neighbor_ref,
2410 parent: Some(current_ref_id as usize),
2411 g: total_cost,
2412 h: 0.0,
2413 f: total_cost,
2414 state: NodeState::Open,
2415 index: neighbor_id as usize,
2416 };
2417 node_pool.insert(neighbor_id, new_node);
2418 true
2419 }
2420 };
2421
2422 if should_add_to_open {
2424 open_list.push(HeapNode {
2425 index: neighbor_id as usize,
2426 f: total_cost,
2427 });
2428 }
2429
2430 if let Some(next_idx) = link.next {
2432 link_idx = next_idx as usize;
2433 } else {
2434 break;
2435 }
2436 }
2437 }
2438 }
2439
2440 Ok(result_polys)
2441 }
2442
2443 fn is_point_in_polygon(
2445 &self,
2446 tile: &super::nav_mesh::MeshTile,
2447 poly: &super::nav_mesh::Poly,
2448 point: &[f32; 3],
2449 ) -> Result<bool, DetourError> {
2450 let mut inside = false;
2451 let px = point[0];
2452 let pz = point[2];
2453
2454 let mut j = poly.vert_count as usize - 1;
2455 for i in 0..poly.vert_count as usize {
2456 let vi_idx = poly.verts[i] as usize;
2457 let vj_idx = poly.verts[j] as usize;
2458
2459 let vix = tile.verts[vi_idx * 3];
2460 let viz = tile.verts[vi_idx * 3 + 2];
2461 let vjx = tile.verts[vj_idx * 3];
2462 let vjz = tile.verts[vj_idx * 3 + 2];
2463
2464 if ((viz > pz) != (vjz > pz)) && (px < (vjx - vix) * (pz - viz) / (vjz - viz) + vix) {
2465 inside = !inside;
2466 }
2467 j = i;
2468 }
2469
2470 Ok(inside)
2471 }
2472
2473 fn closest_point_on_segment(
2475 &self,
2476 seg_start: &[f32; 3],
2477 seg_end: &[f32; 3],
2478 point: &[f32; 3],
2479 ) -> [f32; 3] {
2480 let dx = seg_end[0] - seg_start[0];
2481 let dz = seg_end[2] - seg_start[2];
2482
2483 let seg_len_squared = dx * dx + dz * dz;
2484
2485 if seg_len_squared < 1e-6 {
2486 return *seg_start;
2488 }
2489
2490 let px = point[0] - seg_start[0];
2491 let pz = point[2] - seg_start[2];
2492
2493 let t = (px * dx + pz * dz) / seg_len_squared;
2494 let t_clamped = t.clamp(0.0, 1.0);
2495
2496 [
2497 seg_start[0] + dx * t_clamped,
2498 seg_start[1] + (seg_end[1] - seg_start[1]) * t_clamped,
2499 seg_start[2] + dz * t_clamped,
2500 ]
2501 }
2502
2503 pub fn find_distance_to_wall(
2505 &self,
2506 start_ref: PolyRef,
2507 center_pos: Vec3,
2508 radius: f32,
2509 filter: &QueryFilter,
2510 ) -> Result<(f32, Vec3, Vec3), DetourError> {
2511 let center_pos = center_pos.to_array();
2512 if !self.nav_mesh.is_valid_poly_ref(start_ref) {
2513 return Err(DetourError::InvalidParam);
2514 }
2515
2516 let mut min_distance = radius;
2517 let mut wall_hit = center_pos;
2518 let mut wall_normal = [0.0, 0.0, 1.0];
2519
2520 const NUM_RAYS: usize = 16;
2522 let angle_step = 2.0 * std::f32::consts::PI / NUM_RAYS as f32;
2523
2524 for i in 0..NUM_RAYS {
2526 let angle = i as f32 * angle_step;
2527 let ray_dir = [angle.cos(), 0.0, angle.sin()];
2528
2529 match self.raycast(
2531 start_ref,
2532 Vec3::from(center_pos),
2533 Vec3::from(ray_dir),
2534 radius,
2535 filter,
2536 ) {
2537 Ok((_, hit_pos_v, t)) => {
2538 let hit_pos = hit_pos_v.to_array();
2539 let hit_distance = t * radius;
2540
2541 if hit_distance < min_distance {
2542 min_distance = hit_distance;
2543 wall_hit = hit_pos;
2544
2545 let dx = center_pos[0] - hit_pos[0];
2547 let dz = center_pos[2] - hit_pos[2];
2548 let len = (dx * dx + dz * dz).sqrt();
2549
2550 if len > 1e-6 {
2551 wall_normal = [dx / len, 0.0, dz / len];
2552 }
2553 }
2554 }
2555 Err(_) => {
2556 }
2558 }
2559 }
2560
2561 if min_distance >= radius {
2563 if let Ok(boundary_result) =
2564 self.find_polygon_boundary_distance(start_ref, ¢er_pos, radius, filter)
2565 {
2566 if boundary_result.0 < min_distance {
2567 min_distance = boundary_result.0;
2568 wall_hit = boundary_result.1;
2569 wall_normal = boundary_result.2;
2570 }
2571 }
2572 }
2573
2574 Ok((min_distance, Vec3::from(wall_hit), Vec3::from(wall_normal)))
2575 }
2576
2577 fn find_polygon_boundary_distance(
2579 &self,
2580 start_ref: PolyRef,
2581 center_pos: &[f32; 3],
2582 max_radius: f32,
2583 filter: &QueryFilter,
2584 ) -> Result<(f32, [f32; 3], [f32; 3]), DetourError> {
2585 let mut min_distance = max_radius;
2586 let mut wall_hit = *center_pos;
2587 let mut wall_normal = [0.0, 0.0, 1.0];
2588
2589 let mut visited = std::collections::HashSet::new();
2590 let mut queue = std::collections::VecDeque::new();
2591
2592 queue.push_back(start_ref);
2594 visited.insert(start_ref.id());
2595
2596 while let Some(current_ref) = queue.pop_front() {
2597 let (tile, poly) = match self.nav_mesh.get_tile_and_poly_by_ref(current_ref) {
2598 Ok((tile, poly)) => (tile, poly),
2599 Err(_) => continue,
2600 };
2601
2602 if !filter.pass_filter(current_ref, tile, poly) {
2603 continue;
2604 }
2605
2606 for i in 0..poly.vert_count as usize {
2608 let j = (i + 1) % poly.vert_count as usize;
2609
2610 let vert1_idx = poly.verts[i] as usize;
2611 let vert2_idx = poly.verts[j] as usize;
2612
2613 let vert1 = [
2614 tile.verts[vert1_idx * 3],
2615 tile.verts[vert1_idx * 3 + 1],
2616 tile.verts[vert1_idx * 3 + 2],
2617 ];
2618 let vert2 = [
2619 tile.verts[vert2_idx * 3],
2620 tile.verts[vert2_idx * 3 + 1],
2621 tile.verts[vert2_idx * 3 + 2],
2622 ];
2623
2624 let has_connection = self.nav_mesh.find_link(tile, poly, i as u8).is_some();
2626
2627 if !has_connection {
2628 let closest_point = self.closest_point_on_segment(&vert1, &vert2, center_pos);
2630 let dx = closest_point[0] - center_pos[0];
2631 let dz = closest_point[2] - center_pos[2];
2632 let distance = (dx * dx + dz * dz).sqrt();
2633
2634 if distance < min_distance {
2635 min_distance = distance;
2636 wall_hit = closest_point;
2637
2638 if distance > 1e-6 {
2640 wall_normal = [dx / distance, 0.0, dz / distance];
2641 }
2642 }
2643 } else {
2644 if let Some(link) = self.nav_mesh.find_link(tile, poly, i as u8) {
2646 let neighbor_ref = link.reference;
2647 if neighbor_ref.is_valid() && !visited.contains(&neighbor_ref.id()) {
2648 if let Ok(neighbor_center) = self.get_poly_center(neighbor_ref) {
2650 let dx = neighbor_center[0] - center_pos[0];
2651 let dz = neighbor_center[2] - center_pos[2];
2652 let dist = (dx * dx + dz * dz).sqrt();
2653
2654 if dist <= max_radius + 5.0 {
2655 queue.push_back(neighbor_ref);
2657 visited.insert(neighbor_ref.id());
2658 }
2659 }
2660 }
2661 }
2662 }
2663 }
2664 }
2665
2666 Ok((min_distance, wall_hit, wall_normal))
2667 }
2668
2669 pub fn find_random_point(
2671 &mut self,
2672 filter: &QueryFilter,
2673 ) -> Result<(PolyRef, Vec3), DetourError> {
2674 self.find_random_point_around(PolyRef::new(0), Vec3::ZERO, f32::MAX, filter)
2675 }
2676
2677 pub fn find_random_point_with_custom_rand<F>(
2688 &mut self,
2689 filter: &QueryFilter,
2690 frand: F,
2691 ) -> Result<(PolyRef, Vec3), DetourError>
2692 where
2693 F: FnMut() -> f32,
2694 {
2695 self.find_random_point_around_circle(PolyRef::new(0), Vec3::ZERO, f32::MAX, filter, frand)
2696 }
2697
2698 pub fn get_poly_height(
2709 &self,
2710 poly_ref: PolyRef,
2711 pos: Vec3,
2712 ) -> Result<Option<f32>, DetourError> {
2713 let pos = pos.to_array();
2714 if !self.nav_mesh.is_valid_poly_ref(poly_ref) {
2716 return Err(DetourError::InvalidParam);
2717 }
2718
2719 let (tile, _poly) = self.nav_mesh.get_tile_and_poly_by_ref(poly_ref)?;
2721 let poly_idx = decode_poly_id(poly_ref) as usize;
2722
2723 self.nav_mesh.get_poly_height(tile, poly_idx, &pos)
2725 }
2726
2727 pub fn find_random_point_around_circle<F>(
2741 &mut self,
2742 center_ref: PolyRef,
2743 center_pos: Vec3,
2744 radius: f32,
2745 filter: &QueryFilter,
2746 mut frand: F,
2747 ) -> Result<(PolyRef, Vec3), DetourError>
2748 where
2749 F: FnMut() -> f32,
2750 {
2751 let saved_seed = self.random_seed;
2753
2754 let rand_val = frand();
2757 self.random_seed = (rand_val * 2147483647.0) as u32;
2758
2759 let result = self.find_random_point_around(center_ref, center_pos, radius, filter);
2761
2762 self.random_seed = saved_seed;
2764
2765 result
2766 }
2767
2768 pub fn find_random_point_around(
2770 &mut self,
2771 center_ref: PolyRef,
2772 center_pos: Vec3,
2773 radius: f32,
2774 filter: &QueryFilter,
2775 ) -> Result<(PolyRef, Vec3), DetourError> {
2776 let center_pos = center_pos.to_array();
2777 if !center_ref.is_valid() || !self.nav_mesh.is_valid_poly_ref(center_ref) {
2779 return self.find_random_point_on_mesh(filter);
2780 }
2781
2782 let polys_in_circle =
2784 self.find_polys_around_circle(center_ref, Vec3::from(center_pos), radius, filter)?;
2785
2786 if polys_in_circle.is_empty() {
2787 return Err(DetourError::InvalidParam);
2788 }
2789
2790 let random_index = (self.random_f32() * polys_in_circle.len() as f32) as usize;
2792 let random_poly = polys_in_circle[random_index];
2793
2794 match self.get_random_point_in_polygon(random_poly) {
2796 Ok(pos) => {
2797 let dx = pos[0] - center_pos[0];
2799 let dz = pos[2] - center_pos[2];
2800 let distance = (dx * dx + dz * dz).sqrt();
2801
2802 if distance <= radius {
2803 Ok((random_poly, Vec3::from(pos)))
2804 } else {
2805 match self.get_poly_center(random_poly) {
2807 Ok(center) => Ok((random_poly, center)),
2808 Err(e) => Err(e),
2809 }
2810 }
2811 }
2812 Err(_) => {
2813 match self.get_poly_center(random_poly) {
2815 Ok(center) => Ok((random_poly, center)),
2816 Err(e) => Err(e),
2817 }
2818 }
2819 }
2820 }
2821
2822 fn find_random_point_on_mesh(
2824 &mut self,
2825 filter: &QueryFilter,
2826 ) -> Result<(PolyRef, Vec3), DetourError> {
2827 let valid_polys = self.get_all_valid_polygons(filter)?;
2829
2830 if valid_polys.is_empty() {
2831 return Err(DetourError::InvalidParam);
2832 }
2833
2834 let random_index = (self.random_f32() * valid_polys.len() as f32) as usize;
2836 let random_poly = valid_polys[random_index];
2837
2838 match self.get_random_point_in_polygon(random_poly) {
2840 Ok(pos) => Ok((random_poly, Vec3::from(pos))),
2841 Err(_) => {
2842 match self.get_poly_center(random_poly) {
2844 Ok(center) => Ok((random_poly, center)),
2845 Err(e) => Err(e),
2846 }
2847 }
2848 }
2849 }
2850
2851 fn get_all_valid_polygons(&self, filter: &QueryFilter) -> Result<Vec<PolyRef>, DetourError> {
2853 let mut valid_polys = Vec::new();
2854
2855 if let Ok(start_polys) = self.nav_mesh.query_polygons(
2860 &[-1000.0, -1000.0, -1000.0],
2861 &[1000.0, 1000.0, 1000.0],
2862 filter,
2863 ) {
2864 valid_polys.extend(start_polys);
2866 }
2867
2868 if valid_polys.is_empty() {
2870 for tile_id in 0..10.min(self.nav_mesh.get_max_tiles()) {
2872 for poly_id in 0..100.min(self.nav_mesh.get_max_polys_per_tile()) {
2873 let poly_ref = super::nav_mesh::encode_poly_ref(tile_id as u32, poly_id as u32);
2874
2875 if self.nav_mesh.is_valid_poly_ref(poly_ref) {
2877 if let Ok((tile, poly)) = self.nav_mesh.get_tile_and_poly_by_ref(poly_ref) {
2879 if filter.pass_filter(poly_ref, tile, poly) {
2881 valid_polys.push(poly_ref);
2882 }
2883 }
2884 }
2885 }
2886 }
2887 }
2888
2889 Ok(valid_polys)
2890 }
2891
2892 fn get_random_point_in_polygon(&mut self, poly_ref: PolyRef) -> Result<[f32; 3], DetourError> {
2894 let (tile, poly) = self.nav_mesh.get_tile_and_poly_by_ref(poly_ref)?;
2895
2896 if poly.vert_count < 3 {
2897 return Err(DetourError::InvalidParam);
2898 }
2899
2900 let mut verts = Vec::with_capacity(poly.vert_count as usize);
2902 for i in 0..poly.vert_count as usize {
2903 let vert_idx = poly.verts[i] as usize;
2904 verts.push([
2905 tile.verts[vert_idx * 3],
2906 tile.verts[vert_idx * 3 + 1],
2907 tile.verts[vert_idx * 3 + 2],
2908 ]);
2909 }
2910
2911 if poly.vert_count == 3 {
2913 Ok(self.random_point_in_triangle(&verts[0], &verts[1], &verts[2]))
2915 } else {
2916 let triangle_count = poly.vert_count - 2;
2918 let random_triangle = (self.random_f32() * triangle_count as f32) as usize;
2919
2920 let v1_idx = 1 + random_triangle;
2922 let v2_idx = 2 + random_triangle;
2923
2924 if v1_idx < verts.len() && v2_idx < verts.len() {
2925 Ok(self.random_point_in_triangle(&verts[0], &verts[v1_idx], &verts[v2_idx]))
2926 } else {
2927 Ok(self.random_point_in_triangle(&verts[0], &verts[1], &verts[2]))
2929 }
2930 }
2931 }
2932
2933 fn random_point_in_triangle(&mut self, a: &[f32; 3], b: &[f32; 3], c: &[f32; 3]) -> [f32; 3] {
2935 let r1 = self.random_f32();
2936 let r2 = self.random_f32();
2937
2938 let sqrt_r1 = r1.sqrt();
2940 let u = 1.0 - sqrt_r1;
2941 let v = r2 * sqrt_r1;
2942 let w = 1.0 - u - v;
2943
2944 [
2945 a[0] * u + b[0] * v + c[0] * w,
2946 a[1] * u + b[1] * v + c[1] * w,
2947 a[2] * u + b[2] * v + c[2] * w,
2948 ]
2949 }
2950
2951 pub fn find_local_neighbourhood(
2953 &self,
2954 start_ref: PolyRef,
2955 center_pos: Vec3,
2956 radius: f32,
2957 filter: &QueryFilter,
2958 max_result: usize,
2959 ) -> Result<(Vec<PolyRef>, Vec<PolyRef>), DetourError> {
2960 let center_pos = center_pos.to_array();
2961 if !self.nav_mesh.is_valid_poly_ref(start_ref) || radius < 0.0 || max_result == 0 {
2962 return Err(DetourError::InvalidParam);
2963 }
2964
2965 let mut result_refs = Vec::new();
2966 let mut result_parents = Vec::new();
2967 let mut stack = Vec::new();
2968 let mut visited = std::collections::HashSet::new();
2969
2970 let radius_sqr = radius * radius;
2971 const MAX_STACK_SIZE: usize = 48;
2972
2973 stack.push(start_ref);
2975 visited.insert(start_ref.id());
2976
2977 result_refs.push(start_ref);
2979 result_parents.push(PolyRef::new(0)); while !stack.is_empty() && stack.len() < MAX_STACK_SIZE {
2982 let current_ref = stack.remove(0); let (current_tile, current_poly) =
2986 self.nav_mesh.get_tile_and_poly_by_ref(current_ref)?;
2987
2988 for i in 0..current_poly.vert_count as usize {
2990 if let Some(link) = self.nav_mesh.find_link(current_tile, current_poly, i as u8) {
2991 let neighbor_ref = link.reference;
2992
2993 if !neighbor_ref.is_valid() {
2995 continue;
2996 }
2997
2998 if visited.contains(&neighbor_ref.id()) {
3000 continue;
3001 }
3002
3003 let (neighbor_tile, neighbor_poly) =
3005 match self.nav_mesh.get_tile_and_poly_by_ref(neighbor_ref) {
3006 Ok((tile, poly)) => (tile, poly),
3007 Err(_) => continue,
3008 };
3009
3010 if neighbor_poly.poly_type == PolyType::OffMeshConnection {
3012 continue;
3013 }
3014
3015 if !filter.pass_filter(neighbor_ref, neighbor_tile, neighbor_poly) {
3017 continue;
3018 }
3019
3020 let (va_v, vb_v) = match self.get_portal_points(current_ref, neighbor_ref) {
3022 Ok((a, b)) => (a, b),
3023 Err(_) => continue,
3024 };
3025 let va = va_v.to_array();
3026 let vb = vb_v.to_array();
3027
3028 let closest_on_edge = self.closest_point_on_segment(&va, &vb, ¢er_pos);
3030 let dist_sqr = {
3031 let dx = center_pos[0] - closest_on_edge[0];
3032 let dy = center_pos[1] - closest_on_edge[1];
3033 let dz = center_pos[2] - closest_on_edge[2];
3034 dx * dx + dy * dy + dz * dz
3035 };
3036
3037 if dist_sqr > radius_sqr {
3039 continue;
3040 }
3041
3042 visited.insert(neighbor_ref.id());
3044
3045 let neighbor_verts = self.get_polygon_vertices(neighbor_tile, neighbor_poly)?;
3047 let mut overlaps = false;
3048
3049 for &existing_ref in &result_refs {
3050 if self.are_polygons_connected(current_ref, existing_ref)? {
3052 continue;
3053 }
3054
3055 let (existing_tile, existing_poly) =
3057 self.nav_mesh.get_tile_and_poly_by_ref(existing_ref)?;
3058 let existing_verts =
3059 self.get_polygon_vertices(existing_tile, existing_poly)?;
3060
3061 if self.polygons_overlap_2d(&neighbor_verts, &existing_verts) {
3063 overlaps = true;
3064 break;
3065 }
3066 }
3067
3068 if overlaps {
3070 continue;
3071 }
3072
3073 if result_refs.len() < max_result {
3075 result_refs.push(neighbor_ref);
3076 result_parents.push(current_ref);
3077 }
3078
3079 if stack.len() < MAX_STACK_SIZE {
3081 stack.push(neighbor_ref);
3082 }
3083 }
3084 }
3085 }
3086
3087 Ok((result_refs, result_parents))
3088 }
3089
3090 fn get_polygon_vertices(
3092 &self,
3093 tile: &super::nav_mesh::MeshTile,
3094 poly: &super::nav_mesh::Poly,
3095 ) -> Result<Vec<[f32; 3]>, DetourError> {
3096 let mut vertices = Vec::new();
3097
3098 for i in 0..poly.vert_count as usize {
3099 let vert_idx = poly.verts[i] as usize;
3100 vertices.push([
3101 tile.verts[vert_idx * 3],
3102 tile.verts[vert_idx * 3 + 1],
3103 tile.verts[vert_idx * 3 + 2],
3104 ]);
3105 }
3106
3107 Ok(vertices)
3108 }
3109
3110 fn are_polygons_connected(
3112 &self,
3113 poly_a: PolyRef,
3114 poly_b: PolyRef,
3115 ) -> Result<bool, DetourError> {
3116 let (tile_a, poly_a_data) = self.nav_mesh.get_tile_and_poly_by_ref(poly_a)?;
3117
3118 for i in 0..poly_a_data.vert_count as usize {
3120 if let Some(link) = self.nav_mesh.find_link(tile_a, poly_a_data, i as u8) {
3121 if link.reference == poly_b {
3122 return Ok(true);
3123 }
3124 }
3125 }
3126
3127 Ok(false)
3128 }
3129
3130 fn polygons_overlap_2d(&self, poly_a: &[[f32; 3]], poly_b: &[[f32; 3]]) -> bool {
3132 let poly_a_2d: Vec<[f32; 2]> = poly_a.iter().map(|v| [v[0], v[2]]).collect();
3134 let poly_b_2d: Vec<[f32; 2]> = poly_b.iter().map(|v| [v[0], v[2]]).collect();
3135
3136 self.convex_polygons_overlap_2d(&poly_a_2d, &poly_b_2d)
3138 }
3139
3140 fn convex_polygons_overlap_2d(&self, poly_a: &[[f32; 2]], poly_b: &[[f32; 2]]) -> bool {
3142 for i in 0..poly_a.len() {
3144 let j = (i + 1) % poly_a.len();
3145 let edge = [poly_a[j][0] - poly_a[i][0], poly_a[j][1] - poly_a[i][1]];
3146 let normal = [-edge[1], edge[0]]; if self.polygons_separated_by_axis(&normal, poly_a, poly_b) {
3149 return false; }
3151 }
3152
3153 for i in 0..poly_b.len() {
3155 let j = (i + 1) % poly_b.len();
3156 let edge = [poly_b[j][0] - poly_b[i][0], poly_b[j][1] - poly_b[i][1]];
3157 let normal = [-edge[1], edge[0]]; if self.polygons_separated_by_axis(&normal, poly_a, poly_b) {
3160 return false; }
3162 }
3163
3164 true }
3166
3167 fn polygons_separated_by_axis(
3169 &self,
3170 axis: &[f32; 2],
3171 poly_a: &[[f32; 2]],
3172 poly_b: &[[f32; 2]],
3173 ) -> bool {
3174 let mut min_a = f32::MAX;
3176 let mut max_a = f32::MIN;
3177 for vertex in poly_a {
3178 let projection = vertex[0] * axis[0] + vertex[1] * axis[1];
3179 min_a = min_a.min(projection);
3180 max_a = max_a.max(projection);
3181 }
3182
3183 let mut min_b = f32::MAX;
3185 let mut max_b = f32::MIN;
3186 for vertex in poly_b {
3187 let projection = vertex[0] * axis[0] + vertex[1] * axis[1];
3188 min_b = min_b.min(projection);
3189 max_b = max_b.max(projection);
3190 }
3191
3192 max_a < min_b || max_b < min_a
3194 }
3195
3196 pub fn init_sliced_find_path(
3211 &mut self,
3212 start_ref: PolyRef,
3213 end_ref: PolyRef,
3214 start_pos: Vec3,
3215 end_pos: Vec3,
3216 filter: &QueryFilter,
3217 options: u32,
3218 ) -> Result<(), DetourError> {
3219 let start_pos = start_pos.to_array();
3220 let end_pos = end_pos.to_array();
3221 if !self.nav_mesh.is_valid_poly_ref(start_ref) || !self.nav_mesh.is_valid_poly_ref(end_ref)
3222 {
3223 return Err(DetourError::InvalidParam);
3224 }
3225
3226 self.sliced_state.active = true;
3228 self.sliced_state.state = if start_ref == end_ref {
3229 SlicedPathState::Success
3230 } else {
3231 SlicedPathState::InProgress
3232 };
3233 self.sliced_state.start_ref = start_ref;
3234 self.sliced_state.end_ref = end_ref;
3235 self.sliced_state.start_pos = start_pos;
3236 self.sliced_state.end_pos = end_pos;
3237 self.sliced_state.filter = filter.clone();
3238 self.sliced_state.current_path.clear();
3239 self.sliced_state.best_node_idx = 0;
3240 self.sliced_state.best_node_cost = f32::MAX;
3241
3242 if start_ref == end_ref {
3244 self.sliced_state.current_path.push(start_ref);
3245 return Ok(());
3246 }
3247
3248 for node in &mut self.node_pool {
3249 node.parent = None;
3250 node.state = NodeState::New;
3251 node.poly = PolyRef::new(0);
3252 node.g = 0.0;
3253 node.h = 0.0;
3254 node.f = 0.0;
3255 }
3256 self.open_list.clear();
3257
3258 let start_h = self.heuristic(&start_pos, &end_pos);
3259 let start_node = &mut self.node_pool[0];
3260 start_node.poly = start_ref;
3261 start_node.state = NodeState::Open;
3262 start_node.g = 0.0;
3263 start_node.h = start_h;
3264 start_node.f = start_h;
3265
3266 self.open_list.push(HeapNode {
3268 index: 0,
3269 f: start_h,
3270 });
3271
3272 self.node_pool[1].poly = end_ref;
3274
3275 self.sliced_state.best_node_idx = 0;
3277 self.sliced_state.best_node_cost = start_h;
3278
3279 const DT_FINDPATH_ANY_ANGLE: u32 = 0x02;
3281 if options & DT_FINDPATH_ANY_ANGLE != 0 {
3282 }
3285
3286 Ok(())
3287 }
3288
3289 pub fn init_sliced_find_path_default(
3301 &mut self,
3302 start_ref: PolyRef,
3303 end_ref: PolyRef,
3304 start_pos: Vec3,
3305 end_pos: Vec3,
3306 filter: &QueryFilter,
3307 ) -> Result<(), DetourError> {
3308 self.init_sliced_find_path(start_ref, end_ref, start_pos, end_pos, filter, 0)
3309 }
3310
3311 pub fn update_sliced_find_path(
3322 &mut self,
3323 max_iter: i32,
3324 ) -> Result<(i32, SlicedPathState), DetourError> {
3325 if !self.sliced_state.active {
3326 return Err(DetourError::InvalidParam);
3327 }
3328
3329 if self.sliced_state.state != SlicedPathState::InProgress {
3330 return Ok((0, self.sliced_state.state));
3331 }
3332
3333 let mut iter_count = 0i32;
3334
3335 while iter_count < max_iter {
3336 let Some(HeapNode {
3338 index: current_idx, ..
3339 }) = self.open_list.pop()
3340 else {
3341 break;
3342 };
3343
3344 self.node_pool[current_idx].state = NodeState::Closed;
3346
3347 let current_poly = self.node_pool[current_idx].poly;
3348
3349 if current_poly == self.sliced_state.end_ref {
3351 self.sliced_state.state = SlicedPathState::Success;
3352 self.sliced_state.current_path = self.reconstruct_path(current_idx)?;
3353 return Ok((iter_count + 1, SlicedPathState::Success));
3354 }
3355
3356 let current_cost = self.node_pool[current_idx].h;
3358 if current_cost < self.sliced_state.best_node_cost {
3359 self.sliced_state.best_node_idx = current_idx;
3360 self.sliced_state.best_node_cost = current_cost;
3361 }
3362
3363 self.expand_neighbors_sliced(current_idx)?;
3365
3366 iter_count += 1;
3367 }
3368
3369 if self.open_list.is_empty() {
3371 self.sliced_state.state = SlicedPathState::PartialPath;
3372 self.sliced_state.current_path =
3373 self.reconstruct_path(self.sliced_state.best_node_idx)?;
3374 }
3375
3376 Ok((iter_count, self.sliced_state.state))
3377 }
3378
3379 pub fn finalize_sliced_find_path(
3387 &mut self,
3388 max_path: usize,
3389 ) -> Result<Vec<PolyRef>, DetourError> {
3390 if !self.sliced_state.active {
3391 return Err(DetourError::InvalidParam);
3392 }
3393
3394 let result = match self.sliced_state.state {
3395 SlicedPathState::Success | SlicedPathState::PartialPath => {
3396 let mut path = self.sliced_state.current_path.clone();
3397 if path.len() > max_path {
3398 path.truncate(max_path);
3399 }
3400 Ok(path)
3401 }
3402 SlicedPathState::InProgress => Err(DetourError::InProgress),
3403 SlicedPathState::Failed => Err(DetourError::PathNotFound),
3404 };
3405
3406 self.sliced_state.active = false;
3408 self.sliced_state.current_path.clear();
3409
3410 result
3411 }
3412
3413 pub fn finalize_sliced_find_path_partial(
3426 &mut self,
3427 existing: &[PolyRef],
3428 max_path: usize,
3429 ) -> Result<Vec<PolyRef>, DetourError> {
3430 if !self.sliced_state.active {
3431 return Err(DetourError::InvalidParam);
3432 }
3433
3434 if existing.is_empty() || max_path == 0 {
3435 return Err(DetourError::InvalidParam);
3436 }
3437
3438 if self.sliced_state.state == SlicedPathState::Failed {
3440 self.sliced_state.active = false;
3441 return Err(DetourError::PathNotFound);
3442 }
3443
3444 let mut path = Vec::with_capacity(max_path);
3445
3446 if self.sliced_state.start_ref == self.sliced_state.end_ref {
3448 path.push(self.sliced_state.start_ref);
3449 } else {
3450 let mut found_node_idx = None;
3452
3453 for &poly_ref in existing.iter().rev() {
3455 for (idx, node) in self.node_pool.iter().enumerate() {
3456 if node.poly == poly_ref && node.state != NodeState::New {
3457 found_node_idx = Some(idx);
3458 break;
3459 }
3460 }
3461 if found_node_idx.is_some() {
3462 break;
3463 }
3464 }
3465
3466 let node_idx = found_node_idx.unwrap_or(self.sliced_state.best_node_idx);
3468
3469 let mut current_idx = node_idx;
3471 let mut reverse_path = Vec::new();
3472
3473 while current_idx < self.node_pool.len() {
3474 let node = &self.node_pool[current_idx];
3475 reverse_path.push(node.poly);
3476
3477 if let Some(parent_idx) = node.parent {
3478 current_idx = parent_idx;
3479 } else {
3480 break;
3481 }
3482
3483 if reverse_path.len() >= max_path {
3485 break;
3486 }
3487 }
3488
3489 path.extend(reverse_path.iter().rev().copied());
3491
3492 if path.len() > max_path {
3494 path.truncate(max_path);
3495 }
3496 }
3497
3498 self.sliced_state.active = false;
3500 self.sliced_state.current_path.clear();
3501
3502 Ok(path)
3503 }
3504
3505 pub fn get_sliced_path_state(&self) -> Option<SlicedPathState> {
3510 if self.sliced_state.active {
3511 Some(self.sliced_state.state)
3512 } else {
3513 None
3514 }
3515 }
3516
3517 pub fn cancel_sliced_find_path(&mut self) {
3519 self.sliced_state.active = false;
3520 self.sliced_state.current_path.clear();
3521 }
3522
3523 fn expand_neighbors_sliced(&mut self, current_idx: usize) -> Result<(), DetourError> {
3525 let current_poly = self.node_pool[current_idx].poly;
3526 let current_g = self.node_pool[current_idx].g;
3527
3528 let (tile, poly) = self.nav_mesh.get_tile_and_poly_by_ref(current_poly)?;
3530
3531 if let Some(first_link) = poly.first_link {
3533 for i in 0..poly.vert_count {
3534 let link_idx = first_link + i as usize;
3535 if link_idx >= tile.links.len() {
3536 continue;
3537 }
3538
3539 let link = &tile.links[link_idx];
3540 if !link.reference.is_valid() {
3541 continue;
3542 }
3543
3544 let neighbor_ref = link.reference;
3545
3546 let (neighbor_tile, neighbor_poly) =
3548 match self.nav_mesh.get_tile_and_poly_by_ref(neighbor_ref) {
3549 Ok((tile, poly)) => (tile, poly),
3550 Err(_) => continue, };
3552
3553 if !self
3554 .sliced_state
3555 .filter
3556 .pass_filter(neighbor_ref, neighbor_tile, neighbor_poly)
3557 {
3558 continue;
3559 }
3560
3561 let neighbor_idx = self.find_or_create_node(neighbor_ref)?;
3563 let neighbor_node = &self.node_pool[neighbor_idx];
3564
3565 if neighbor_node.state == NodeState::Closed {
3567 continue;
3568 }
3569
3570 let movement_cost = 1.0; let tentative_g = current_g + movement_cost;
3573
3574 if neighbor_node.state == NodeState::New || tentative_g < neighbor_node.g {
3576 let neighbor_pos = self.get_poly_center(neighbor_ref)?.to_array();
3577 let h = self.heuristic(&neighbor_pos, &self.sliced_state.end_pos);
3578
3579 let neighbor_node = &mut self.node_pool[neighbor_idx];
3580 neighbor_node.parent = Some(current_idx);
3581 neighbor_node.g = tentative_g;
3582 neighbor_node.h = h;
3583 neighbor_node.f = tentative_g + h;
3584
3585 if neighbor_node.state == NodeState::New {
3586 neighbor_node.state = NodeState::Open;
3587 self.open_list.push(HeapNode {
3588 index: neighbor_idx,
3589 f: neighbor_node.f,
3590 });
3591 }
3592 }
3593 }
3594 }
3595
3596 Ok(())
3597 }
3598
3599 fn find_or_create_node(&mut self, poly_ref: PolyRef) -> Result<usize, DetourError> {
3601 for (i, node) in self.node_pool.iter().enumerate() {
3603 if node.poly == poly_ref {
3604 return Ok(i);
3605 }
3606 }
3607
3608 for (i, node) in self.node_pool.iter().enumerate() {
3610 if node.state == NodeState::New && !node.poly.is_valid() {
3611 let node = &mut self.node_pool[i];
3612 node.poly = poly_ref;
3613 return Ok(i);
3614 }
3615 }
3616
3617 Err(DetourError::OutOfMemory)
3619 }
3620
3621 fn reconstruct_path(&self, goal_idx: usize) -> Result<Vec<PolyRef>, DetourError> {
3623 let mut path = Vec::new();
3624 let mut current_idx = goal_idx;
3625
3626 loop {
3627 let node = &self.node_pool[current_idx];
3628 path.push(node.poly);
3629
3630 if let Some(parent_idx) = node.parent {
3631 current_idx = parent_idx;
3632 } else {
3633 break;
3634 }
3635 }
3636
3637 path.reverse();
3638 Ok(path)
3639 }
3640
3641 pub fn find_polys_around_shape(
3680 &mut self,
3681 start_ref: PolyRef,
3682 verts: &[Vec3],
3683 filter: &QueryFilter,
3684 ) -> Result<Vec<(PolyRef, Option<PolyRef>, f32)>, DetourError> {
3685 use landmark_common::intersect_segment_poly_2d;
3686
3687 if !self.nav_mesh.is_valid_poly_ref(start_ref) {
3688 return Err(DetourError::InvalidParam);
3689 }
3690
3691 if verts.len() < 3 {
3692 return Err(DetourError::InvalidParam);
3693 }
3694
3695 let mut center = [0.0, 0.0, 0.0];
3697 for vert in verts {
3698 let v = vert.to_array();
3699 center[0] += v[0];
3700 center[1] += v[1];
3701 center[2] += v[2];
3702 }
3703 let nverts = verts.len() as f32;
3704 center[0] /= nverts;
3705 center[1] /= nverts;
3706 center[2] /= nverts;
3707
3708 let mut flat_verts = Vec::with_capacity(verts.len() * 3);
3710 for vert in verts {
3711 let v = vert.to_array();
3712 flat_verts.push(v[0]);
3713 flat_verts.push(v[1]);
3714 flat_verts.push(v[2]);
3715 }
3716
3717 self.node_pool.clear();
3719 self.open_list.clear();
3720
3721 let start_node = Node::new(start_ref, 0);
3722 self.node_pool.push(start_node);
3723 self.node_pool[0].g = 0.0;
3724 self.node_pool[0].f = 0.0;
3725 self.node_pool[0].state = NodeState::Open;
3726 self.open_list.push(HeapNode { index: 0, f: 0.0 });
3727
3728 let mut result = Vec::new();
3729
3730 while let Some(heap_node) = self.open_list.pop() {
3731 let best_idx = heap_node.index;
3732 if best_idx >= self.node_pool.len() {
3733 continue;
3734 }
3735
3736 self.node_pool[best_idx].state = NodeState::Closed;
3738
3739 let best_ref = self.node_pool[best_idx].poly;
3740 let best_cost = self.node_pool[best_idx].g;
3741
3742 let (best_tile, best_poly) = match self.nav_mesh.get_tile_and_poly_by_ref(best_ref) {
3744 Ok(val) => val,
3745 Err(_) => continue,
3746 };
3747
3748 let parent_ref = self.node_pool[best_idx]
3750 .parent
3751 .map(|idx| self.node_pool[idx].poly);
3752
3753 result.push((best_ref, parent_ref, best_cost));
3755
3756 for i in 0..best_poly.vert_count as usize {
3758 let link = match self.nav_mesh.find_link(best_tile, best_poly, i as u8) {
3759 Some(l) => l,
3760 None => continue,
3761 };
3762
3763 let neighbor_ref = link.reference;
3764
3765 if !neighbor_ref.is_valid() || Some(neighbor_ref) == parent_ref {
3767 continue;
3768 }
3769
3770 let (neighbor_tile, neighbor_poly) =
3772 match self.nav_mesh.get_tile_and_poly_by_ref(neighbor_ref) {
3773 Ok(val) => val,
3774 Err(_) => continue,
3775 };
3776
3777 if !filter.pass_filter(neighbor_ref, neighbor_tile, neighbor_poly) {
3779 continue;
3780 }
3781
3782 let (va_v, vb_v) = match self.get_portal_points(best_ref, neighbor_ref) {
3784 Ok(val) => val,
3785 Err(_) => continue,
3786 };
3787 let va = va_v.to_array();
3788 let vb = vb_v.to_array();
3789
3790 match intersect_segment_poly_2d(&va, &vb, &flat_verts, verts.len()) {
3792 Some((tmin, tmax, _, _)) => {
3793 if tmin > 1.0 || tmax < 0.0 {
3795 continue;
3796 }
3797 }
3798 None => continue,
3799 }
3800
3801 let edge_mid = [
3803 (va[0] + vb[0]) * 0.5,
3804 (va[1] + vb[1]) * 0.5,
3805 (va[2] + vb[2]) * 0.5,
3806 ];
3807
3808 let (parent_tile, parent_poly) = if let Some(parent_ref) = parent_ref {
3811 match self.nav_mesh.get_tile_and_poly_by_ref(parent_ref) {
3812 Ok((t, p)) => (Some(t), Some(p)),
3813 Err(_) => (None, None),
3814 }
3815 } else {
3816 (None, None)
3817 };
3818
3819 let cost = filter.get_cost(
3820 ¢er,
3821 &edge_mid,
3822 parent_ref.unwrap_or(PolyRef::new(0)),
3823 parent_tile,
3824 parent_poly,
3825 best_ref,
3826 best_tile,
3827 best_poly,
3828 neighbor_ref,
3829 Some(neighbor_tile),
3830 Some(neighbor_poly),
3831 );
3832
3833 let total = best_cost + cost;
3834
3835 let mut neighbor_idx = None;
3837 for (idx, node) in self.node_pool.iter().enumerate() {
3838 if node.poly == neighbor_ref {
3839 neighbor_idx = Some(idx);
3840 break;
3841 }
3842 }
3843
3844 if let Some(idx) = neighbor_idx {
3845 if self.node_pool[idx].state == NodeState::Closed {
3847 continue;
3848 }
3849
3850 if total < self.node_pool[idx].g {
3852 self.node_pool[idx].parent = Some(best_idx);
3853 self.node_pool[idx].g = total;
3854 self.node_pool[idx].f = total;
3855
3856 if self.node_pool[idx].state == NodeState::Open {
3857 self.open_list.push(HeapNode {
3859 index: idx,
3860 f: total,
3861 });
3862 } else {
3863 self.node_pool[idx].state = NodeState::Open;
3864 self.open_list.push(HeapNode {
3865 index: idx,
3866 f: total,
3867 });
3868 }
3869 }
3870 } else {
3871 if self.node_pool.len() >= DT_MAX_NODES {
3873 continue;
3874 }
3875
3876 let mut new_node = Node::new(neighbor_ref, self.node_pool.len());
3877 new_node.parent = Some(best_idx);
3878 new_node.g = total;
3879 new_node.f = total;
3880 new_node.state = NodeState::Open;
3881
3882 let node_idx = self.node_pool.len();
3883 self.node_pool.push(new_node);
3884 self.open_list.push(HeapNode {
3885 index: node_idx,
3886 f: total,
3887 });
3888 }
3889 }
3890 }
3891
3892 Ok(result)
3893 }
3894
3895 pub fn closest_point_on_poly_boundary(
3906 &self,
3907 poly_ref: PolyRef,
3908 pos: Vec3,
3909 ) -> Result<Vec3, DetourError> {
3910 use landmark_common::distance_pt_poly_edges_sqr;
3911
3912 let pos = pos.to_array();
3913 let (tile, poly) = self.nav_mesh.get_tile_and_poly_by_ref(poly_ref)?;
3914
3915 let mut verts = Vec::with_capacity(poly.vert_count as usize * 3);
3917 for i in 0..poly.vert_count as usize {
3918 let idx = poly.verts[i] as usize;
3919 verts.push(tile.verts[idx * 3]);
3920 verts.push(tile.verts[idx * 3 + 1]);
3921 verts.push(tile.verts[idx * 3 + 2]);
3922 }
3923
3924 let (inside, edge_dists, edge_ts) =
3925 distance_pt_poly_edges_sqr(&pos, &verts, poly.vert_count as usize);
3926
3927 if inside {
3928 Ok(Vec3::from(pos))
3930 } else {
3931 let mut min_dist = edge_dists[0];
3933 let mut min_idx = 0;
3934
3935 for (i, &dist) in edge_dists.iter().enumerate().skip(1) {
3936 if dist < min_dist {
3937 min_dist = dist;
3938 min_idx = i;
3939 }
3940 }
3941
3942 let va_idx = min_idx * 3;
3944 let vb_idx = ((min_idx + 1) % poly.vert_count as usize) * 3;
3945
3946 let va = [verts[va_idx], verts[va_idx + 1], verts[va_idx + 2]];
3947 let vb = [verts[vb_idx], verts[vb_idx + 1], verts[vb_idx + 2]];
3948 let t = edge_ts[min_idx];
3949
3950 Ok(Vec3::from([
3952 va[0] + (vb[0] - va[0]) * t,
3953 va[1] + (vb[1] - va[1]) * t,
3954 va[2] + (vb[2] - va[2]) * t,
3955 ]))
3956 }
3957 }
3958
3959 pub fn get_edge_mid_point(
3970 &self,
3971 from_ref: PolyRef,
3972 to_ref: PolyRef,
3973 ) -> Result<Vec3, DetourError> {
3974 let (left, right) = self.get_portal_points(from_ref, to_ref)?;
3975
3976 Ok((left + right) * 0.5)
3978 }
3979
3980 pub fn query_polygons(
3997 &self,
3998 center: Vec3,
3999 half_extents: Vec3,
4000 filter: &QueryFilter,
4001 max_polys: usize,
4002 ) -> Result<Vec<PolyRef>, DetourError> {
4003 let mut collect_query = super::poly_query::CollectPolysQuery::new(max_polys);
4004 self.query_polygons_with_query(center, half_extents, filter, &mut collect_query)?;
4005 Ok(collect_query.polys().to_vec())
4006 }
4007
4008 pub fn query_polygons_with_query(
4012 &self,
4013 center: Vec3,
4014 half_extents: Vec3,
4015 filter: &QueryFilter,
4016 query: &mut dyn super::poly_query::PolyQuery,
4017 ) -> Result<(), DetourError> {
4018 let center = center.to_array();
4019 let half_extents = half_extents.to_array();
4020 let bmin = [
4022 center[0] - half_extents[0],
4023 center[1] - half_extents[1],
4024 center[2] - half_extents[2],
4025 ];
4026 let bmax = [
4027 center[0] + half_extents[0],
4028 center[1] + half_extents[1],
4029 center[2] + half_extents[2],
4030 ];
4031
4032 let tiles = self.nav_mesh.get_tiles_in_bounds(&bmin, &bmax)?;
4034
4035 for tile in tiles {
4037 let mut batch_polys = Vec::new();
4038 let mut batch_refs = Vec::new();
4039 const BATCH_SIZE: usize = 32;
4040
4041 for (poly_idx, poly) in tile.polys.iter().enumerate() {
4043 let poly_ref = self.nav_mesh.get_poly_ref_base(tile) | (poly_idx as u32);
4045 let poly_ref = PolyRef::new(poly_ref);
4046
4047 if !filter.pass_filter(poly_ref, tile, poly) {
4049 continue;
4050 }
4051
4052 let poly_bounds = self.nav_mesh.get_poly_bounds(tile, poly)?;
4054 if !Self::overlap_bounds(&poly_bounds.0, &poly_bounds.1, &bmin, &bmax) {
4055 continue;
4056 }
4057
4058 batch_polys.push(poly);
4060 batch_refs.push(poly_ref);
4061
4062 if batch_polys.len() >= BATCH_SIZE {
4064 query.process(tile, &batch_polys, &batch_refs);
4065 batch_polys.clear();
4066 batch_refs.clear();
4067 }
4068 }
4069
4070 if !batch_polys.is_empty() {
4072 query.process(tile, &batch_polys, &batch_refs);
4073 }
4074 }
4075
4076 Ok(())
4077 }
4078
4079 fn overlap_bounds(amin: &[f32; 3], amax: &[f32; 3], bmin: &[f32; 3], bmax: &[f32; 3]) -> bool {
4081 !(amin[0] > bmax[0]
4082 || amax[0] < bmin[0]
4083 || amin[1] > bmax[1]
4084 || amax[1] < bmin[1]
4085 || amin[2] > bmax[2]
4086 || amax[2] < bmin[2])
4087 }
4088
4089 pub fn get_poly_wall_segments(
4090 &self,
4091 poly_ref: PolyRef,
4092 filter: &QueryFilter,
4093 max_segments: usize,
4094 ) -> Result<Vec<(Vec3, Vec3)>, DetourError> {
4095 if !poly_ref.is_valid() {
4096 return Err(DetourError::InvalidParam);
4097 }
4098
4099 let (tile, poly) = self.nav_mesh.get_tile_and_poly_by_ref(poly_ref)?;
4100
4101 if !filter.pass_filter(poly_ref, tile, poly) {
4103 return Ok(Vec::new());
4104 }
4105
4106 let mut wall_segments = Vec::new();
4107
4108 for i in 0..poly.vert_count as usize {
4110 if wall_segments.len() >= max_segments {
4111 break;
4112 }
4113
4114 let curr_vert_idx = poly.verts[i] as usize;
4116 let next_vert_idx = poly.verts[(i + 1) % poly.vert_count as usize] as usize;
4117
4118 if curr_vert_idx * 3 + 2 >= tile.verts.len()
4120 || next_vert_idx * 3 + 2 >= tile.verts.len()
4121 {
4122 continue;
4123 }
4124
4125 let curr_vert = [
4126 tile.verts[curr_vert_idx * 3],
4127 tile.verts[curr_vert_idx * 3 + 1],
4128 tile.verts[curr_vert_idx * 3 + 2],
4129 ];
4130 let next_vert = [
4131 tile.verts[next_vert_idx * 3],
4132 tile.verts[next_vert_idx * 3 + 1],
4133 tile.verts[next_vert_idx * 3 + 2],
4134 ];
4135
4136 let is_wall = if let Some(link) = self.nav_mesh.find_link(tile, poly, i as u8) {
4139 if link.reference.is_valid() {
4141 if let Ok((neighbor_tile, neighbor_poly)) =
4143 self.nav_mesh.get_tile_and_poly_by_ref(link.reference)
4144 {
4145 !filter.pass_filter(link.reference, neighbor_tile, neighbor_poly)
4147 } else {
4148 true
4150 }
4151 } else {
4152 true
4154 }
4155 } else {
4156 true
4158 };
4159
4160 if is_wall {
4161 wall_segments.push((Vec3::from(curr_vert), Vec3::from(next_vert)));
4163 }
4164 }
4165
4166 Ok(wall_segments)
4167 }
4168
4169 pub fn is_valid_poly_ref(&self, poly_ref: PolyRef, filter: &QueryFilter) -> bool {
4171 if !poly_ref.is_valid() {
4172 return false;
4173 }
4174
4175 if let Ok((tile, poly)) = self.nav_mesh.get_tile_and_poly_by_ref(poly_ref) {
4177 filter.pass_filter(poly_ref, tile, poly)
4179 } else {
4180 false
4181 }
4182 }
4183
4184 pub fn is_in_closed_list(&self, poly_ref: PolyRef) -> bool {
4186 for node in &self.node_pool {
4188 if node.poly == poly_ref && node.state == NodeState::Closed {
4189 return true;
4190 }
4191 }
4192 false
4193 }
4194
4195 pub fn query_polygons_custom<Q: super::poly_query::PolyQuery>(
4230 &self,
4231 center: Vec3,
4232 extents: Vec3,
4233 filter: &QueryFilter,
4234 query: &mut Q,
4235 ) -> Result<(), DetourError> {
4236 self.query_polygons_with_query(center, extents, filter, query)
4237 }
4238
4239 pub fn get_node_pool(&self) -> &[Node] {
4244 &self.node_pool
4245 }
4246
4247 pub fn get_attached_nav_mesh(&self) -> &NavMesh {
4249 self.nav_mesh
4250 }
4251}
4252
4253#[cfg(test)]
4254mod tests {
4255 use super::super::{
4256 NavMeshBuilder, NavMeshCreateParams, NavMeshParams, PolyFlags, encode_poly_ref,
4257 };
4258 use super::*;
4259 use crate::{CollectPolysQuery, FindNearestPolyQuery, nav_mesh::encode_poly_ref_with_salt};
4260 use landmark::MESH_NULL_IDX;
4261
4262 #[test]
4263 fn test_basic_query() {
4264 let params = NavMeshParams {
4266 origin: [0.0, 0.0, 0.0],
4267 tile_width: 100.0,
4268 tile_height: 100.0,
4269 max_tiles: 128,
4270 max_polys_per_tile: 1024,
4271 };
4272
4273 let nav_mesh = NavMesh::new(params).unwrap();
4274
4275 let query = NavMeshQuery::new(&nav_mesh);
4277
4278 assert_eq!(query.get_query_extent(), Vec3::new(2.0, 4.0, 2.0));
4280 }
4281
4282 #[test]
4283 fn test_set_query_extent() {
4284 let params = NavMeshParams {
4286 origin: [0.0, 0.0, 0.0],
4287 tile_width: 100.0,
4288 tile_height: 100.0,
4289 max_tiles: 128,
4290 max_polys_per_tile: 1024,
4291 };
4292
4293 let nav_mesh = NavMesh::new(params).unwrap();
4294
4295 let mut query = NavMeshQuery::new(&nav_mesh);
4297
4298 let extent = Vec3::new(10.0, 20.0, 10.0);
4300 query.set_query_extent(extent);
4301
4302 assert_eq!(query.get_query_extent(), extent);
4304 }
4305
4306 #[test]
4307 fn test_pathfinding_with_off_mesh_connections() {
4308 use super::super::{NavMeshParams, PolyFlags};
4309
4310 let params = NavMeshParams {
4312 origin: [0.0, 0.0, 0.0],
4313 tile_width: 100.0,
4314 tile_height: 100.0,
4315 max_tiles: 128,
4316 max_polys_per_tile: 1024,
4317 };
4318
4319 let mut nav_mesh = NavMesh::new(params).unwrap();
4320
4321 nav_mesh.create_test_tile(0, 0, 0).unwrap();
4323
4324 let connection_ref = nav_mesh
4326 .add_off_mesh_connection(
4327 [10.0, 0.0, 10.0], [20.0, 0.0, 20.0], 2.0, PolyFlags::WALK, 1, 0, 42, )
4335 .unwrap();
4336
4337 let query = NavMeshQuery::new(&nav_mesh);
4339
4340 assert!(nav_mesh.is_off_mesh_connection(connection_ref));
4342
4343 let (closest, _is_over) = query
4345 .closest_point_on_off_mesh_connection(connection_ref, &[15.0, 0.0, 15.0])
4346 .unwrap();
4347
4348 assert!(closest[0] >= 10.0 && closest[0] <= 20.0);
4350 assert!(closest[2] >= 10.0 && closest[2] <= 20.0);
4351
4352 let endpoint = query
4354 .get_off_mesh_connection_endpoint(
4355 connection_ref,
4356 Vec3::new(11.0, 0.0, 11.0), )
4358 .unwrap();
4359
4360 assert_eq!(endpoint, Vec3::new(20.0, 0.0, 20.0));
4362
4363 let endpoint2 = query
4365 .get_off_mesh_connection_endpoint(
4366 connection_ref,
4367 Vec3::new(19.0, 0.0, 19.0), )
4369 .unwrap();
4370
4371 assert_eq!(endpoint2, Vec3::new(10.0, 0.0, 10.0));
4373 }
4374
4375 #[test]
4376 fn test_off_mesh_connection_cost_calculation() {
4377 use super::super::{NavMeshParams, PolyFlags, QueryFilter};
4378
4379 let params = NavMeshParams {
4380 origin: [0.0, 0.0, 0.0],
4381 tile_width: 100.0,
4382 tile_height: 100.0,
4383 max_tiles: 128,
4384 max_polys_per_tile: 1024,
4385 };
4386
4387 let mut nav_mesh = NavMesh::new(params).unwrap();
4388
4389 nav_mesh.create_test_tile(0, 0, 0).unwrap();
4391
4392 let connection_ref = nav_mesh
4394 .add_off_mesh_connection(
4395 [0.0, 0.0, 0.0],
4396 [10.0, 0.0, 0.0],
4397 1.0,
4398 PolyFlags::WALK,
4399 5,
4400 0,
4401 1,
4402 )
4403 .unwrap();
4404
4405 let query = NavMeshQuery::new(&nav_mesh);
4406 let mut filter = QueryFilter::default();
4407 filter.area_cost[5] = 2.0; let cost = query
4411 .get_off_mesh_connection_cost(
4412 PolyRef::new(1), connection_ref,
4414 &[0.0, 0.0, 0.0],
4415 &[10.0, 0.0, 0.0],
4416 &filter,
4417 )
4418 .unwrap();
4419
4420 assert!((cost - 20.0).abs() < 1e-6);
4422 }
4423
4424 #[test]
4425 fn test_off_mesh_connection_direction_constraints() {
4426 use super::super::{NavMeshParams, PolyFlags};
4427
4428 let params = NavMeshParams {
4429 origin: [0.0, 0.0, 0.0],
4430 tile_width: 100.0,
4431 tile_height: 100.0,
4432 max_tiles: 128,
4433 max_polys_per_tile: 1024,
4434 };
4435
4436 let mut nav_mesh = NavMesh::new(params).unwrap();
4437
4438 nav_mesh.create_test_tile(0, 0, 0).unwrap();
4440
4441 let connection_ref = nav_mesh
4443 .add_off_mesh_connection(
4444 [0.0, 0.0, 0.0],
4445 [10.0, 0.0, 0.0],
4446 1.0,
4447 PolyFlags::WALK,
4448 1,
4449 1,
4450 1, )
4452 .unwrap();
4453
4454 let query = NavMeshQuery::new(&nav_mesh);
4455
4456 let endpoint1 = query.get_off_mesh_connection_endpoint(
4458 connection_ref,
4459 Vec3::new(1.0, 0.0, 0.0), );
4461 assert!(endpoint1.is_ok());
4462 assert_eq!(endpoint1.unwrap(), Vec3::new(10.0, 0.0, 0.0));
4463
4464 let endpoint2 = query.get_off_mesh_connection_endpoint(
4466 connection_ref,
4467 Vec3::new(9.0, 0.0, 0.0), );
4469 assert!(endpoint2.is_err()); }
4471
4472 #[test]
4473 fn test_off_mesh_connection_poly_end_points() {
4474 use super::super::{NavMeshParams, PolyFlags};
4475
4476 let params = NavMeshParams {
4477 origin: [0.0, 0.0, 0.0],
4478 tile_width: 100.0,
4479 tile_height: 100.0,
4480 max_tiles: 128,
4481 max_polys_per_tile: 1024,
4482 };
4483
4484 let mut nav_mesh = NavMesh::new(params).unwrap();
4485
4486 nav_mesh.create_test_tile(0, 0, 0).unwrap();
4488
4489 let connection_ref = nav_mesh
4491 .add_off_mesh_connection(
4492 [10.0, 0.0, 10.0], [20.0, 0.0, 20.0], 2.0,
4495 PolyFlags::WALK,
4496 1,
4497 0, 42,
4499 )
4500 .unwrap();
4501
4502 let query = NavMeshQuery::new(&nav_mesh);
4503
4504 let (start, end) = query
4506 .get_off_mesh_connection_poly_end_points(PolyRef::new(0), connection_ref)
4507 .unwrap();
4508 assert_eq!(start, Vec3::new(10.0, 0.0, 10.0));
4509 assert_eq!(end, Vec3::new(20.0, 0.0, 20.0));
4510
4511 let mock_prev_ref = PolyRef::new(1);
4514 let result = query.get_off_mesh_connection_poly_end_points(mock_prev_ref, connection_ref);
4515 if let Ok((start, end)) = result {
4518 let a = Vec3::new(10.0, 0.0, 10.0);
4519 let b = Vec3::new(20.0, 0.0, 20.0);
4520 assert!((start == a && end == b) || (start == b && end == a));
4522 }
4523 }
4525
4526 #[test]
4527 fn test_off_mesh_connection_straight_path_integration() {
4528 use super::super::{NavMeshParams, PolyFlags};
4529
4530 let params = NavMeshParams {
4531 origin: [0.0, 0.0, 0.0],
4532 tile_width: 100.0,
4533 tile_height: 100.0,
4534 max_tiles: 128,
4535 max_polys_per_tile: 1024,
4536 };
4537
4538 let mut nav_mesh = NavMesh::new(params).unwrap();
4539
4540 nav_mesh.create_test_tile(0, 0, 0).unwrap();
4542
4543 let connection_ref = nav_mesh
4545 .add_off_mesh_connection(
4546 [5.0, 0.0, 5.0],
4547 [15.0, 0.0, 15.0],
4548 1.0,
4549 PolyFlags::WALK,
4550 1,
4551 0, 1,
4553 )
4554 .unwrap();
4555
4556 let query = NavMeshQuery::new(&nav_mesh);
4557
4558 let path = vec![PolyRef::new(1), connection_ref, PolyRef::new(2)];
4560
4561 let result = query.find_straight_path(Vec3::ZERO, Vec3::new(20.0, 0.0, 20.0), &path);
4563
4564 match result {
4567 Ok(straight_path) => {
4568 assert!(straight_path.waypoints.len() >= 2);
4570 assert_eq!(straight_path.waypoints[0], Vec3::ZERO);
4571 assert_eq!(
4572 straight_path.waypoints[straight_path.waypoints.len() - 1],
4573 Vec3::new(20.0, 0.0, 20.0)
4574 );
4575 }
4576 Err(_) => {
4577 }
4580 }
4581 }
4582
4583 #[test]
4584 fn test_get_poly_wall_segments() -> std::result::Result<(), Box<dyn std::error::Error>> {
4585 let nav_params = NavMeshParams {
4587 origin: [0.0, 0.0, 0.0],
4588 tile_width: 32.0,
4589 tile_height: 32.0,
4590 max_tiles: 1,
4591 max_polys_per_tile: 256,
4592 };
4593
4594 let mut nav_mesh = NavMesh::new(nav_params.clone())?;
4595
4596 let params = NavMeshCreateParams {
4598 nav_mesh_params: nav_params,
4599 verts: vec![
4600 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 10.0, 0.0, 10.0, 0.0, 0.0, 10.0, ],
4605 vert_count: 4,
4606 polys: vec![
4607 0,
4608 1,
4609 2,
4610 3, MESH_NULL_IDX, MESH_NULL_IDX, MESH_NULL_IDX, MESH_NULL_IDX, ],
4616 poly_flags: vec![PolyFlags::WALK],
4617 poly_areas: vec![0],
4618 poly_count: 1,
4619 nvp: 4,
4620 detail_meshes: vec![0, 0, 0, 2],
4621 detail_verts: vec![],
4622 detail_vert_count: 0,
4623 detail_tris: vec![0, 1, 2, 0, 2, 3],
4624 detail_tri_count: 2,
4625 off_mesh_con_verts: vec![],
4626 off_mesh_con_rad: vec![],
4627 off_mesh_con_flags: vec![],
4628 off_mesh_con_areas: vec![],
4629 off_mesh_con_dir: vec![],
4630 off_mesh_con_user_id: vec![],
4631 off_mesh_con_count: 0,
4632 bmin: [0.0, 0.0, 0.0],
4633 bmax: [10.0, 0.0, 10.0],
4634 walkable_height: 2.0,
4635 walkable_radius: 0.6,
4636 walkable_climb: 0.9,
4637 cs: 0.3,
4638 ch: 0.2,
4639 build_bv_tree: true,
4640 };
4641
4642 let tile = NavMeshBuilder::build_tile(¶ms)?;
4643 let _tile_ref = nav_mesh.add_mesh_tile(tile)?;
4644
4645 let query = NavMeshQuery::new(&nav_mesh);
4647 let filter = QueryFilter::default();
4648
4649 let center = [5.0, 0.0, 5.0];
4651 let half_extents = [5.0, 5.0, 5.0];
4652 let (poly_ref, _) =
4653 query.find_nearest_poly(Vec3::from(center), Vec3::from(half_extents), &filter)?;
4654
4655 let wall_segments = query.get_poly_wall_segments(poly_ref, &filter, 10)?;
4657
4658 assert_eq!(wall_segments.len(), 4);
4660
4661 let expected_edges = [
4663 ([0.0, 0.0, 0.0], [10.0, 0.0, 0.0]), ([10.0, 0.0, 0.0], [10.0, 0.0, 10.0]), ([10.0, 0.0, 10.0], [0.0, 0.0, 10.0]), ([0.0, 0.0, 10.0], [0.0, 0.0, 0.0]), ];
4668
4669 for (i, segment) in wall_segments.iter().enumerate() {
4670 let start = segment.0.to_array();
4671 let end = segment.1.to_array();
4672 let expected = expected_edges[i];
4673
4674 for j in 0..3 {
4676 assert!(
4677 (start[j] - expected.0[j]).abs() < 0.001,
4678 "Segment {} start mismatch: expected {:?}, got {:?}",
4679 i,
4680 expected.0,
4681 start
4682 );
4683 assert!(
4684 (end[j] - expected.1[j]).abs() < 0.001,
4685 "Segment {} end mismatch: expected {:?}, got {:?}",
4686 i,
4687 expected.1,
4688 end
4689 );
4690 }
4691 }
4692
4693 let limited_segments = query.get_poly_wall_segments(poly_ref, &filter, 2)?;
4695 assert_eq!(limited_segments.len(), 2); let invalid_ref = encode_poly_ref(999, 999);
4699 let result = query.get_poly_wall_segments(invalid_ref, &filter, 10);
4700 assert!(result.is_err());
4701
4702 Ok(())
4703 }
4704
4705 #[test]
4706 fn test_get_poly_wall_segments_with_neighbors()
4707 -> std::result::Result<(), Box<dyn std::error::Error>> {
4708 let nav_params = NavMeshParams {
4710 origin: [0.0, 0.0, 0.0],
4711 tile_width: 10.0,
4712 tile_height: 10.0,
4713 max_tiles: 4,
4714 max_polys_per_tile: 256,
4715 };
4716
4717 let mut nav_mesh = NavMesh::new(nav_params.clone())?;
4718
4719 for tx in 0..2 {
4721 let x_offset = tx as f32 * 10.0;
4722
4723 let params = NavMeshCreateParams {
4724 nav_mesh_params: nav_params.clone(),
4725 verts: vec![
4726 x_offset + 0.0,
4727 0.0,
4728 0.0, x_offset + 10.0,
4730 0.0,
4731 0.0, x_offset + 10.0,
4733 0.0,
4734 10.0, x_offset + 0.0,
4736 0.0,
4737 10.0, ],
4739 vert_count: 4,
4740 polys: vec![
4741 0,
4742 1,
4743 2,
4744 3,
4745 MESH_NULL_IDX,
4746 MESH_NULL_IDX,
4747 MESH_NULL_IDX,
4748 MESH_NULL_IDX,
4749 ],
4750 poly_flags: vec![PolyFlags::WALK],
4751 poly_areas: vec![0],
4752 poly_count: 1,
4753 nvp: 4,
4754 detail_meshes: vec![0, 0, 0, 2],
4755 detail_verts: vec![],
4756 detail_vert_count: 0,
4757 detail_tris: vec![0, 1, 2, 0, 2, 3],
4758 detail_tri_count: 2,
4759 off_mesh_con_verts: vec![],
4760 off_mesh_con_rad: vec![],
4761 off_mesh_con_flags: vec![],
4762 off_mesh_con_areas: vec![],
4763 off_mesh_con_dir: vec![],
4764 off_mesh_con_user_id: vec![],
4765 off_mesh_con_count: 0,
4766 bmin: [x_offset, 0.0, 0.0],
4767 bmax: [x_offset + 10.0, 0.0, 10.0],
4768 walkable_height: 2.0,
4769 walkable_radius: 0.6,
4770 walkable_climb: 0.9,
4771 cs: 0.3,
4772 ch: 0.2,
4773 build_bv_tree: true,
4774 };
4775
4776 let mut tile = NavMeshBuilder::build_tile(¶ms)?;
4777
4778 if let Some(header) = tile.header.as_mut() {
4780 header.x = tx;
4781 header.y = 0;
4782 header.layer = 0;
4783 }
4784
4785 nav_mesh.add_mesh_tile(tile)?;
4786 }
4787
4788 let query = NavMeshQuery::new(&nav_mesh);
4792 let filter = QueryFilter::default();
4793
4794 let center = [5.0, 0.0, 5.0];
4796 let half_extents = [5.0, 5.0, 5.0];
4797 let (poly_ref, _) =
4798 query.find_nearest_poly(Vec3::from(center), Vec3::from(half_extents), &filter)?;
4799
4800 let wall_segments = query.get_poly_wall_segments(poly_ref, &filter, 10)?;
4802
4803 assert!(wall_segments.len() >= 2);
4805 assert!(wall_segments.len() <= 4);
4806
4807 for segment in &wall_segments {
4809 let dist_sqr = segment.0.distance_squared(segment.1);
4811 assert!(dist_sqr > 0.001, "Wall segment should have non-zero length");
4812 }
4813
4814 Ok(())
4815 }
4816
4817 fn create_simple_nav_mesh() -> Result<NavMesh, DetourError> {
4822 let params = NavMeshParams {
4823 origin: [0.0, 0.0, 0.0],
4824 tile_width: 10.0,
4825 tile_height: 10.0,
4826 max_tiles: 1,
4827 max_polys_per_tile: 1,
4828 };
4829
4830 let mut nav_mesh = NavMesh::new(params.clone()).unwrap();
4831
4832 let tile_params = NavMeshCreateParams {
4834 nav_mesh_params: params,
4835 verts: vec![
4836 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 10.0, 0.0, 10.0, 0.0, 0.0, 10.0,
4837 ],
4838 vert_count: 4,
4839 polys: vec![0, 1, 2, 3, 0xffff, 0xffff],
4840 poly_flags: vec![PolyFlags::WALK],
4841 poly_areas: vec![0],
4842 poly_count: 1,
4843 nvp: 6,
4844 detail_meshes: vec![0, 4, 0, 2],
4845 detail_verts: vec![
4846 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 10.0, 0.0, 10.0, 0.0, 0.0, 10.0,
4847 ],
4848 detail_vert_count: 4,
4849 detail_tris: vec![0, 1, 2, 0, 2, 3],
4850 detail_tri_count: 2,
4851 off_mesh_con_verts: vec![],
4852 off_mesh_con_rad: vec![],
4853 off_mesh_con_flags: vec![],
4854 off_mesh_con_areas: vec![],
4855 off_mesh_con_dir: vec![],
4856 off_mesh_con_user_id: vec![],
4857 off_mesh_con_count: 0,
4858 bmin: [0.0, 0.0, 0.0],
4859 bmax: [10.0, 0.0, 10.0],
4860 walkable_height: 2.0,
4861 walkable_radius: 0.6,
4862 walkable_climb: 0.9,
4863 cs: 0.3,
4864 ch: 0.2,
4865 build_bv_tree: true,
4866 };
4867
4868 let mut tile = NavMeshBuilder::build_tile(&tile_params)?;
4869
4870 if let Some(header) = tile.header.as_mut() {
4872 header.x = 0;
4873 header.y = 0;
4874 header.layer = 0;
4875 }
4876
4877 nav_mesh.add_mesh_tile(tile)?;
4878
4879 let tiles = nav_mesh.get_all_tiles();
4881 println!("Number of tiles: {}", tiles.len());
4882 if !tiles.is_empty() {
4883 let first_tile = &tiles[0];
4884 println!("First tile has {} polygons", first_tile.polys.len());
4885 if !first_tile.polys.is_empty() {
4886 println!("First polygon: {:?}", first_tile.polys[0]);
4887 }
4888 }
4889
4890 Ok(nav_mesh)
4891 }
4892
4893 #[test]
4894 fn test_sliced_pathfinding_simple() -> Result<(), DetourError> {
4895 let nav_mesh = create_simple_nav_mesh()?;
4896 let mut query = NavMeshQuery::new(&nav_mesh);
4897 let filter = QueryFilter::default();
4898
4899 let tiles = nav_mesh.get_all_tiles();
4901 if tiles.is_empty() {
4902 panic!("No tiles in nav mesh!");
4903 }
4904
4905 let first_tile = &tiles[0];
4907 let tile_salt = first_tile.salt;
4908 println!("Tile salt: {}", tile_salt);
4909
4910 let mut poly_ref = PolyRef::new(0);
4912 for tile_id in 1..10 {
4913 let test_ref = encode_poly_ref_with_salt(tile_salt, tile_id, 0);
4914 if nav_mesh.is_valid_poly_ref(test_ref) {
4915 println!("Found valid tile_id: {}", tile_id);
4916 poly_ref = test_ref;
4917 println!(
4918 "Using poly_ref: {:?} (hex: 0x{:08x})",
4919 poly_ref,
4920 poly_ref.id()
4921 );
4922 break;
4923 }
4924 }
4925
4926 if !poly_ref.is_valid() {
4928 let center = [5.0, 0.0, 5.0];
4929 let half_extents = [1.0, 1.0, 1.0];
4930 match query.find_nearest_poly(Vec3::from(center), Vec3::from(half_extents), &filter) {
4931 Ok((found_ref, _pos)) => poly_ref = found_ref,
4932 Err(_) => poly_ref = encode_poly_ref_with_salt(tile_salt, 1, 0),
4933 }
4934 }
4935 println!(
4936 "Final poly_ref: {:?} (hex: 0x{:08x})",
4937 poly_ref,
4938 poly_ref.id()
4939 );
4940 println!(
4941 "Is valid poly_ref: {}",
4942 nav_mesh.is_valid_poly_ref(poly_ref)
4943 );
4944
4945 let start_pos = [5.0, 0.0, 5.0];
4946 let end_pos = [8.0, 0.0, 8.0];
4947
4948 query.init_sliced_find_path(
4950 poly_ref,
4951 poly_ref,
4952 Vec3::from(start_pos),
4953 Vec3::from(end_pos),
4954 &filter,
4955 0,
4956 )?;
4957
4958 let (iterations, state) = query.update_sliced_find_path(100)?;
4960 assert_eq!(state, SlicedPathState::Success);
4961 assert_eq!(iterations, 0); let path = query.finalize_sliced_find_path(10)?;
4965 assert_eq!(path.len(), 1);
4966 assert_eq!(path[0], poly_ref);
4967
4968 Ok(())
4969 }
4970
4971 #[test]
4972 fn test_sliced_pathfinding_with_iterations() -> Result<(), DetourError> {
4973 let nav_mesh = create_3x3_grid_navmesh();
4975 let mut query = NavMeshQuery::new(&nav_mesh);
4976 let filter = QueryFilter::default();
4977
4978 let start_center = [5.0, 0.0, 5.0]; let end_center = [25.0, 0.0, 25.0]; let half_extents = [20.0, 10.0, 20.0]; println!("Finding start polygon near {:?}", start_center);
4984 let start_result =
4985 query.find_nearest_poly(Vec3::from(start_center), Vec3::from(half_extents), &filter);
4986 let (start_ref, start_nearest) = match start_result {
4987 Ok(result) => {
4988 println!("Found start polygon: {:?} at {:?}", result.0, result.1);
4989 result
4990 }
4991 Err(e) => {
4992 println!("Failed to find start polygon: {:?}", e);
4993 return Err(e);
4994 }
4995 };
4996
4997 println!("Finding end polygon near {:?}", end_center);
4998 let end_result =
4999 query.find_nearest_poly(Vec3::from(end_center), Vec3::from(half_extents), &filter);
5000 let (end_ref, end_nearest) = match end_result {
5001 Ok(result) => {
5002 println!("Found end polygon: {:?} at {:?}", result.0, result.1);
5003 result
5004 }
5005 Err(e) => {
5006 println!("Failed to find end polygon: {:?}", e);
5007 return Err(e);
5008 }
5009 };
5010 let start_pos = start_nearest.to_array();
5011 let end_pos = end_nearest.to_array();
5012
5013 println!(
5015 "Initializing sliced pathfinding from {:?} to {:?}",
5016 start_ref, end_ref
5017 );
5018 match query.init_sliced_find_path(
5019 start_ref,
5020 end_ref,
5021 Vec3::from(start_pos),
5022 Vec3::from(end_pos),
5023 &filter,
5024 0,
5025 ) {
5026 Ok(()) => println!("Sliced pathfinding initialized successfully"),
5027 Err(e) => {
5028 println!("Failed to initialize sliced pathfinding: {:?}", e);
5029
5030 println!(
5032 "Checking if start_ref is valid: {}",
5033 nav_mesh.is_valid_poly_ref(start_ref)
5034 );
5035 println!(
5036 "Checking if end_ref is valid: {}",
5037 nav_mesh.is_valid_poly_ref(end_ref)
5038 );
5039
5040 return Err(e);
5041 }
5042 }
5043
5044 let mut total_iterations = 0;
5046 let mut state = SlicedPathState::InProgress;
5047
5048 while state == SlicedPathState::InProgress {
5049 println!(
5050 "Updating sliced pathfinding, iteration {}",
5051 total_iterations
5052 );
5053 match query.update_sliced_find_path(2) {
5054 Ok((iter, new_state)) => {
5055 println!(" Updated {} iterations, new state: {:?}", iter, new_state);
5056 total_iterations += iter;
5057 state = new_state;
5058 }
5059 Err(e) => {
5060 println!(" Update failed: {:?}", e);
5061 return Err(e);
5062 }
5063 }
5064
5065 if total_iterations > 100 {
5067 panic!("Too many iterations");
5068 }
5069 }
5070
5071 assert_eq!(state, SlicedPathState::Success);
5073
5074 let path = query.finalize_sliced_find_path(20)?;
5076 assert!(path.len() > 1); assert_eq!(path[0], start_ref);
5078 assert_eq!(path[path.len() - 1], end_ref);
5079
5080 Ok(())
5081 }
5082
5083 #[test]
5084 fn test_sliced_pathfinding_partial() -> Result<(), DetourError> {
5085 let nav_mesh = create_simple_nav_mesh()?;
5086 let mut query = NavMeshQuery::new(&nav_mesh);
5087 let filter = QueryFilter::default();
5088
5089 let start_center = [5.0, 0.0, 5.0];
5091 let half_extents = [2.0, 2.0, 2.0];
5092 let (start_ref, _) =
5093 query.find_nearest_poly(Vec3::from(start_center), Vec3::from(half_extents), &filter)?;
5094 let invalid_end_ref = PolyRef::new(999); let start_pos = [5.0, 0.0, 5.0];
5096 let end_pos = [50.0, 0.0, 50.0];
5097
5098 let result = query.init_sliced_find_path(
5100 start_ref,
5101 invalid_end_ref,
5102 Vec3::from(start_pos),
5103 Vec3::from(end_pos),
5104 &filter,
5105 0,
5106 );
5107 assert!(result.is_err());
5108
5109 Ok(())
5110 }
5111
5112 #[test]
5113 fn test_sliced_pathfinding_any_angle() -> Result<(), DetourError> {
5114 let nav_mesh = create_simple_nav_mesh()?;
5115 let mut query = NavMeshQuery::new(&nav_mesh);
5116 let filter = QueryFilter::default();
5117
5118 let center = [5.0, 0.0, 5.0];
5120 let half_extents = [2.0, 2.0, 2.0];
5121 let (poly_ref, _) =
5122 query.find_nearest_poly(Vec3::from(center), Vec3::from(half_extents), &filter)?;
5123 let start_pos = [5.0, 0.0, 5.0];
5124 let end_pos = [8.0, 0.0, 8.0];
5125
5126 const DT_FINDPATH_ANY_ANGLE: u32 = 0x02;
5128 query.init_sliced_find_path(
5129 poly_ref,
5130 poly_ref,
5131 Vec3::from(start_pos),
5132 Vec3::from(end_pos),
5133 &filter,
5134 DT_FINDPATH_ANY_ANGLE,
5135 )?;
5136
5137 let (_, state) = query.update_sliced_find_path(100)?;
5139 assert_eq!(state, SlicedPathState::Success);
5140
5141 Ok(())
5142 }
5143
5144 #[test]
5145 fn test_query_polygons() -> Result<(), DetourError> {
5146 let nav_mesh = create_simple_nav_mesh()?;
5147 let query = NavMeshQuery::new(&nav_mesh);
5148 let filter = QueryFilter::default();
5149
5150 let center = [5.0, 0.0, 5.0];
5152 let half_extents = [3.0, 2.0, 3.0];
5153
5154 let polys =
5155 query.query_polygons(Vec3::from(center), Vec3::from(half_extents), &filter, 10)?;
5156
5157 assert!(!polys.is_empty());
5159
5160 Ok(())
5161 }
5162
5163 #[test]
5164 fn test_query_polygons_with_custom_query() -> Result<(), DetourError> {
5165 let nav_mesh = create_simple_nav_mesh()?;
5166 let query = NavMeshQuery::new(&nav_mesh);
5167 let filter = QueryFilter::default();
5168
5169 let mut collect_query = CollectPolysQuery::new(5);
5171 let center = [5.0, 0.0, 5.0];
5172 let half_extents = [3.0, 2.0, 3.0];
5173
5174 query.query_polygons_with_query(
5175 Vec3::from(center),
5176 Vec3::from(half_extents),
5177 &filter,
5178 &mut collect_query,
5179 )?;
5180
5181 assert!(collect_query.num_collected() > 0);
5183 assert!(!collect_query.overflow());
5184
5185 Ok(())
5186 }
5187
5188 #[test]
5189 fn test_find_nearest_poly_query() -> Result<(), DetourError> {
5190 let nav_mesh = create_simple_nav_mesh()?;
5191 let query = NavMeshQuery::new(&nav_mesh);
5192 let filter = QueryFilter::default();
5193
5194 let center = [5.0, 0.0, 5.0];
5196 let half_extents = [5.0, 2.0, 5.0];
5197 let mut nearest_query = FindNearestPolyQuery::new(&query, ¢er);
5198
5199 query.query_polygons_with_query(
5200 Vec3::from(center),
5201 Vec3::from(half_extents),
5202 &filter,
5203 &mut nearest_query,
5204 )?;
5205
5206 assert!(nearest_query.nearest_ref().is_valid());
5208 assert!(nearest_query.nearest_distance_sqr() < f32::MAX);
5209
5210 Ok(())
5211 }
5212
5213 fn create_3x3_grid_navmesh() -> NavMesh {
5215 let params = NavMeshParams {
5216 origin: [0.0, 0.0, 0.0],
5217 tile_width: 30.0,
5218 tile_height: 30.0,
5219 max_tiles: 1,
5220 max_polys_per_tile: 9,
5221 };
5222
5223 let mut nav_mesh = NavMesh::new(params.clone()).unwrap();
5224
5225 let cell_size = 10.0;
5227
5228 let mut verts = Vec::new();
5231 for z in 0..4 {
5232 for x in 0..4 {
5233 verts.push(x as f32 * cell_size);
5234 verts.push(0.0);
5235 verts.push(z as f32 * cell_size);
5236 }
5237 }
5238
5239 let mut polys = Vec::new();
5241 let mut poly_flags = Vec::new();
5242 let mut poly_areas = Vec::new();
5243
5244 for row in 0..3 {
5245 for col in 0..3 {
5246 let base_idx = row * 4 + col;
5247 let _poly_idx = row * 3 + col;
5248
5249 polys.push(base_idx as u16);
5251 polys.push((base_idx + 1) as u16);
5252 polys.push((base_idx + 5) as u16);
5253 polys.push((base_idx + 4) as u16);
5254
5255 polys.push(if row > 0 {
5258 ((row - 1) * 3 + col) as u16
5259 } else {
5260 0xffff
5261 });
5262 polys.push(if col < 2 {
5264 (row * 3 + col + 1) as u16
5265 } else {
5266 0xffff
5267 });
5268 polys.push(if row < 2 {
5270 ((row + 1) * 3 + col) as u16
5271 } else {
5272 0xffff
5273 });
5274 polys.push(if col > 0 {
5276 (row * 3 + col - 1) as u16
5277 } else {
5278 0xffff
5279 });
5280
5281 poly_flags.push(PolyFlags::WALK);
5282 poly_areas.push(0);
5283 }
5284 }
5285
5286 let mut detail_meshes = Vec::new();
5288 let mut detail_tris = Vec::new();
5289
5290 for i in 0..9 {
5291 detail_meshes.push(0); detail_meshes.push(4); detail_meshes.push((i * 2) as u32); detail_meshes.push(2); detail_tris.push(0);
5298 detail_tris.push(1);
5299 detail_tris.push(2);
5300 detail_tris.push(0);
5301 detail_tris.push(2);
5302 detail_tris.push(3);
5303 }
5304
5305 let tile_params = NavMeshCreateParams {
5306 nav_mesh_params: params.clone(),
5307 verts: verts.clone(),
5308 vert_count: 16,
5309 polys,
5310 poly_flags,
5311 poly_areas,
5312 poly_count: 9,
5313 nvp: 6,
5314 detail_meshes,
5315 detail_verts: verts,
5316 detail_vert_count: 16,
5317 detail_tris,
5318 detail_tri_count: 18,
5319 off_mesh_con_verts: vec![],
5320 off_mesh_con_rad: vec![],
5321 off_mesh_con_flags: vec![],
5322 off_mesh_con_areas: vec![],
5323 off_mesh_con_dir: vec![],
5324 off_mesh_con_user_id: vec![],
5325 off_mesh_con_count: 0,
5326 bmin: [0.0, 0.0, 0.0],
5327 bmax: [30.0, 0.0, 30.0],
5328 walkable_height: 2.0,
5329 walkable_radius: 0.6,
5330 walkable_climb: 0.9,
5331 cs: 0.3,
5332 ch: 0.2,
5333 build_bv_tree: true,
5334 };
5335
5336 let tile = NavMeshBuilder::build_tile(&tile_params).unwrap();
5337 nav_mesh.add_mesh_tile(tile).unwrap();
5338 nav_mesh
5339 }
5340
5341 #[test]
5342 fn test_get_poly_height() -> Result<(), DetourError> {
5343 let nav_mesh = create_simple_nav_mesh()?;
5344 let query = NavMeshQuery::new(&nav_mesh);
5345 let filter = QueryFilter::default();
5346
5347 let center = [5.0, 0.0, 5.0];
5349 let half_extents = [2.0, 2.0, 2.0];
5350
5351 let (poly_ref, nearest_pt) =
5353 query.find_nearest_poly(Vec3::from(center), Vec3::from(half_extents), &filter)?;
5354
5355 if poly_ref.is_valid() {
5357 let height = query.get_poly_height(poly_ref, nearest_pt)?;
5359
5360 if height.is_some() {
5363 assert!(height.unwrap() >= -100.0 && height.unwrap() <= 100.0);
5365 }
5366
5367 let outside_pos = Vec3::new(1000.0, 0.0, 1000.0);
5369 let height_outside = query.get_poly_height(poly_ref, outside_pos)?;
5370
5371 assert!(height_outside.is_none());
5373 }
5374
5375 Ok(())
5376 }
5377
5378 #[test]
5379 fn test_find_random_point_with_custom_rand() -> Result<(), DetourError> {
5380 let nav_mesh = create_3x3_grid_navmesh();
5381 let mut query = NavMeshQuery::new(&nav_mesh);
5382 let filter = QueryFilter::default();
5383
5384 let counter = std::cell::Cell::new(0);
5386 let custom_rand = || {
5387 let val = counter.get();
5388 counter.set(val + 1);
5389 (val as f32 * 0.123) % 1.0
5391 };
5392
5393 let (poly_ref, point) = query.find_random_point_with_custom_rand(&filter, custom_rand)?;
5394
5395 assert!(poly_ref.is_valid());
5396 assert!(point[0] >= 0.0 && point[0] <= 30.0);
5398 assert!(point[2] >= 0.0 && point[2] <= 30.0);
5399
5400 Ok(())
5401 }
5402
5403 #[test]
5404 fn test_find_random_point_around_circle_with_custom_rand() -> Result<(), DetourError> {
5405 let nav_mesh = create_3x3_grid_navmesh();
5406 let mut query = NavMeshQuery::new(&nav_mesh);
5407 let filter = QueryFilter::default();
5408
5409 let center = [15.0, 0.0, 15.0];
5411 let half_extents = [5.0, 2.0, 5.0];
5412 let (center_ref, _) =
5413 query.find_nearest_poly(Vec3::from(center), Vec3::from(half_extents), &filter)?;
5414
5415 let seed = std::cell::Cell::new(42u32);
5417 let custom_rand = || {
5418 let val = seed.get();
5420 let new_val = val.wrapping_mul(1103515245).wrapping_add(12345);
5421 seed.set(new_val);
5422 (new_val & 0x7FFFFFFF) as f32 / 2147483647.0
5423 };
5424
5425 let radius = 10.0;
5426 let (poly_ref, point) = query.find_random_point_around_circle(
5427 center_ref,
5428 Vec3::from(center),
5429 radius,
5430 &filter,
5431 custom_rand,
5432 )?;
5433
5434 assert!(poly_ref.is_valid());
5435
5436 let dx = point[0] - center[0];
5438 let dz = point[2] - center[2];
5439 let distance = (dx * dx + dz * dz).sqrt();
5440 assert!(distance <= radius);
5441
5442 Ok(())
5443 }
5444
5445 #[test]
5446 fn test_api_harmonization_methods() -> Result<(), DetourError> {
5447 let nav_mesh = create_simple_nav_mesh()?;
5448 let mut query = NavMeshQuery::new(&nav_mesh);
5449 let filter = QueryFilter::default();
5450
5451 let center = [15.0, 0.0, 15.0];
5453 let half_extents = [20.0, 10.0, 20.0];
5454
5455 match query.find_nearest_poly_extended(
5457 Vec3::from(center),
5458 Vec3::from(half_extents),
5459 &filter,
5460 ) {
5461 Ok((nearest_ref, nearest_pt, _is_over_poly)) => {
5462 assert!(nearest_ref.is_valid());
5463
5464 let (nearest_ref_basic, nearest_pt_basic) = query.find_nearest_poly(
5466 Vec3::from(center),
5467 Vec3::from(half_extents),
5468 &filter,
5469 )?;
5470 assert_eq!(nearest_ref, nearest_ref_basic);
5471 assert_eq!(nearest_pt, nearest_pt_basic);
5472
5473 let path_refs = vec![nearest_ref];
5475 let start_pos = nearest_pt.to_array();
5476 let end_pos = nearest_pt.to_array(); let path_with_options = query.find_straight_path_with_options(
5479 Vec3::from(start_pos),
5480 Vec3::from(end_pos),
5481 &path_refs,
5482 0,
5483 )?;
5484 let path_basic = query.find_straight_path(
5485 Vec3::from(start_pos),
5486 Vec3::from(end_pos),
5487 &path_refs,
5488 )?;
5489
5490 assert_eq!(
5492 path_with_options.waypoint_count(),
5493 path_basic.waypoint_count()
5494 );
5495
5496 query.init_sliced_find_path_default(
5498 nearest_ref,
5499 nearest_ref,
5500 Vec3::from(start_pos),
5501 Vec3::from(end_pos),
5502 &filter,
5503 )?;
5504
5505 let mut query2 = NavMeshQuery::new(&nav_mesh);
5507 query2.init_sliced_find_path(
5508 nearest_ref,
5509 nearest_ref,
5510 Vec3::from(start_pos),
5511 Vec3::from(end_pos),
5512 &filter,
5513 0,
5514 )?;
5515
5516 let (_iter1, state1) = query.update_sliced_find_path(1)?;
5518 let (_iter2, state2) = query2.update_sliced_find_path(1)?;
5519 assert_eq!(state1, state2);
5520 }
5521 Err(_) => {
5522 assert!(
5527 query
5528 .find_nearest_poly(Vec3::from(center), Vec3::from(half_extents), &filter)
5529 .is_err()
5530 );
5531 assert!(
5532 query
5533 .find_nearest_poly_extended(
5534 Vec3::from(center),
5535 Vec3::from(half_extents),
5536 &filter
5537 )
5538 .is_err()
5539 );
5540
5541 let empty_path = vec![];
5543 let pos = [0.0, 0.0, 0.0];
5544 assert!(
5545 query
5546 .find_straight_path(Vec3::from(pos), Vec3::from(pos), &empty_path)
5547 .is_err()
5548 );
5549 assert!(
5550 query
5551 .find_straight_path_with_options(
5552 Vec3::from(pos),
5553 Vec3::from(pos),
5554 &empty_path,
5555 0
5556 )
5557 .is_err()
5558 );
5559 }
5560 }
5561
5562 Ok(())
5563 }
5564}