kengaai_navigation/
lib.rs1use glam::{Vec3, Vec2};
2use serde::{Deserialize, Serialize};
3use anyhow::Result;
4
5#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
6struct Vec3Ser {
7 x: f32,
8 y: f32,
9 z: f32,
10}
11
12impl From<Vec3> for Vec3Ser {
13 fn from(v: Vec3) -> Self {
14 Self { x: v.x, y: v.y, z: v.z }
15 }
16}
17
18impl From<Vec3Ser> for Vec3 {
19 fn from(v: Vec3Ser) -> Self {
20 Vec3::new(v.x, v.y, v.z)
21 }
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct NavTriangle {
27 #[serde(with = "serde_vertices")]
28 pub vertices: [Vec3; 3],
29 pub neighbors: Vec<usize>, #[serde(with = "serde_vec3")]
31 pub center: Vec3,
32 pub area: f32,
33}
34
35mod serde_vec3 {
36 use super::*;
37 use serde::{Deserialize, Deserializer, Serialize, Serializer};
38
39 pub fn serialize<S>(v: &Vec3, serializer: S) -> Result<S::Ok, S::Error>
40 where
41 S: Serializer,
42 {
43 Vec3Ser::from(*v).serialize(serializer)
44 }
45
46 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec3, D::Error>
47 where
48 D: Deserializer<'de>,
49 {
50 Vec3Ser::deserialize(deserializer).map(Vec3::from)
51 }
52}
53
54mod serde_vertices {
55 use super::*;
56 use serde::{Deserialize, Deserializer, Serialize, Serializer};
57
58 pub fn serialize<S>(v: &[Vec3; 3], serializer: S) -> Result<S::Ok, S::Error>
59 where
60 S: Serializer,
61 {
62 let ser: [Vec3Ser; 3] = [Vec3Ser::from(v[0]), Vec3Ser::from(v[1]), Vec3Ser::from(v[2])];
63 ser.serialize(serializer)
64 }
65
66 pub fn deserialize<'de, D>(deserializer: D) -> Result<[Vec3; 3], D::Error>
67 where
68 D: Deserializer<'de>,
69 {
70 let ser: [Vec3Ser; 3] = Deserialize::deserialize(deserializer)?;
71 Ok([Vec3::from(ser[0]), Vec3::from(ser[1]), Vec3::from(ser[2])])
72 }
73}
74
75impl NavTriangle {
76 pub fn new(vertices: [Vec3; 3]) -> Self {
77 let center = (vertices[0] + vertices[1] + vertices[2]) / 3.0;
78 let v1 = vertices[1] - vertices[0];
79 let v2 = vertices[2] - vertices[0];
80 let area = v1.cross(v2).length() / 2.0;
81
82 Self {
83 vertices,
84 neighbors: Vec::new(),
85 center,
86 area,
87 }
88 }
89
90 pub fn contains_point_2d(&self, point: Vec3) -> bool {
92 let p = Vec2::new(point.x, point.z);
93 let v0 = Vec2::new(self.vertices[0].x, self.vertices[0].z);
94 let v1 = Vec2::new(self.vertices[1].x, self.vertices[1].z);
95 let v2 = Vec2::new(self.vertices[2].x, self.vertices[2].z);
96
97 let sign1 = (p - v0).dot(Vec2::new(-(v1 - v0).y, (v1 - v0).x));
99 let sign2 = (p - v1).dot(Vec2::new(-(v2 - v1).y, (v2 - v1).x));
100 let sign3 = (p - v2).dot(Vec2::new(-(v0 - v2).y, (v0 - v2).x));
101
102 (sign1 >= 0.0 && sign2 >= 0.0 && sign3 >= 0.0) || (sign1 <= 0.0 && sign2 <= 0.0 && sign3 <= 0.0)
103 }
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct NavMesh {
109 pub triangles: Vec<NavTriangle>,
110 #[serde(with = "serde_vec3")]
111 pub bounds_min: Vec3,
112 #[serde(with = "serde_vec3")]
113 pub bounds_max: Vec3,
114}
115
116impl NavMesh {
117 pub fn new() -> Self {
118 Self {
119 triangles: Vec::new(),
120 bounds_min: Vec3::splat(f32::MAX),
121 bounds_max: Vec3::splat(f32::MIN),
122 }
123 }
124
125 pub fn add_triangle(&mut self, triangle: NavTriangle) {
127 for vertex in &triangle.vertices {
129 self.bounds_min = self.bounds_min.min(*vertex);
130 self.bounds_max = self.bounds_max.max(*vertex);
131 }
132 self.triangles.push(triangle);
133 }
134
135 pub fn find_triangle(&self, point: Vec3) -> Option<usize> {
137 for (i, triangle) in self.triangles.iter().enumerate() {
138 if triangle.contains_point_2d(point) {
139 return Some(i);
140 }
141 }
142 None
143 }
144
145 pub fn find_nearest_triangle(&self, point: Vec3) -> Option<usize> {
147 let mut nearest_index = None;
148 let mut nearest_distance = f32::MAX;
149
150 for (i, triangle) in self.triangles.iter().enumerate() {
151 let distance = triangle.center.distance(point);
152 if distance < nearest_distance {
153 nearest_distance = distance;
154 nearest_index = Some(i);
155 }
156 }
157
158 nearest_index
159 }
160}
161
162impl Default for NavMesh {
163 fn default() -> Self {
164 Self::new()
165 }
166}
167
168pub struct Pathfinder {
170 nav_mesh: NavMesh,
171}
172
173impl Pathfinder {
174 pub fn new(nav_mesh: NavMesh) -> Self {
175 Self { nav_mesh }
176 }
177
178 pub fn find_path(&self, start: Vec3, end: Vec3) -> Result<Vec<Vec3>> {
180 let start_triangle = self.nav_mesh.find_triangle(start)
181 .or_else(|| self.nav_mesh.find_nearest_triangle(start))
182 .ok_or_else(|| anyhow::anyhow!("Start point not on nav mesh"))?;
183
184 let end_triangle = self.nav_mesh.find_triangle(end)
185 .or_else(|| self.nav_mesh.find_nearest_triangle(end))
186 .ok_or_else(|| anyhow::anyhow!("End point not on nav mesh"))?;
187
188 if start_triangle == end_triangle {
189 return Ok(vec![start, end]);
190 }
191
192 let mut path = vec![start];
194 let mut current = start_triangle;
195 let mut visited = std::collections::HashSet::new();
196 visited.insert(current);
197
198 while current != end_triangle {
200 let current_triangle = &self.nav_mesh.triangles[current];
201 let mut best_neighbor = None;
202 let mut best_distance = f32::MAX;
203
204 for &neighbor_idx in ¤t_triangle.neighbors {
205 if visited.contains(&neighbor_idx) {
206 continue;
207 }
208
209 let neighbor = &self.nav_mesh.triangles[neighbor_idx];
210 let distance = neighbor.center.distance(end);
211 if distance < best_distance {
212 best_distance = distance;
213 best_neighbor = Some(neighbor_idx);
214 }
215 }
216
217 if let Some(next) = best_neighbor {
218 path.push(self.nav_mesh.triangles[next].center);
219 current = next;
220 visited.insert(current);
221 } else {
222 break;
224 }
225 }
226
227 path.push(end);
228 Ok(path)
229 }
230}
231