fyrox_impl/utils/
navmesh.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Contains all structures and methods to create and manage navigation meshes (navmesh).
22//!
23//! Navigation mesh is a set of convex polygons which is used for path finding in complex
24//! environment.
25
26#![warn(missing_docs)]
27
28use crate::{
29    core::{
30        algebra::{Point3, Vector3},
31        arrayvec::ArrayVec,
32        math::{self, plane::Plane, ray::Ray, PositionProvider, TriangleDefinition, Vector3Ext},
33        reflect::prelude::*,
34        visitor::{Visit, VisitResult, Visitor},
35    },
36    scene::mesh::{
37        buffer::{VertexAttributeUsage, VertexReadTrait},
38        Mesh,
39    },
40    utils::{
41        astar::{Graph, GraphVertex, PathError, PathKind, VertexData, VertexDataProvider},
42        raw_mesh::{RawMeshBuilder, RawVertex},
43    },
44};
45use fxhash::{FxBuildHasher, FxHashMap};
46use fyrox_core::math::octree::{Octree, OctreeNode};
47use std::ops::{Deref, DerefMut};
48
49#[derive(Clone, Debug, Default, Visit)]
50struct Vertex {
51    triangle_index: usize,
52    data: VertexData,
53}
54
55impl Deref for Vertex {
56    type Target = VertexData;
57
58    fn deref(&self) -> &Self::Target {
59        &self.data
60    }
61}
62
63impl DerefMut for Vertex {
64    fn deref_mut(&mut self) -> &mut Self::Target {
65        &mut self.data
66    }
67}
68
69impl PositionProvider for Vertex {
70    fn position(&self) -> Vector3<f32> {
71        self.data.position
72    }
73}
74
75impl VertexDataProvider for Vertex {}
76
77/// See module docs.
78#[derive(Clone, Debug, Default, Reflect)]
79#[reflect(hide_all)]
80pub struct Navmesh {
81    octree: Octree,
82    triangles: Vec<TriangleDefinition>,
83    vertices: Vec<Vector3<f32>>,
84    graph: Graph<Vertex>,
85}
86
87impl PartialEq for Navmesh {
88    fn eq(&self, other: &Self) -> bool {
89        self.triangles == other.triangles && self.vertices == other.vertices
90    }
91}
92
93impl Visit for Navmesh {
94    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
95        let mut region = visitor.enter_region(name)?;
96
97        // Backward compatibility.
98        if region.is_reading() {
99            let mut pathfinder = Graph::<GraphVertex>::new();
100            if pathfinder.visit("PathFinder", &mut region).is_ok() {
101                self.vertices = pathfinder
102                    .vertices
103                    .iter()
104                    .map(|v| v.position)
105                    .collect::<Vec<_>>();
106            } else {
107                self.vertices.visit("Vertices", &mut region)?;
108            }
109        } else {
110            self.vertices.visit("Vertices", &mut region)?;
111        }
112
113        self.triangles.visit("Triangles", &mut region)?;
114
115        drop(region);
116
117        // No need to save octree, we can restore it on load.
118        if visitor.is_reading() {
119            let raw_triangles = self
120                .triangles
121                .iter()
122                .map(|t| {
123                    [
124                        self.vertices[t[0] as usize],
125                        self.vertices[t[1] as usize],
126                        self.vertices[t[2] as usize],
127                    ]
128                })
129                .collect::<Vec<[Vector3<f32>; 3]>>();
130
131            self.octree = Octree::new(&raw_triangles, 32);
132        }
133
134        let graph = make_graph(&self.triangles, &self.vertices);
135        self.graph = graph;
136
137        Ok(())
138    }
139}
140
141#[derive(Copy, Clone, Debug)]
142struct Portal {
143    left: usize,
144    right: usize,
145}
146
147fn triangle_area_2d(a: Vector3<f32>, b: Vector3<f32>, c: Vector3<f32>) -> f32 {
148    let abx = b[0] - a[0];
149    let abz = b[2] - a[2];
150    let acx = c[0] - a[0];
151    let acz = c[2] - a[2];
152    acx * abz - abx * acz
153}
154
155#[derive(PartialEq, Clone, Copy, Eq)]
156enum Winding {
157    Clockwise,
158    CounterClockwise,
159}
160
161fn winding(a: Vector3<f32>, b: Vector3<f32>, c: Vector3<f32>) -> Winding {
162    if triangle_area_2d(a, b, c) > 0.0 {
163        Winding::Clockwise
164    } else {
165        Winding::CounterClockwise
166    }
167}
168
169fn make_graph(triangles: &[TriangleDefinition], vertices: &[Vector3<f32>]) -> Graph<Vertex> {
170    let mut graph = Graph::new();
171
172    // Add vertices at the center of each triangle.
173    for (triangle_index, triangle) in triangles.iter().enumerate() {
174        let a = vertices[triangle[0] as usize];
175        let b = vertices[triangle[1] as usize];
176        let c = vertices[triangle[2] as usize];
177
178        let center = (a + b + c).scale(1.0 / 3.0);
179        graph.add_vertex(Vertex {
180            triangle_index,
181            data: VertexData::new(center),
182        });
183    }
184
185    // Build edge-triangle map first.
186    #[derive(Copy, Clone, PartialEq, Hash, Eq)]
187    struct Edge {
188        a: usize,
189        b: usize,
190    }
191
192    let total_edge_count = triangles.len() * 3;
193    let mut edge_triangle_map =
194        FxHashMap::with_capacity_and_hasher(total_edge_count, FxBuildHasher::default());
195
196    for (triangle_index, triangle) in triangles.iter().enumerate() {
197        for edge in triangle.edges() {
198            edge_triangle_map.insert(
199                Edge {
200                    a: edge.a as usize,
201                    b: edge.b as usize,
202                },
203                triangle_index,
204            );
205        }
206    }
207
208    // Link vertices.
209    for (triangle_index, triangle) in triangles.iter().enumerate() {
210        for edge in triangle.edges() {
211            // Adjacent edge must have opposite winding.
212            let adjacent_edge = Edge {
213                a: edge.b as usize,
214                b: edge.a as usize,
215            };
216
217            if let Some(adjacent_triangle_index) = edge_triangle_map.get(&adjacent_edge) {
218                graph.link_bidirect(triangle_index, *adjacent_triangle_index);
219            }
220        }
221    }
222
223    graph
224}
225
226/// A temporary modification context which allows you to modify a navmesh. When the modification
227/// context is dropped, it recalculates navigation graph automatically.
228pub struct NavmeshModificationContext<'a> {
229    navmesh: &'a mut Navmesh,
230}
231
232impl Drop for NavmeshModificationContext<'_> {
233    fn drop(&mut self) {
234        let graph = make_graph(&self.navmesh.triangles, &self.navmesh.vertices);
235        self.navmesh.graph = graph;
236    }
237}
238
239impl NavmeshModificationContext<'_> {
240    /// Adds the triangle to the navigational mesh and returns its index in the internal array. Vertex indices in
241    /// the triangle must be valid!
242    pub fn add_triangle(&mut self, triangle: TriangleDefinition) -> u32 {
243        let index = self.navmesh.triangles.len();
244        self.navmesh.triangles.push(triangle);
245        index as u32
246    }
247
248    /// Removes a triangle at the given index from the navigational mesh.
249    pub fn remove_triangle(&mut self, index: usize) -> TriangleDefinition {
250        self.navmesh.triangles.remove(index)
251    }
252
253    /// Removes last triangle from the navigational mesh. Automatically fixes vertex links in the internal
254    /// navigational graph.
255    pub fn pop_triangle(&mut self) -> Option<TriangleDefinition> {
256        if self.navmesh.triangles.is_empty() {
257            None
258        } else {
259            Some(self.remove_triangle(self.navmesh.triangles.len() - 1))
260        }
261    }
262
263    /// Removes a vertex at the given index from the navigational mesh. All triangles that share the vertex will
264    /// be also removed.
265    pub fn remove_vertex(&mut self, index: usize) -> Vector3<f32> {
266        // Remove triangles that sharing the vertex first.
267        let mut i = 0;
268        while i < self.navmesh.triangles.len() {
269            if self.navmesh.triangles[i]
270                .indices()
271                .contains(&(index as u32))
272            {
273                self.remove_triangle(i);
274            } else {
275                i += 1;
276            }
277        }
278
279        // Shift vertex indices in triangles. Example:
280        //
281        // 0:A 1:B 2:C 3:D 4:E
282        // [A,B,C], [A,C,D], [A,D,E], [D,C,E]
283        // [0,1,2], [0,2,3], [0,3,4], [3,2,4]
284        //
285        // Remove B.
286        //
287        // 0:A 1:C 2:D 3:E
288        // [A,C,D], [A,D,E], [D,C,E]
289        // [0,1,2], [0,2,3], [2,1,3]
290        for triangle in self.navmesh.triangles.iter_mut() {
291            for other_vertex_index in triangle.indices_mut() {
292                if *other_vertex_index > index as u32 {
293                    *other_vertex_index -= 1;
294                }
295            }
296        }
297
298        self.navmesh.vertices.remove(index)
299    }
300
301    /// Returns a mutable reference to the internal array of vertices.
302    pub fn vertices_mut(&mut self) -> &mut [Vector3<f32>] {
303        &mut self.navmesh.vertices
304    }
305
306    /// Adds the vertex to the navigational mesh. The vertex will **not** be connected with any other vertex.
307    pub fn add_vertex(&mut self, vertex: Vector3<f32>) -> u32 {
308        let index = self.navmesh.vertices.len();
309        self.navmesh.vertices.push(vertex);
310        index as u32
311    }
312
313    /// Removes last vertex from the navigational mesh. All triangles that share the vertex will be also removed.
314    pub fn pop_vertex(&mut self) -> Option<Vector3<f32>> {
315        if self.navmesh.vertices.is_empty() {
316            None
317        } else {
318            Some(self.remove_vertex(self.navmesh.vertices.len() - 1))
319        }
320    }
321
322    /// Inserts the vertex at the given index. Automatically shift indices in triangles to preserve mesh structure.
323    pub fn insert_vertex(&mut self, index: u32, vertex: Vector3<f32>) {
324        self.navmesh.vertices.insert(index as usize, vertex);
325
326        // Shift vertex indices in triangles. Example:
327        //
328        // 0:A 1:C 2:D 3:E
329        // [A,C,D], [A,D,E], [D,C,E]
330        // [0,1,2], [0,2,3], [2,1,3]
331        //
332        // Insert B.
333        //
334        // 0:A 1:B 2:C 3:D 4:E
335        // [A,C,D], [A,D,E], [D,C,E]
336        // [0,2,3], [0,3,4], [3,2,4]
337        for triangle in self.navmesh.triangles.iter_mut() {
338            for other_vertex_index in triangle.indices_mut() {
339                if *other_vertex_index >= index {
340                    *other_vertex_index += 1;
341                }
342            }
343        }
344    }
345}
346
347impl Navmesh {
348    /// Creates new navigation mesh from given set of triangles and vertices. This is
349    /// low level method that allows to specify triangles and vertices directly. In
350    /// most cases you should use `from_mesh` method.
351    pub fn new(triangles: Vec<TriangleDefinition>, vertices: Vec<Vector3<f32>>) -> Self {
352        // Build triangles for octree.
353        let raw_triangles = triangles
354            .iter()
355            .map(|t| {
356                [
357                    vertices[t[0] as usize],
358                    vertices[t[1] as usize],
359                    vertices[t[2] as usize],
360                ]
361            })
362            .collect::<Vec<[Vector3<f32>; 3]>>();
363
364        Self {
365            graph: make_graph(&triangles, &vertices),
366            triangles,
367            vertices,
368            octree: Octree::new(&raw_triangles, 32),
369        }
370    }
371
372    /// Creates new navigation mesh (navmesh) from given mesh. It is most simple way to create complex
373    /// navigation mesh, it should be used in pair with model loading functionality - you can
374    /// load model from file and turn it into navigation mesh, or even build navigation mesh
375    /// from a model in existing scene. This method "eats" any kind of meshes with any amount
376    /// of surfaces - it joins all surfaces into single mesh and creates navmesh from it.
377    ///
378    /// Example:
379    /// ```
380    /// # use fyrox_impl::scene::Scene;
381    /// # use fyrox_impl::utils::navmesh::Navmesh;
382    /// # use fyrox_graph::SceneGraph;
383    /// #
384    /// fn make_navmesh(scene: &Scene, navmesh_name: &str) -> Navmesh {
385    ///     // Find mesh node in existing scene and create navigation mesh from it.
386    ///     let navmesh_node_handle = scene.graph.find_by_name_from_root(navmesh_name).unwrap().0;
387    ///     Navmesh::from_mesh(scene.graph[navmesh_node_handle].as_mesh())
388    /// }
389    /// ```
390    pub fn from_mesh(mesh: &Mesh) -> Self {
391        // Join surfaces into one simple mesh.
392        let mut builder = RawMeshBuilder::<RawVertex>::default();
393        let global_transform = mesh.global_transform();
394        for surface in mesh.surfaces() {
395            let shared_data = surface.data();
396            let shared_data = shared_data.data_ref();
397
398            let vertex_buffer = &shared_data.vertex_buffer;
399            for triangle in shared_data.geometry_buffer.iter() {
400                builder.insert(RawVertex::from(
401                    global_transform
402                        .transform_point(&Point3::from(
403                            vertex_buffer
404                                .get(triangle[0] as usize)
405                                .unwrap()
406                                .read_3_f32(VertexAttributeUsage::Position)
407                                .unwrap(),
408                        ))
409                        .coords,
410                ));
411                builder.insert(RawVertex::from(
412                    global_transform
413                        .transform_point(&Point3::from(
414                            vertex_buffer
415                                .get(triangle[1] as usize)
416                                .unwrap()
417                                .read_3_f32(VertexAttributeUsage::Position)
418                                .unwrap(),
419                        ))
420                        .coords,
421                ));
422                builder.insert(RawVertex::from(
423                    global_transform
424                        .transform_point(&Point3::from(
425                            vertex_buffer
426                                .get(triangle[2] as usize)
427                                .unwrap()
428                                .read_3_f32(VertexAttributeUsage::Position)
429                                .unwrap(),
430                        ))
431                        .coords,
432                ));
433            }
434        }
435
436        let mesh = builder.build();
437
438        Navmesh::new(
439            mesh.triangles,
440            mesh.vertices
441                .into_iter()
442                .map(|v| Vector3::new(v.x, v.y, v.z))
443                .collect::<Vec<_>>(),
444        )
445    }
446
447    /// Tries to get a projected point on the navmesh, that is closest to the given query point.
448    /// Returns a tuple with the projection point and the triangle index, that contains this
449    /// projection point.
450    ///
451    /// ## Complexity
452    ///
453    /// This method has `O(log(n))` complexity in the best case (when the query point lies inside the
454    /// navmesh bounds) and `O(n)` complexity in the worst case. `n` here is the number of triangles
455    /// in the navmesh.
456    pub fn query_closest(&self, query_point: Vector3<f32>) -> Option<(Vector3<f32>, usize)> {
457        let mut closest = None;
458        let mut closest_distance = f32::MAX;
459
460        /* TODO: Octree query seems to be bugged and needs investigation.
461        self.octree.point_query(query_point, |triangles| {
462            // O(log(n))
463            self.query_closest_internal(
464                &mut closest,
465                &mut closest_distance,
466                triangles.iter().map(|i| *i as usize),
467                query_point,
468            )
469        });
470
471        if closest.is_none() {
472            self.query_closest_internal(
473                &mut closest,
474                &mut closest_distance,
475                0..self.triangles.len(),
476                query_point,
477            )
478        }*/
479
480        // O(n)
481        self.query_closest_internal(
482            &mut closest,
483            &mut closest_distance,
484            0..self.triangles.len(),
485            query_point,
486        );
487
488        closest
489    }
490
491    fn query_closest_internal(
492        &self,
493        closest: &mut Option<(Vector3<f32>, usize)>,
494        closest_distance: &mut f32,
495        triangles: impl Iterator<Item = usize>,
496        query_point: Vector3<f32>,
497    ) {
498        for triangle_index in triangles {
499            let triangle = &self.triangles[triangle_index];
500            let a = self.vertices[triangle[0] as usize];
501            let b = self.vertices[triangle[1] as usize];
502            let c = self.vertices[triangle[2] as usize];
503
504            let Some(plane) = Plane::from_triangle(&a, &b, &c) else {
505                continue;
506            };
507            let projection = plane.project(&query_point);
508            if math::is_point_inside_triangle(&projection, &[a, b, c]) {
509                let sqr_distance = query_point.sqr_distance(&projection);
510                if sqr_distance < *closest_distance {
511                    *closest_distance = sqr_distance;
512                    *closest = Some((projection, triangle_index));
513                }
514            }
515
516            // Check each edge by projecting the query point onto it and checking where the
517            // projection lies.
518            for edge in self.triangles[triangle_index].edges() {
519                let a = self.vertices[edge.a as usize];
520                let b = self.vertices[edge.b as usize];
521                let ray = Ray::from_two_points(a, b);
522                let t = ray.project_point(&query_point);
523                if (0.0..=1.0).contains(&t) {
524                    let edge_pt_projection = ray.get_point(t);
525                    let sqr_distance = query_point.sqr_distance(&edge_pt_projection);
526                    if sqr_distance < *closest_distance {
527                        *closest_distance = sqr_distance;
528                        *closest = Some((ray.get_point(t), triangle_index));
529                    }
530                }
531            }
532
533            // Also check vertices.
534            for pt in [a, b, c] {
535                let sqr_distance = query_point.sqr_distance(&pt);
536                if sqr_distance < *closest_distance {
537                    *closest_distance = sqr_distance;
538                    *closest = Some((pt, triangle_index));
539                }
540            }
541        }
542    }
543
544    /// Creates a temporary modification context which allows you to modify the navmesh. When the
545    /// modification context is dropped, it recalculates navigation graph automatically.
546    pub fn modify(&mut self) -> NavmeshModificationContext {
547        NavmeshModificationContext { navmesh: self }
548    }
549
550    /// Returns reference to array of triangles.
551    pub fn triangles(&self) -> &[TriangleDefinition] {
552        &self.triangles
553    }
554
555    /// Returns reference to the internal array of vertices.
556    pub fn vertices(&self) -> &[Vector3<f32>] {
557        &self.vertices
558    }
559
560    /// Returns shared reference to inner octree.
561    pub fn octree(&self) -> &Octree {
562        &self.octree
563    }
564
565    /// Tries to build path using indices of begin and end points.
566    ///
567    /// Example:
568    ///
569    /// ```
570    /// use fyrox_impl::utils::navmesh::Navmesh;
571    /// use fyrox_impl::core::algebra::Vector3;
572    /// use fyrox_impl::utils::astar::{PathKind, PathError};
573    ///
574    /// fn find_path(navmesh: &Navmesh, begin: Vector3<f32>, end: Vector3<f32>, path: &mut Vec<Vector3<f32>>) -> Result<PathKind, PathError> {
575    ///     if let Some((_, begin_index)) = navmesh.query_closest(begin) {
576    ///         if let Some((_, end_index)) = navmesh.query_closest(end) {
577    ///             return navmesh.build_path(begin_index, end_index, path);
578    ///         }
579    ///     }
580    ///     Ok(PathKind::Partial)
581    /// }
582    /// ```
583    pub fn build_path(
584        &self,
585        from: usize,
586        to: usize,
587        path: &mut Vec<Vector3<f32>>,
588    ) -> Result<PathKind, PathError> {
589        self.graph.build_positional_path(from, to, path)
590    }
591
592    /// Tries to pick a triangle by given ray. Returns closest result.
593    pub fn ray_cast(&self, ray: Ray) -> Option<(Vector3<f32>, usize)> {
594        let mut buffer = ArrayVec::<usize, 128>::new();
595
596        self.octree.ray_query_static(&ray, &mut buffer);
597
598        let mut closest_distance = f32::MAX;
599        let mut result = None;
600        for node in buffer.into_iter() {
601            if let OctreeNode::Leaf { indices, .. } = self.octree.node(node) {
602                for &index in indices {
603                    let triangle = self.triangles[index as usize];
604                    let a = self.vertices()[triangle[0] as usize];
605                    let b = self.vertices()[triangle[1] as usize];
606                    let c = self.vertices()[triangle[2] as usize];
607
608                    if let Some(intersection) = ray.triangle_intersection_point(&[a, b, c]) {
609                        let distance = intersection.metric_distance(&ray.origin);
610                        if distance < closest_distance {
611                            closest_distance = distance;
612                            result = Some((intersection, index as usize));
613                        }
614                    }
615                }
616            } else {
617                unreachable!()
618            }
619        }
620
621        result
622    }
623
624    fn portal_between(&self, src_triangle: usize, dest_triangle: usize) -> Option<Portal> {
625        let src_triangle = self.triangles.get(src_triangle)?;
626        let dest_triangle = self.triangles.get(dest_triangle)?;
627        for src_triangle_edge in src_triangle.edges() {
628            for dest_triangle_edge in dest_triangle.edges() {
629                if src_triangle_edge == dest_triangle_edge {
630                    let a = self.vertices[src_triangle[0] as usize];
631                    let b = self.vertices[src_triangle[1] as usize];
632                    let c = self.vertices[src_triangle[2] as usize];
633
634                    return if winding(a, b, c) == Winding::Clockwise {
635                        Some(Portal {
636                            left: src_triangle_edge.a as usize,
637                            right: src_triangle_edge.b as usize,
638                        })
639                    } else {
640                        Some(Portal {
641                            left: src_triangle_edge.b as usize,
642                            right: src_triangle_edge.a as usize,
643                        })
644                    };
645                }
646            }
647        }
648        None
649    }
650}
651
652/// Navmesh agent is a "pathfinding unit" that performs navigation on a mesh. It is designed to
653/// cover most of simple use cases when you need to build and follow some path from point A to point B.
654#[derive(Visit, Clone, Debug)]
655#[visit(optional)]
656pub struct NavmeshAgent {
657    path: Vec<Vector3<f32>>,
658    current: u32,
659    position: Vector3<f32>,
660    last_warp_position: Vector3<f32>,
661    target: Vector3<f32>,
662    last_target_position: Vector3<f32>,
663    recalculation_threshold: f32,
664    speed: f32,
665    path_dirty: bool,
666    radius: f32,
667    interpolator: f32,
668}
669
670impl Default for NavmeshAgent {
671    fn default() -> Self {
672        Self::new()
673    }
674}
675
676impl NavmeshAgent {
677    /// Creates new navigation mesh agent.
678    pub fn new() -> Self {
679        Self {
680            path: vec![],
681            current: 0,
682            position: Default::default(),
683            last_warp_position: Default::default(),
684            target: Default::default(),
685            last_target_position: Default::default(),
686            recalculation_threshold: 0.25,
687            speed: 1.5,
688            path_dirty: true,
689            radius: 0.2,
690            interpolator: 0.0,
691        }
692    }
693
694    /// Returns agent's position.
695    pub fn position(&self) -> Vector3<f32> {
696        self.position
697    }
698
699    /// Returns agent's path that will be followed.
700    pub fn path(&self) -> &[Vector3<f32>] {
701        &self.path
702    }
703
704    /// Sets new speed of agent's movement.
705    pub fn set_speed(&mut self, speed: f32) {
706        self.speed = speed;
707    }
708
709    /// Returns current agent's movement speed.
710    pub fn speed(&self) -> f32 {
711        self.speed
712    }
713
714    /// Sets a new path recalculation threshold (in meters). The threshold is used to prevent
715    /// path recalculation in case if a target's position or the agent position haven't significantly
716    /// moved. This significance is defined by the threshold.
717    pub fn set_threshold(&mut self, threshold: f32) {
718        self.recalculation_threshold = threshold;
719    }
720
721    /// Returns the current path recalculation threshold (in meters). See [`Self::set_threshold`]
722    /// for more info.
723    pub fn threshold(&self) -> f32 {
724        self.recalculation_threshold
725    }
726
727    /// Sets a new radius for the navmesh agent. The agent will use this radius to walk around
728    /// corners with the distance equal to the radius. This could help to prevent the agent from
729    /// being stuck in the corners. The default value is 0.2 meters.
730    pub fn set_radius(&mut self, radius: f32) {
731        self.radius = radius;
732    }
733
734    /// Returns the current radius of the navmesh agent. See [`Self::set_radius`] for more info
735    /// about radius parameter.
736    pub fn radius(&self) -> f32 {
737        self.radius
738    }
739}
740
741impl NavmeshAgent {
742    /// Calculates path from point A to point B. In most cases there is no need to use this method
743    /// directly, because `update` will call it anyway if target position has moved.
744    pub fn calculate_path(
745        &mut self,
746        navmesh: &Navmesh,
747        src_point: Vector3<f32>,
748        dest_point: Vector3<f32>,
749    ) -> Result<PathKind, PathError> {
750        self.path.clear();
751
752        self.current = 0;
753        self.interpolator = 0.0;
754
755        if let Some((src_point_on_navmesh, src_triangle)) = navmesh.query_closest(src_point) {
756            if let Some((dest_point_on_navmesh, dest_triangle)) = navmesh.query_closest(dest_point)
757            {
758                if src_triangle == dest_triangle {
759                    self.path.push(src_point_on_navmesh);
760                    self.path.push(dest_point_on_navmesh);
761
762                    return Ok(PathKind::Full);
763                }
764
765                let mut path_triangle_indices = Vec::new();
766                let path_kind = navmesh.graph.build_indexed_path(
767                    src_triangle,
768                    dest_triangle,
769                    &mut path_triangle_indices,
770                )?;
771
772                path_triangle_indices.reverse();
773
774                self.straighten_path(
775                    navmesh,
776                    src_point_on_navmesh,
777                    dest_point_on_navmesh,
778                    &path_triangle_indices,
779                );
780
781                return Ok(path_kind);
782            }
783        }
784
785        Err(PathError::Empty)
786    }
787
788    fn straighten_path(
789        &mut self,
790        navmesh: &Navmesh,
791        src_position: Vector3<f32>,
792        dest_position: Vector3<f32>,
793        path_triangles: &[usize],
794    ) {
795        self.path.push(src_position);
796
797        if path_triangles.len() > 1 {
798            let mut funnel_apex = src_position;
799            let mut funnel_vertices = [funnel_apex; 2];
800            let mut side_indices = [0; 2];
801            let side_signs = [1.0, -1.0];
802
803            let mut i = 0;
804            while i < path_triangles.len() {
805                let portal_vertices = if i + 1 < path_triangles.len() {
806                    let portal = navmesh
807                        .portal_between(path_triangles[i], path_triangles[i + 1])
808                        .unwrap();
809
810                    let mut left = navmesh.vertices[portal.left];
811                    let mut right = navmesh.vertices[portal.right];
812
813                    if self.radius > 0.0 {
814                        let delta = right - left;
815                        let len = delta.norm();
816                        let offset = delta.scale(self.radius.min(len * 0.5) / len);
817
818                        left += offset;
819                        right -= offset;
820                    }
821
822                    [left, right]
823                } else {
824                    [dest_position, dest_position]
825                };
826
827                for current in 0..2 {
828                    let opposite = 1 - current;
829                    let side_sign = side_signs[current];
830                    if side_sign
831                        * triangle_area_2d(
832                            funnel_apex,
833                            funnel_vertices[current],
834                            portal_vertices[current],
835                        )
836                        >= 0.0
837                    {
838                        if funnel_apex == funnel_vertices[current]
839                            || side_sign
840                                * triangle_area_2d(
841                                    funnel_apex,
842                                    funnel_vertices[opposite],
843                                    portal_vertices[current],
844                                )
845                                < 0.0
846                        {
847                            funnel_vertices[current] = portal_vertices[current];
848                            side_indices[current] = i;
849                        } else {
850                            funnel_apex = funnel_vertices[opposite];
851                            funnel_vertices = [funnel_apex; 2];
852
853                            self.path.push(funnel_apex);
854
855                            i = side_indices[opposite];
856                            side_indices[current] = i;
857
858                            break;
859                        }
860                    }
861                }
862
863                i += 1;
864            }
865        }
866
867        self.path.push(dest_position);
868    }
869
870    /// Performs single update tick that moves agent to the target along the path (which is automatically
871    /// recalculated if target's position has changed).
872    pub fn update(&mut self, dt: f32, navmesh: &Navmesh) -> Result<PathKind, PathError> {
873        if self.path_dirty {
874            self.calculate_path(navmesh, self.position, self.target)?;
875            self.path_dirty = false;
876        }
877
878        if let Some(source) = self.path.get(self.current as usize) {
879            if let Some(destination) = self.path.get((self.current + 1) as usize) {
880                let len = destination.metric_distance(source);
881                self.position = source.lerp(destination, self.interpolator.clamp(0.0, 1.0));
882                self.interpolator += (self.speed * dt) / len.max(f32::EPSILON);
883                if self.interpolator >= 1.0 {
884                    self.current += 1;
885                    self.interpolator = 0.0;
886                } else if self.interpolator < 0.0 {
887                    self.current = self.current.saturating_sub(1);
888                    self.interpolator = 1.0;
889                }
890            }
891        }
892
893        Ok(PathKind::Full)
894    }
895
896    /// Returns current steering target which in most cases next path point from which
897    /// agent is close to.
898    pub fn steering_target(&self) -> Option<Vector3<f32>> {
899        self.path
900            .get(self.current as usize + 1)
901            .or_else(|| self.path.last())
902            .cloned()
903    }
904
905    /// Sets new target for the agent.
906    pub fn set_target(&mut self, new_target: Vector3<f32>) {
907        if new_target.metric_distance(&self.last_target_position) >= self.recalculation_threshold {
908            self.path_dirty = true;
909            self.last_target_position = new_target;
910        }
911
912        self.target = new_target;
913    }
914
915    /// Returns current target of the agent.
916    pub fn target(&self) -> Vector3<f32> {
917        self.target
918    }
919
920    /// Sets new position of the agent.
921    pub fn set_position(&mut self, new_position: Vector3<f32>) {
922        if new_position.metric_distance(&self.last_warp_position) >= self.recalculation_threshold {
923            self.path_dirty = true;
924            self.last_warp_position = new_position;
925        }
926
927        self.position = new_position;
928    }
929}
930
931/// Allows you to build agent in declarative manner.
932pub struct NavmeshAgentBuilder {
933    position: Vector3<f32>,
934    target: Vector3<f32>,
935    recalculation_threshold: f32,
936    speed: f32,
937}
938
939impl Default for NavmeshAgentBuilder {
940    fn default() -> Self {
941        Self::new()
942    }
943}
944
945impl NavmeshAgentBuilder {
946    /// Creates new builder instance.
947    pub fn new() -> Self {
948        Self {
949            position: Default::default(),
950            target: Default::default(),
951            recalculation_threshold: 0.25,
952            speed: 1.5,
953        }
954    }
955
956    /// Sets new desired position of the agent being built.
957    pub fn with_position(mut self, position: Vector3<f32>) -> Self {
958        self.position = position;
959        self
960    }
961
962    /// Sets new desired target of the agent being built.
963    pub fn with_target(mut self, position: Vector3<f32>) -> Self {
964        self.target = position;
965        self
966    }
967
968    /// Sets new desired recalculation threshold (in meters) of the agent being built.
969    pub fn with_recalculation_threshold(mut self, threshold: f32) -> Self {
970        self.recalculation_threshold = threshold;
971        self
972    }
973
974    /// Sets new desired movement speed of the agent being built.
975    pub fn with_speed(mut self, speed: f32) -> Self {
976        self.speed = speed;
977        self
978    }
979
980    /// Build the agent.
981    pub fn build(self) -> NavmeshAgent {
982        NavmeshAgent {
983            position: self.position,
984            last_warp_position: self.position,
985            target: self.target,
986            last_target_position: self.target,
987            recalculation_threshold: self.recalculation_threshold,
988            speed: self.speed,
989            ..Default::default()
990        }
991    }
992}
993
994#[cfg(test)]
995mod test {
996    use crate::{
997        core::{algebra::Vector3, math::TriangleDefinition},
998        utils::navmesh::{Navmesh, NavmeshAgent},
999    };
1000
1001    #[test]
1002    fn test_navmesh() {
1003        let navmesh = Navmesh::new(
1004            vec![
1005                TriangleDefinition([0, 1, 3]),
1006                TriangleDefinition([1, 2, 3]),
1007                TriangleDefinition([2, 5, 3]),
1008                TriangleDefinition([2, 4, 5]),
1009                TriangleDefinition([4, 7, 5]),
1010                TriangleDefinition([4, 6, 7]),
1011            ],
1012            vec![
1013                Vector3::new(0.0, 0.0, 0.0),
1014                Vector3::new(0.0, 0.0, 1.0),
1015                Vector3::new(1.0, 0.0, 1.0),
1016                Vector3::new(1.0, 0.0, 0.0),
1017                Vector3::new(2.0, 0.0, 1.0),
1018                Vector3::new(2.0, 0.0, 0.0),
1019                Vector3::new(3.0, 0.0, 1.0),
1020                Vector3::new(3.0, 0.0, 0.0),
1021            ],
1022        );
1023
1024        let mut agent = NavmeshAgent::new();
1025
1026        agent.set_target(Vector3::new(3.0, 0.0, 1.0));
1027        agent.update(1.0 / 60.0, &navmesh).unwrap();
1028
1029        let graph = &navmesh.graph;
1030
1031        assert_eq!(graph.vertices.len(), 6);
1032        assert_eq!(graph.vertices[0].neighbours[0], 1);
1033
1034        assert_eq!(graph.vertices[1].neighbours[0], 0);
1035        assert_eq!(graph.vertices[1].neighbours[1], 2);
1036
1037        assert_eq!(graph.vertices[2].neighbours[0], 1);
1038        assert_eq!(graph.vertices[2].neighbours[1], 3);
1039
1040        assert_eq!(graph.vertices[3].neighbours[0], 2);
1041        assert_eq!(graph.vertices[3].neighbours[1], 4);
1042
1043        assert_eq!(graph.vertices[4].neighbours[0], 3);
1044        assert_eq!(graph.vertices[4].neighbours[1], 5);
1045
1046        assert_eq!(graph.vertices[5].neighbours[0], 4);
1047
1048        assert_eq!(
1049            agent.path,
1050            vec![
1051                Vector3::new(0.0, 0.0, 0.0),
1052                Vector3::new(3.0, 0.0, 1.0),
1053                Vector3::new(3.0, 0.0, 1.0)
1054            ]
1055        );
1056    }
1057}