Skip to main content

fyrox_impl/scene/
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//! Navigational mesh (navmesh for short) is a surface which can be used for path finding. See [`NavigationalMesh`] docs
22//! for more info and usage examples.
23
24use crate::scene::node::constructor::NodeConstructor;
25use crate::{
26    core::{
27        color::Color,
28        math::aabb::AxisAlignedBoundingBox,
29        parking_lot::RwLock,
30        pool::Handle,
31        reflect::prelude::*,
32        type_traits::prelude::*,
33        uuid::{uuid, Uuid},
34        variable::InheritableVariable,
35        visitor::prelude::*,
36    },
37    scene::{
38        base::{Base, BaseBuilder},
39        debug::{Line, SceneDrawingContext},
40        graph::Graph,
41        node::{Node, NodeTrait},
42    },
43    utils::navmesh::Navmesh,
44};
45use fyrox_core::algebra::Vector3;
46use fyrox_core::math::TriangleDefinition;
47use fyrox_core::parking_lot::{RwLockReadGuard, RwLockWriteGuard};
48use fyrox_graph::constructor::ConstructorProvider;
49use fyrox_graph::SceneGraph;
50use std::{
51    ops::{Deref, DerefMut},
52    sync::Arc,
53};
54
55#[derive(Clone, Default, Reflect, Debug)]
56pub(crate) struct Container(Arc<RwLock<Navmesh>>);
57
58impl PartialEq for Container {
59    fn eq(&self, other: &Self) -> bool {
60        *self.0.read() == *other.0.read()
61    }
62}
63
64impl Visit for Container {
65    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
66        self.0.write().visit(name, visitor)
67    }
68}
69
70/// Navigational mesh (navmesh for short) is a surface which can be used for path finding. Unlike [A* Pathfinder](crate::utils::astar),
71/// it can build arbitrary paths on a surface of large polygons, making a path from point A to point B linear (standard pathfinder builds
72/// path only from vertex to vertex). Navmeshes should be used when you have an arbitrary "walkable" surface, for example, a game level
73/// with rooms, hallways, multiple floors and so on. A* pathfinder should be used for strategies or any other types of games with uniform
74/// pathfinding grid.
75///
76/// ## How to create
77///
78/// You should prefer using the navmesh editor to create navigational meshes, however if it is not possible, you can create it manually.
79/// Use [`NavigationalMeshBuilder`] to create new instance and add it to your scene graph. Keep in mind, that this node is just a
80/// convenient wrapper around [`Navmesh`], so you should also read its docs to get better understanding how it works.
81///
82/// ```rust
83/// # use fyrox_impl::{
84/// #     core::{algebra::Vector3, math::TriangleDefinition, pool::Handle},
85/// #     scene::{base::BaseBuilder, graph::Graph, navmesh::NavigationalMeshBuilder, node::Node},
86/// #     utils::navmesh::Navmesh,
87/// # };
88/// # use fyrox_impl::scene::navmesh::NavigationalMesh;
89///
90/// fn create_navmesh(graph: &mut Graph) -> Handle<NavigationalMesh> {
91///     // A simple navmesh with four vertices and two triangles.
92///     let navmesh = Navmesh::new(
93///         vec![TriangleDefinition([0, 1, 2]), TriangleDefinition([0, 2, 3])],
94///         vec![
95///             Vector3::new(-1.0, 0.0, 1.0),
96///             Vector3::new(1.0, 0.0, 1.0),
97///             Vector3::new(1.0, 0.0, -1.0),
98///             Vector3::new(-1.0, 0.0, -1.0),
99///         ],
100///     );
101///     NavigationalMeshBuilder::new(BaseBuilder::new())
102///         .with_navmesh(navmesh)
103///         .build(graph)
104/// }
105/// ```
106///
107/// ## Agents
108///
109/// Navigational mesh agent helps you to build paths along the surface of a navigational mesh and follow it. Agents can be
110/// used to drive the motion of your game characters. Every agent knows about its target and automatically rebuilds the path
111/// if the target has moved. Navmesh agents are able to move along the path, providing you with their current position, so you
112/// can use it to perform an actual motion of your game characters. Agents work together with navigational meshes, you need
113/// to update their state every frame, so they can recalculate path if needed. A simple example could something like this:
114///
115/// ```rust
116/// # use fyrox_impl::utils::navmesh::NavmeshAgent;
117/// # struct Foo {
118/// // Add this to your script
119/// agent: NavmeshAgent
120/// # }
121/// ```
122///
123/// After that, you need to update the agent every frame to make sure it will follow the target:
124///
125/// ```rust
126/// # use fyrox_impl::{
127/// #    core::algebra::Vector3, scene::navmesh::NavigationalMesh, utils::navmesh::NavmeshAgent,
128/// # };
129/// fn update_agent(
130///     agent: &mut NavmeshAgent,
131///     target: Vector3<f32>,
132///     dt: f32,
133///     navmesh: &NavigationalMesh,
134/// ) {
135///     // Set the target to follow and the speed.
136///     agent.set_target(target);
137///     agent.set_speed(1.0);
138///
139///     // Update the agent.
140///     agent.update(dt, &navmesh.navmesh_ref()).unwrap();
141///
142///     // Print its position - you can use this position as target point of your game character.
143///     println!("{}", agent.position());
144/// }
145/// ```
146///
147/// This method should be called in `on_update` of your script. It accepts four parameters: a reference to the agent, a
148/// target which it will follow, a time step (`context.dt`), and a reference to navigational mesh node. You can fetch
149/// navigational mesh from the scene graph by its name:
150///
151/// ```rust
152/// # use fyrox_impl::scene::{navmesh::NavigationalMesh, Scene};
153/// # use fyrox_graph::SceneGraph;
154/// fn find_navmesh<'a>(scene: &'a mut Scene, name: &str) -> &'a mut NavigationalMesh {
155///     let handle = scene.graph.find_by_name_from_root(name).unwrap().0;
156///     scene.graph[handle].as_navigational_mesh_mut()
157/// }
158/// ```
159#[derive(Debug, Clone, Visit, Reflect, Default, ComponentProvider)]
160#[reflect(derived_type = "Node")]
161pub struct NavigationalMesh {
162    base: Base,
163    #[reflect(read_only)]
164    navmesh: InheritableVariable<Container>,
165}
166
167impl TypeUuidProvider for NavigationalMesh {
168    fn type_uuid() -> Uuid {
169        uuid!("d0ce963c-b50a-4707-bd21-af6dc0d1c668")
170    }
171}
172
173impl Deref for NavigationalMesh {
174    type Target = Base;
175
176    fn deref(&self) -> &Self::Target {
177        &self.base
178    }
179}
180
181impl DerefMut for NavigationalMesh {
182    fn deref_mut(&mut self) -> &mut Self::Target {
183        &mut self.base
184    }
185}
186
187impl ConstructorProvider<Node, Graph> for NavigationalMesh {
188    fn constructor() -> NodeConstructor {
189        NodeConstructor::new::<Self>().with_variant("Navmesh", |_| {
190            let navmesh = Navmesh::new(
191                vec![TriangleDefinition([0, 1, 2]), TriangleDefinition([0, 2, 3])],
192                vec![
193                    Vector3::new(-1.0, 0.0, 1.0),
194                    Vector3::new(1.0, 0.0, 1.0),
195                    Vector3::new(1.0, 0.0, -1.0),
196                    Vector3::new(-1.0, 0.0, -1.0),
197                ],
198            );
199
200            NavigationalMeshBuilder::new(BaseBuilder::new().with_name("Navmesh"))
201                .with_navmesh(navmesh)
202                .build_node()
203                .into()
204        })
205    }
206}
207
208impl NodeTrait for NavigationalMesh {
209    fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
210        self.base.local_bounding_box()
211    }
212
213    fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
214        self.base.world_bounding_box()
215    }
216
217    fn id(&self) -> Uuid {
218        Self::type_uuid()
219    }
220
221    fn debug_draw(&self, ctx: &mut SceneDrawingContext) {
222        let navmesh = self.navmesh.0.read();
223
224        for vertex in navmesh.vertices().iter() {
225            ctx.draw_sphere(*vertex, 6, 6, 0.1, Color::GREEN);
226        }
227
228        for triangle in navmesh.triangles().iter() {
229            for edge in &triangle.edges() {
230                ctx.add_line(Line {
231                    begin: navmesh.vertices()[edge.a as usize],
232                    end: navmesh.vertices()[edge.b as usize],
233                    color: Color::GREEN,
234                });
235            }
236        }
237    }
238}
239
240impl NavigationalMesh {
241    /// Returns a reference to the inner navigational mesh.
242    pub fn navmesh_ref(&self) -> RwLockReadGuard<Navmesh> {
243        self.navmesh.0.read()
244    }
245
246    /// Returns a reference to the inner navigational mesh.
247    pub fn navmesh_mut(&mut self) -> RwLockWriteGuard<Navmesh> {
248        self.navmesh.0.write()
249    }
250
251    /// Returns a shared reference to the inner navigational mesh. It could be used to perform
252    /// off-thread path calculations.
253    pub fn navmesh(&self) -> Arc<RwLock<Navmesh>> {
254        self.navmesh.0.clone()
255    }
256}
257
258/// Creates navigational meshes and adds them to a scene graph.
259pub struct NavigationalMeshBuilder {
260    base_builder: BaseBuilder,
261    navmesh: Navmesh,
262}
263
264impl NavigationalMeshBuilder {
265    /// Creates new navigational mesh builder.
266    pub fn new(base_builder: BaseBuilder) -> Self {
267        Self {
268            base_builder,
269            navmesh: Default::default(),
270        }
271    }
272
273    /// Sets the actual navigational mesh.
274    pub fn with_navmesh(mut self, navmesh: Navmesh) -> Self {
275        self.navmesh = navmesh;
276        self
277    }
278
279    fn build_navigational_mesh(self) -> NavigationalMesh {
280        NavigationalMesh {
281            base: self.base_builder.build_base(),
282            navmesh: InheritableVariable::new_modified(Container(Arc::new(RwLock::new(
283                self.navmesh,
284            )))),
285        }
286    }
287
288    /// Creates new navigational mesh instance.
289    pub fn build_node(self) -> Node {
290        Node::new(self.build_navigational_mesh())
291    }
292
293    /// Creates new navigational mesh instance and adds it to the graph.
294    pub fn build(self, graph: &mut Graph) -> Handle<NavigationalMesh> {
295        graph.add_node(self.build_node()).to_variant()
296    }
297}