Skip to main content

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