1use super::nav_mesh::NavMesh;
13use super::status::Status;
14use super::{PolyRef, QueryFilter};
15use crate::error::DetourError;
16use glam::Vec3;
17
18use std::cmp::Reverse;
19use std::collections::{BinaryHeap, HashMap};
20
21#[allow(dead_code)]
23const MAX_HIERARCHY_LEVELS: usize = 4;
24
25const DEFAULT_CLUSTER_SIZE: f32 = 64.0;
27
28#[derive(Debug)]
30pub struct HierarchicalPathfinder {
31 #[allow(dead_code)]
34 nav_mesh: NavMesh,
35 pub levels: Vec<HierarchyLevel>,
37 pub base_cluster_size: f32,
39}
40
41#[derive(Debug, Clone)]
43pub struct HierarchyLevel {
44 pub level: usize,
46 pub cluster_size: f32,
48 pub clusters: Vec<Cluster>,
50 pub connections: Vec<ClusterConnection>,
52 pub spatial_index: HashMap<(i32, i32), Vec<usize>>,
54}
55
56#[derive(Debug, Clone)]
58pub struct Cluster {
59 pub id: usize,
61 pub bounds: ClusterBounds,
63 pub polygons: Vec<PolyRef>,
65 pub portals: Vec<Portal>,
67 pub center: Vec3,
69 pub walkable: bool,
71}
72
73#[derive(Debug, Clone)]
75pub struct ClusterConnection {
76 pub from_cluster: usize,
78 pub to_cluster: usize,
80 pub portal: Portal,
82 pub cost: f32,
84 pub distance: f32,
86}
87
88#[derive(Debug, Clone)]
90pub struct Portal {
91 pub position: Vec3,
93 pub width: f32,
95 pub direction: Vec3,
97 pub poly_refs: [PolyRef; 2],
99}
100
101#[derive(Debug, Clone)]
103pub struct ClusterBounds {
104 pub min: Vec3,
105 pub max: Vec3,
106}
107
108#[derive(Debug, Clone)]
110pub struct HierarchicalConfig {
111 pub max_levels: usize,
113 pub base_cluster_size: f32,
115 pub level_scale_factor: f32,
117 pub max_search_distance: f32,
119 pub enable_caching: bool,
121}
122
123impl Default for HierarchicalConfig {
124 fn default() -> Self {
125 Self {
126 max_levels: 3,
127 base_cluster_size: DEFAULT_CLUSTER_SIZE,
128 level_scale_factor: 4.0,
129 max_search_distance: 1000.0,
130 enable_caching: true,
131 }
132 }
133}
134
135#[derive(Debug)]
137pub struct HierarchicalPath {
138 pub path: Vec<Vec3>,
140 pub cluster_path: Vec<Vec<usize>>,
142 pub cost: f32,
144 pub status: Status,
146}
147
148#[allow(dead_code)]
151#[derive(Debug, Clone)]
152struct CacheEntry {
153 path: Vec<usize>,
154 cost: f32,
155 timestamp: u64,
156}
157
158#[allow(dead_code)]
161#[derive(Debug)]
162struct PathCache {
163 entries: HashMap<(usize, usize, usize), CacheEntry>, max_entries: usize,
165 current_time: u64,
166}
167
168impl HierarchicalPathfinder {
169 pub fn new(nav_mesh: NavMesh, config: HierarchicalConfig) -> Result<Self, DetourError> {
171 let mut pathfinder = Self {
172 nav_mesh,
173 levels: Vec::new(),
174 base_cluster_size: config.base_cluster_size,
175 };
176
177 pathfinder.build_hierarchy(&config)?;
179
180 Ok(pathfinder)
181 }
182
183 pub fn find_path(
185 &self,
186 start: Vec3,
187 end: Vec3,
188 filter: &QueryFilter,
189 ) -> Result<HierarchicalPath, DetourError> {
190 let start_clusters = self.find_clusters_at_position(start)?;
192 let end_clusters = self.find_clusters_at_position(end)?;
193
194 if start_clusters.is_empty() || end_clusters.is_empty() {
195 return Ok(HierarchicalPath {
196 path: Vec::new(),
197 cluster_path: Vec::new(),
198 cost: f32::INFINITY,
199 status: Status::Failure,
200 });
201 }
202
203 let mut cluster_paths = Vec::new();
205 let mut current_start = start_clusters[self.levels.len() - 1];
206 let mut current_end = end_clusters[self.levels.len() - 1];
207
208 for level in (0..self.levels.len()).rev() {
210 let level_path = self.find_path_at_level(level, current_start, current_end, filter)?;
211
212 if level_path.is_empty() {
213 return Ok(HierarchicalPath {
214 path: Vec::new(),
215 cluster_path: Vec::new(),
216 cost: f32::INFINITY,
217 status: Status::Failure,
218 });
219 }
220
221 cluster_paths.push(level_path.clone());
222
223 if level > 0 {
225 current_start = start_clusters[level - 1];
226 current_end = end_clusters[level - 1];
227 }
228 }
229
230 let world_path = self.convert_to_world_path(&cluster_paths, start, end)?;
232 let total_cost = self.calculate_path_cost(&world_path);
233
234 Ok(HierarchicalPath {
235 path: world_path,
236 cluster_path: cluster_paths,
237 cost: total_cost,
238 status: Status::Success,
239 })
240 }
241
242 fn build_hierarchy(&mut self, config: &HierarchicalConfig) -> Result<(), DetourError> {
244 for level in 0..config.max_levels {
246 let cluster_size =
247 config.base_cluster_size * config.level_scale_factor.powi(level as i32);
248 let hierarchy_level = self.build_level(level, cluster_size)?;
249 self.levels.push(hierarchy_level);
250 }
251
252 Ok(())
253 }
254
255 fn build_level(&self, level: usize, cluster_size: f32) -> Result<HierarchyLevel, DetourError> {
257 let mut clusters = Vec::new();
258 let mut spatial_index = HashMap::new();
259
260 let mesh_bounds = self.get_mesh_bounds();
262
263 let grid_width = ((mesh_bounds.max.x - mesh_bounds.min.x) / cluster_size).ceil() as i32;
265 let grid_height = ((mesh_bounds.max.z - mesh_bounds.min.z) / cluster_size).ceil() as i32;
266
267 let mut cluster_id = 0;
268
269 for grid_y in 0..grid_height {
270 for grid_x in 0..grid_width {
271 let min_x = mesh_bounds.min.x + grid_x as f32 * cluster_size;
272 let min_z = mesh_bounds.min.z + grid_y as f32 * cluster_size;
273 let max_x = min_x + cluster_size;
274 let max_z = min_z + cluster_size;
275
276 let bounds = ClusterBounds {
277 min: Vec3::new(min_x, mesh_bounds.min.y, min_z),
278 max: Vec3::new(max_x, mesh_bounds.max.y, max_z),
279 };
280
281 let polygons = self.find_polygons_in_bounds(&bounds)?;
283
284 if !polygons.is_empty() {
285 let center = Vec3::new(
286 (min_x + max_x) * 0.5,
287 (mesh_bounds.min.y + mesh_bounds.max.y) * 0.5,
288 (min_z + max_z) * 0.5,
289 );
290
291 let cluster = Cluster {
292 id: cluster_id,
293 bounds,
294 polygons,
295 portals: Vec::new(),
296 center,
297 walkable: true,
298 };
299
300 spatial_index
302 .entry((grid_x, grid_y))
303 .or_insert_with(Vec::new)
304 .push(clusters.len());
305
306 clusters.push(cluster);
307 cluster_id += 1;
308 }
309 }
310 }
311
312 let connections = self.build_cluster_connections(&clusters)?;
314
315 Ok(HierarchyLevel {
316 level,
317 cluster_size,
318 clusters,
319 connections,
320 spatial_index,
321 })
322 }
323
324 pub fn find_clusters_at_position(&self, position: Vec3) -> Result<Vec<usize>, DetourError> {
326 let mut cluster_ids = Vec::new();
327
328 for level in &self.levels {
329 let grid_x =
331 ((position.x - self.get_mesh_bounds().min.x) / level.cluster_size).floor() as i32;
332 let grid_z =
333 ((position.z - self.get_mesh_bounds().min.z) / level.cluster_size).floor() as i32;
334
335 if let Some(cluster_indices) = level.spatial_index.get(&(grid_x, grid_z)) {
336 for &cluster_idx in cluster_indices {
338 let cluster = &level.clusters[cluster_idx];
339 if self.point_in_bounds(position, &cluster.bounds) {
340 cluster_ids.push(cluster_idx);
341 break;
342 }
343 }
344 }
345
346 if cluster_ids.len() <= level.level {
348 let nearest = self.find_nearest_cluster(level, position)?;
349 cluster_ids.push(nearest);
350 }
351 }
352
353 Ok(cluster_ids)
354 }
355
356 fn find_path_at_level(
358 &self,
359 level: usize,
360 start_cluster: usize,
361 end_cluster: usize,
362 _filter: &QueryFilter,
363 ) -> Result<Vec<usize>, DetourError> {
364 if level >= self.levels.len() {
365 return Err(DetourError::Failure);
366 }
367
368 let hierarchy_level = &self.levels[level];
369
370 let mut open_set = BinaryHeap::new();
372 let mut came_from = HashMap::new();
373 let mut g_score = HashMap::new();
374 let mut f_score = HashMap::new();
375
376 g_score.insert(start_cluster, 0.0);
378 let h_start = self.heuristic_cost(hierarchy_level, start_cluster, end_cluster);
379 f_score.insert(start_cluster, h_start);
380 open_set.push(Reverse((
381 ordered_float::NotNan::new(h_start).map_err(|_| DetourError::Failure)?,
382 start_cluster,
383 )));
384
385 while let Some(Reverse((_, current))) = open_set.pop() {
386 if current == end_cluster {
387 let mut path = vec![current];
389 let mut node = current;
390 while let Some(&parent) = came_from.get(&node) {
391 path.push(parent);
392 node = parent;
393 }
394 path.reverse();
395 return Ok(path);
396 }
397
398 for connection in &hierarchy_level.connections {
400 let neighbor = if connection.from_cluster == current {
401 connection.to_cluster
402 } else if connection.to_cluster == current {
403 connection.from_cluster
404 } else {
405 continue;
406 };
407
408 let tentative_g_score =
409 g_score.get(¤t).unwrap_or(&f32::INFINITY) + connection.cost;
410
411 if tentative_g_score < *g_score.get(&neighbor).unwrap_or(&f32::INFINITY) {
412 came_from.insert(neighbor, current);
413 g_score.insert(neighbor, tentative_g_score);
414 let h_neighbor = self.heuristic_cost(hierarchy_level, neighbor, end_cluster);
415 let f_neighbor = tentative_g_score + h_neighbor;
416 f_score.insert(neighbor, f_neighbor);
417
418 open_set.push(Reverse((
419 ordered_float::NotNan::new(f_neighbor).map_err(|_| DetourError::Failure)?,
420 neighbor,
421 )));
422 }
423 }
424 }
425
426 Ok(Vec::new())
428 }
429
430 fn heuristic_cost(&self, level: &HierarchyLevel, from: usize, to: usize) -> f32 {
432 if from >= level.clusters.len() || to >= level.clusters.len() {
433 return f32::INFINITY;
434 }
435
436 let from_cluster = &level.clusters[from];
437 let to_cluster = &level.clusters[to];
438
439 (to_cluster.center - from_cluster.center).length()
441 }
442
443 fn convert_to_world_path(
445 &self,
446 cluster_paths: &[Vec<usize>],
447 start: Vec3,
448 end: Vec3,
449 ) -> Result<Vec<Vec3>, DetourError> {
450 if cluster_paths.is_empty() {
451 return Ok(vec![start, end]);
452 }
453
454 let mut world_path = vec![start];
455
456 if let Some(detailed_path) = cluster_paths.first() {
458 for &cluster_id in detailed_path.iter().skip(1) {
459 if cluster_id < self.levels[0].clusters.len() {
460 let cluster = &self.levels[0].clusters[cluster_id];
461 world_path.push(cluster.center);
462 }
463 }
464 }
465
466 world_path.push(end);
467 Ok(world_path)
468 }
469
470 pub fn calculate_path_cost(&self, path: &[Vec3]) -> f32 {
472 let mut total_cost = 0.0;
473 for i in 1..path.len() {
474 total_cost += (path[i] - path[i - 1]).length();
475 }
476 total_cost
477 }
478
479 fn get_mesh_bounds(&self) -> ClusterBounds {
481 ClusterBounds {
484 min: Vec3::new(-500.0, -10.0, -500.0),
485 max: Vec3::new(500.0, 10.0, 500.0),
486 }
487 }
488
489 fn find_polygons_in_bounds(&self, bounds: &ClusterBounds) -> Result<Vec<PolyRef>, DetourError> {
490 if bounds.max.x >= -600.0
497 && bounds.min.x <= 600.0
498 && bounds.max.z >= -600.0
499 && bounds.min.z <= 600.0
500 {
501 let base_id = ((bounds.min.x + 600.0) / 100.0) as u32 * 100
503 + ((bounds.min.z + 600.0) / 100.0) as u32;
504 Ok(vec![
505 PolyRef::new(base_id + 1),
506 PolyRef::new(base_id + 2),
507 PolyRef::new(base_id + 3),
508 ])
509 } else {
510 Ok(vec![]) }
512 }
513
514 fn point_in_bounds(&self, point: Vec3, bounds: &ClusterBounds) -> bool {
515 point.x >= bounds.min.x
516 && point.x <= bounds.max.x
517 && point.y >= bounds.min.y
518 && point.y <= bounds.max.y
519 && point.z >= bounds.min.z
520 && point.z <= bounds.max.z
521 }
522
523 fn find_nearest_cluster(
524 &self,
525 level: &HierarchyLevel,
526 position: Vec3,
527 ) -> Result<usize, DetourError> {
528 let mut nearest_idx = 0;
529 let mut nearest_distance = f32::INFINITY;
530
531 for (idx, cluster) in level.clusters.iter().enumerate() {
532 let distance = (cluster.center - position).length();
533 if distance < nearest_distance {
534 nearest_distance = distance;
535 nearest_idx = idx;
536 }
537 }
538
539 Ok(nearest_idx)
540 }
541
542 fn build_cluster_connections(
543 &self,
544 clusters: &[Cluster],
545 ) -> Result<Vec<ClusterConnection>, DetourError> {
546 let mut connections = Vec::new();
547
548 for i in 0..clusters.len() {
550 for j in (i + 1)..clusters.len() {
551 if self.clusters_are_adjacent(&clusters[i], &clusters[j]) {
552 let distance = (clusters[j].center - clusters[i].center).length();
553 let cost = distance * 1.0; let portal = Portal {
557 position: (clusters[i].center + clusters[j].center) * 0.5,
558 width: 2.0,
559 direction: (clusters[j].center - clusters[i].center).normalize(),
560 poly_refs: [PolyRef::new(1), PolyRef::new(2)], };
562
563 connections.push(ClusterConnection {
564 from_cluster: i,
565 to_cluster: j,
566 portal,
567 cost,
568 distance,
569 });
570 }
571 }
572 }
573
574 Ok(connections)
575 }
576
577 fn clusters_are_adjacent(&self, cluster1: &Cluster, cluster2: &Cluster) -> bool {
578 let distance = (cluster1.center - cluster2.center).length();
580 distance <= cluster1.bounds.max.x - cluster1.bounds.min.x + 1.0 }
582}
583
584#[allow(dead_code)]
585impl PathCache {
586 fn new(max_entries: usize) -> Self {
587 Self {
588 entries: HashMap::new(),
589 max_entries,
590 current_time: 0,
591 }
592 }
593
594 fn get(&mut self, level: usize, from: usize, to: usize) -> Option<Vec<usize>> {
595 self.current_time += 1;
596
597 if let Some(entry) = self.entries.get_mut(&(level, from, to)) {
598 entry.timestamp = self.current_time;
599 Some(entry.path.clone())
600 } else {
601 None
602 }
603 }
604
605 fn insert(&mut self, level: usize, from: usize, to: usize, path: Vec<usize>, cost: f32) {
606 self.current_time += 1;
607
608 if self.entries.len() >= self.max_entries {
610 let oldest_key = self
611 .entries
612 .iter()
613 .min_by_key(|(_, entry)| entry.timestamp)
614 .map(|(key, _)| *key);
615
616 if let Some(key) = oldest_key {
617 self.entries.remove(&key);
618 }
619 }
620
621 self.entries.insert(
622 (level, from, to),
623 CacheEntry {
624 path,
625 cost,
626 timestamp: self.current_time,
627 },
628 );
629 }
630}
631
632#[cfg(test)]
633mod tests {
634 use super::*;
635 use crate::nav_mesh::NavMesh;
636
637 fn create_test_nav_mesh() -> NavMesh {
638 use super::super::NavMeshParams;
639 let params = NavMeshParams {
640 origin: [0.0, 0.0, 0.0],
641 tile_width: 100.0,
642 tile_height: 100.0,
643 max_tiles: 256, max_polys_per_tile: 2048,
645 };
646 NavMesh::new(params).expect("Failed to create test NavMesh")
647 }
648
649 #[test]
650 fn test_hierarchical_pathfinder_creation() {
651 let nav_mesh = create_test_nav_mesh();
652 let config = HierarchicalConfig::default();
653
654 let pathfinder = HierarchicalPathfinder::new(nav_mesh, config);
655 assert!(pathfinder.is_ok());
656
657 let pathfinder = pathfinder.unwrap();
658 assert_eq!(pathfinder.levels.len(), 3); }
660
661 #[test]
662 fn test_cluster_bounds() {
663 let bounds = ClusterBounds {
664 min: Vec3::new(0.0, 0.0, 0.0),
665 max: Vec3::new(10.0, 10.0, 10.0),
666 };
667
668 let nav_mesh = create_test_nav_mesh();
669 let config = HierarchicalConfig::default();
670 let pathfinder = HierarchicalPathfinder::new(nav_mesh, config).unwrap();
671
672 assert!(pathfinder.point_in_bounds(Vec3::new(5.0, 5.0, 5.0), &bounds));
674
675 assert!(!pathfinder.point_in_bounds(Vec3::new(15.0, 5.0, 5.0), &bounds));
677 }
678
679 #[test]
680 fn test_cluster_adjacency() {
681 let nav_mesh = create_test_nav_mesh();
682 let config = HierarchicalConfig::default();
683 let pathfinder = HierarchicalPathfinder::new(nav_mesh, config).unwrap();
684
685 let cluster1 = Cluster {
686 id: 0,
687 bounds: ClusterBounds {
688 min: Vec3::new(0.0, 0.0, 0.0),
689 max: Vec3::new(10.0, 10.0, 10.0),
690 },
691 polygons: vec![PolyRef::new(1), PolyRef::new(2)],
692 portals: Vec::new(),
693 center: Vec3::new(5.0, 5.0, 5.0),
694 walkable: true,
695 };
696
697 let cluster2 = Cluster {
698 id: 1,
699 bounds: ClusterBounds {
700 min: Vec3::new(10.0, 0.0, 0.0),
701 max: Vec3::new(20.0, 10.0, 10.0),
702 },
703 polygons: vec![PolyRef::new(3), PolyRef::new(4)],
704 portals: Vec::new(),
705 center: Vec3::new(15.0, 5.0, 5.0),
706 walkable: true,
707 };
708
709 assert!(pathfinder.clusters_are_adjacent(&cluster1, &cluster2));
711
712 let cluster3 = Cluster {
713 id: 2,
714 bounds: ClusterBounds {
715 min: Vec3::new(50.0, 0.0, 0.0),
716 max: Vec3::new(60.0, 10.0, 10.0),
717 },
718 polygons: vec![PolyRef::new(5), PolyRef::new(6)],
719 portals: Vec::new(),
720 center: Vec3::new(55.0, 5.0, 5.0),
721 walkable: true,
722 };
723
724 assert!(!pathfinder.clusters_are_adjacent(&cluster1, &cluster3));
726 }
727
728 #[test]
729 fn test_path_cache() {
730 let mut cache = PathCache::new(3);
731
732 cache.insert(0, 1, 2, vec![1, 5, 2], 10.0);
734 cache.insert(0, 2, 3, vec![2, 6, 3], 15.0);
735
736 assert_eq!(cache.get(0, 1, 2), Some(vec![1, 5, 2]));
738 assert_eq!(cache.get(0, 2, 3), Some(vec![2, 6, 3]));
739 assert_eq!(cache.get(0, 3, 4), None);
740
741 cache.insert(0, 3, 4, vec![3, 7, 4], 20.0);
743 cache.insert(0, 4, 5, vec![4, 8, 5], 25.0);
744
745 assert!(cache.entries.len() <= 3);
747 }
748
749 #[test]
750 fn test_heuristic_cost_calculation() {
751 let nav_mesh = create_test_nav_mesh();
752 let config = HierarchicalConfig::default();
753 let pathfinder = HierarchicalPathfinder::new(nav_mesh, config).unwrap();
754
755 if !pathfinder.levels.is_empty() && pathfinder.levels[0].clusters.len() >= 2 {
756 let cost = pathfinder.heuristic_cost(&pathfinder.levels[0], 0, 1);
757 assert!(cost >= 0.0);
758 assert!(cost.is_finite());
759 }
760 }
761}