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}