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::BaseSceneGraph;
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/// fn create_navmesh(graph: &mut Graph) -> Handle<Node> {
89///     // A simple navmesh with four vertices and two triangles.
90///     let navmesh = Navmesh::new(
91///         vec![TriangleDefinition([0, 1, 2]), TriangleDefinition([0, 2, 3])],
92///         vec![
93///             Vector3::new(-1.0, 0.0, 1.0),
94///             Vector3::new(1.0, 0.0, 1.0),
95///             Vector3::new(1.0, 0.0, -1.0),
96///             Vector3::new(-1.0, 0.0, -1.0),
97///         ],
98///     );
99///     NavigationalMeshBuilder::new(BaseBuilder::new())
100///         .with_navmesh(navmesh)
101///         .build(graph)
102/// }
103/// ```
104///
105/// ## Agents
106///
107/// Navigational mesh agent helps you to build paths along the surface of a navigational mesh and follow it. Agents can be
108/// used to drive the motion of your game characters. Every agent knows about its target and automatically rebuilds the path
109/// if the target has moved. Navmesh agents are able to move along the path, providing you with their current position, so you
110/// can use it to perform an actual motion of your game characters. Agents work together with navigational meshes, you need
111/// to update their state every frame, so they can recalculate path if needed. A simple example could something like this:
112///
113/// ```rust
114/// # use fyrox_impl::utils::navmesh::NavmeshAgent;
115/// # struct Foo {
116/// // Add this to your script
117/// agent: NavmeshAgent
118/// # }
119/// ```
120///
121/// After that, you need to update the agent every frame to make sure it will follow the target:
122///
123/// ```rust
124/// # use fyrox_impl::{
125/// #    core::algebra::Vector3, scene::navmesh::NavigationalMesh, utils::navmesh::NavmeshAgent,
126/// # };
127/// fn update_agent(
128///     agent: &mut NavmeshAgent,
129///     target: Vector3<f32>,
130///     dt: f32,
131///     navmesh: &NavigationalMesh,
132/// ) {
133///     // Set the target to follow and the speed.
134///     agent.set_target(target);
135///     agent.set_speed(1.0);
136///
137///     // Update the agent.
138///     agent.update(dt, &navmesh.navmesh_ref()).unwrap();
139///
140///     // Print its position - you can use this position as target point of your game character.
141///     println!("{}", agent.position());
142/// }
143/// ```
144///
145/// This method should be called in `on_update` of your script. It accepts four parameters: a reference to the agent, a
146/// target which it will follow, a time step (`context.dt`), and a reference to navigational mesh node. You can fetch
147/// navigational mesh from the scene graph by its name:
148///
149/// ```rust
150/// # use fyrox_impl::scene::{navmesh::NavigationalMesh, Scene};
151/// # use fyrox_graph::SceneGraph;
152/// fn find_navmesh<'a>(scene: &'a mut Scene, name: &str) -> &'a mut NavigationalMesh {
153///     let handle = scene.graph.find_by_name_from_root(name).unwrap().0;
154///     scene.graph[handle].as_navigational_mesh_mut()
155/// }
156/// ```
157#[derive(Debug, Clone, Visit, Reflect, Default, ComponentProvider)]
158pub struct NavigationalMesh {
159    base: Base,
160    #[reflect(read_only)]
161    navmesh: InheritableVariable<Container>,
162}
163
164impl TypeUuidProvider for NavigationalMesh {
165    fn type_uuid() -> Uuid {
166        uuid!("d0ce963c-b50a-4707-bd21-af6dc0d1c668")
167    }
168}
169
170impl Deref for NavigationalMesh {
171    type Target = Base;
172
173    fn deref(&self) -> &Self::Target {
174        &self.base
175    }
176}
177
178impl DerefMut for NavigationalMesh {
179    fn deref_mut(&mut self) -> &mut Self::Target {
180        &mut self.base
181    }
182}
183
184impl ConstructorProvider<Node, Graph> for NavigationalMesh {
185    fn constructor() -> NodeConstructor {
186        NodeConstructor::new::<Self>().with_variant("Navmesh", |_| {
187            let navmesh = Navmesh::new(
188                vec![TriangleDefinition([0, 1, 2]), TriangleDefinition([0, 2, 3])],
189                vec![
190                    Vector3::new(-1.0, 0.0, 1.0),
191                    Vector3::new(1.0, 0.0, 1.0),
192                    Vector3::new(1.0, 0.0, -1.0),
193                    Vector3::new(-1.0, 0.0, -1.0),
194                ],
195            );
196
197            NavigationalMeshBuilder::new(BaseBuilder::new().with_name("Navmesh"))
198                .with_navmesh(navmesh)
199                .build_node()
200                .into()
201        })
202    }
203}
204
205impl NodeTrait for NavigationalMesh {
206    fn local_bounding_box(&self) -> AxisAlignedBoundingBox {
207        self.base.local_bounding_box()
208    }
209
210    fn world_bounding_box(&self) -> AxisAlignedBoundingBox {
211        self.base.world_bounding_box()
212    }
213
214    fn id(&self) -> Uuid {
215        Self::type_uuid()
216    }
217
218    fn debug_draw(&self, ctx: &mut SceneDrawingContext) {
219        let navmesh = self.navmesh.0.read();
220
221        for vertex in navmesh.vertices().iter() {
222            ctx.draw_sphere(*vertex, 6, 6, 0.1, Color::GREEN);
223        }
224
225        for triangle in navmesh.triangles().iter() {
226            for edge in &triangle.edges() {
227                ctx.add_line(Line {
228                    begin: navmesh.vertices()[edge.a as usize],
229                    end: navmesh.vertices()[edge.b as usize],
230                    color: Color::GREEN,
231                });
232            }
233        }
234    }
235}
236
237impl NavigationalMesh {
238    /// Returns a reference to the inner navigational mesh.
239    pub fn navmesh_ref(&self) -> RwLockReadGuard<Navmesh> {
240        self.navmesh.0.read()
241    }
242
243    /// Returns a reference to the inner navigational mesh.
244    pub fn navmesh_mut(&mut self) -> RwLockWriteGuard<Navmesh> {
245        self.navmesh.0.write()
246    }
247
248    /// Returns a shared reference to the inner navigational mesh. It could be used to perform
249    /// off-thread path calculations.
250    pub fn navmesh(&self) -> Arc<RwLock<Navmesh>> {
251        self.navmesh.0.clone()
252    }
253}
254
255/// Creates navigational meshes and adds them to a scene graph.
256pub struct NavigationalMeshBuilder {
257    base_builder: BaseBuilder,
258    navmesh: Navmesh,
259}
260
261impl NavigationalMeshBuilder {
262    /// Creates new navigational mesh builder.
263    pub fn new(base_builder: BaseBuilder) -> Self {
264        Self {
265            base_builder,
266            navmesh: Default::default(),
267        }
268    }
269
270    /// Sets the actual navigational mesh.
271    pub fn with_navmesh(mut self, navmesh: Navmesh) -> Self {
272        self.navmesh = navmesh;
273        self
274    }
275
276    fn build_navigational_mesh(self) -> NavigationalMesh {
277        NavigationalMesh {
278            base: self.base_builder.build_base(),
279            navmesh: InheritableVariable::new_modified(Container(Arc::new(RwLock::new(
280                self.navmesh,
281            )))),
282        }
283    }
284
285    /// Creates new navigational mesh instance.
286    pub fn build_node(self) -> Node {
287        Node::new(self.build_navigational_mesh())
288    }
289
290    /// Creates new navigational mesh instance and adds it to the graph.
291    pub fn build(self, graph: &mut Graph) -> Handle<Node> {
292        graph.add_node(self.build_node())
293    }
294}