1use crate::types::Vec3;
23use crate::wmo_group_types::WmoBspNode;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum BspAxisType {
28 X,
30 Y,
32 Z,
34 Other,
36}
37
38pub trait BspNodeExt {
40 fn is_leaf(&self) -> bool;
42
43 fn get_axis_type(&self) -> BspAxisType;
45
46 fn negative_child(&self) -> i16;
48
49 fn positive_child(&self) -> i16;
51}
52
53impl BspNodeExt for WmoBspNode {
54 fn is_leaf(&self) -> bool {
55 self.num_faces > 0 || (self.children[0] == -1 && self.children[1] == -1)
57 }
58
59 fn get_axis_type(&self) -> BspAxisType {
60 let nx = self.plane.normal.x.abs();
61 let ny = self.plane.normal.y.abs();
62 let nz = self.plane.normal.z.abs();
63
64 const EPSILON: f32 = 0.0001;
66
67 if nx > 1.0 - EPSILON && ny < EPSILON && nz < EPSILON {
68 BspAxisType::X
69 } else if ny > 1.0 - EPSILON && nx < EPSILON && nz < EPSILON {
70 BspAxisType::Y
71 } else if nz > 1.0 - EPSILON && nx < EPSILON && ny < EPSILON {
72 BspAxisType::Z
73 } else {
74 BspAxisType::Other
75 }
76 }
77
78 fn negative_child(&self) -> i16 {
79 self.children[0]
80 }
81
82 fn positive_child(&self) -> i16 {
83 self.children[1]
84 }
85}
86
87#[derive(Debug, Clone)]
89pub struct BspTree {
90 nodes: Vec<WmoBspNode>,
91}
92
93impl BspTree {
94 pub fn new(nodes: Vec<WmoBspNode>) -> Self {
96 Self { nodes }
97 }
98
99 pub fn empty() -> Self {
101 Self { nodes: Vec::new() }
102 }
103
104 pub fn is_empty(&self) -> bool {
106 self.nodes.is_empty()
107 }
108
109 pub fn len(&self) -> usize {
111 self.nodes.len()
112 }
113
114 pub fn get_node(&self, index: usize) -> Option<&WmoBspNode> {
116 self.nodes.get(index)
117 }
118
119 pub fn query_point(&self, point: &[f32; 3]) -> Vec<usize> {
123 let mut leaves = Vec::new();
124 if !self.nodes.is_empty() {
125 self.query_recursive(point, 0, &mut leaves);
126 }
127 leaves
128 }
129
130 fn query_recursive(&self, point: &[f32; 3], node_index: i16, leaves: &mut Vec<usize>) {
132 if node_index < 0 {
133 return;
134 }
135
136 let idx = node_index as usize;
137 if idx >= self.nodes.len() {
138 return;
139 }
140
141 let node = &self.nodes[idx];
142
143 if node.is_leaf() {
145 leaves.push(idx);
146 return;
147 }
148
149 let axis = node.get_axis_type();
150
151 if axis == BspAxisType::Z {
154 self.query_recursive(point, node.negative_child(), leaves);
155 self.query_recursive(point, node.positive_child(), leaves);
156 } else {
157 let component = match axis {
159 BspAxisType::X => point[0],
160 BspAxisType::Y => point[1],
161 _ => {
162 let dist = node.plane.normal.x * point[0]
164 + node.plane.normal.y * point[1]
165 + node.plane.normal.z * point[2]
166 - node.plane.distance;
167 if dist < 0.0 {
168 self.query_recursive(point, node.negative_child(), leaves);
169 } else {
170 self.query_recursive(point, node.positive_child(), leaves);
171 }
172 return;
173 }
174 };
175
176 if component < node.plane.distance {
177 self.query_recursive(point, node.negative_child(), leaves);
178 } else {
179 self.query_recursive(point, node.positive_child(), leaves);
180 }
181 }
182 }
183
184 pub fn pick_closest_tri_neg_z(
200 &self,
201 point: &[f32; 3],
202 vertices: &[Vec3],
203 indices: &[u16],
204 ) -> Option<usize> {
205 let leaf_indices = self.query_point(point);
206
207 let mut closest_t = f32::NEG_INFINITY;
208 let mut closest_tri = None;
209
210 for leaf_idx in leaf_indices {
211 let node = &self.nodes[leaf_idx];
212 if node.num_faces == 0 {
213 continue;
214 }
215
216 for face_offset in 0..node.num_faces {
218 let face_index = node.first_face as usize + face_offset as usize;
219 let tri_start = face_index * 3;
220
221 if tri_start + 2 >= indices.len() {
222 continue;
223 }
224
225 let i0 = indices[tri_start] as usize;
226 let i1 = indices[tri_start + 1] as usize;
227 let i2 = indices[tri_start + 2] as usize;
228
229 if i0 >= vertices.len() || i1 >= vertices.len() || i2 >= vertices.len() {
230 continue;
231 }
232
233 let v0 = &vertices[i0];
234 let v1 = &vertices[i1];
235 let v2 = &vertices[i2];
236
237 if let Some(t) = ray_triangle_intersect_neg_z(point, v0, v1, v2) {
239 if t > 0.0 && t > closest_t {
242 closest_t = t;
243 closest_tri = Some(face_index);
244 }
245 }
246 }
247 }
248
249 closest_tri
250 }
251}
252
253fn ray_triangle_intersect_neg_z(origin: &[f32; 3], v0: &Vec3, v1: &Vec3, v2: &Vec3) -> Option<f32> {
261 let edge1 = [v1.x - v0.x, v1.y - v0.y, v1.z - v0.z];
263 let edge2 = [v2.x - v0.x, v2.y - v0.y, v2.z - v0.z];
264
265 let h = [edge2[1], -edge2[0], 0.0];
267
268 let a = edge1[0] * h[0] + edge1[1] * h[1] + edge1[2] * h[2];
269
270 const EPSILON: f32 = 0.000001;
271 if a > -EPSILON && a < EPSILON {
272 return None; }
274
275 let f = 1.0 / a;
276 let s = [origin[0] - v0.x, origin[1] - v0.y, origin[2] - v0.z];
277
278 let u = f * (s[0] * h[0] + s[1] * h[1] + s[2] * h[2]);
279 if !(0.0..=1.0).contains(&u) {
280 return None;
281 }
282
283 let q = [
285 s[1] * edge1[2] - s[2] * edge1[1],
286 s[2] * edge1[0] - s[0] * edge1[2],
287 s[0] * edge1[1] - s[1] * edge1[0],
288 ];
289
290 let v = f * (-q[2]);
292 if v < 0.0 || u + v > 1.0 {
293 return None;
294 }
295
296 let t = f * (edge2[0] * q[0] + edge2[1] * q[1] + edge2[2] * q[2]);
298
299 if t > EPSILON { Some(t) } else { None }
300}
301
302pub fn point_in_group(point: &[f32; 3], bsp: &BspTree, vertices: &[Vec3], indices: &[u16]) -> bool {
304 bsp.pick_closest_tri_neg_z(point, vertices, indices)
305 .is_some()
306}
307
308#[cfg(test)]
309mod tests {
310 use super::*;
311 use crate::wmo_group_types::WmoPlane;
312
313 fn make_plane(axis: BspAxisType, dist: f32) -> WmoPlane {
314 let normal = match axis {
315 BspAxisType::X => Vec3 {
316 x: 1.0,
317 y: 0.0,
318 z: 0.0,
319 },
320 BspAxisType::Y => Vec3 {
321 x: 0.0,
322 y: 1.0,
323 z: 0.0,
324 },
325 BspAxisType::Z => Vec3 {
326 x: 0.0,
327 y: 0.0,
328 z: 1.0,
329 },
330 BspAxisType::Other => Vec3 {
331 x: 0.577,
332 y: 0.577,
333 z: 0.577,
334 },
335 };
336 WmoPlane {
337 normal,
338 distance: dist,
339 }
340 }
341
342 #[test]
343 fn test_bsp_node_is_leaf() {
344 let leaf = WmoBspNode {
345 plane: make_plane(BspAxisType::X, 0.0),
346 children: [-1, -1],
347 first_face: 0,
348 num_faces: 2,
349 };
350 assert!(leaf.is_leaf());
351
352 let internal = WmoBspNode {
353 plane: make_plane(BspAxisType::X, 0.0),
354 children: [1, 2],
355 first_face: 0,
356 num_faces: 0,
357 };
358 assert!(!internal.is_leaf());
359 }
360
361 #[test]
362 fn test_bsp_axis_type() {
363 let x_node = WmoBspNode {
364 plane: make_plane(BspAxisType::X, 5.0),
365 children: [-1, -1],
366 first_face: 0,
367 num_faces: 0,
368 };
369 assert_eq!(x_node.get_axis_type(), BspAxisType::X);
370
371 let y_node = WmoBspNode {
372 plane: make_plane(BspAxisType::Y, 5.0),
373 children: [-1, -1],
374 first_face: 0,
375 num_faces: 0,
376 };
377 assert_eq!(y_node.get_axis_type(), BspAxisType::Y);
378
379 let z_node = WmoBspNode {
380 plane: make_plane(BspAxisType::Z, 5.0),
381 children: [-1, -1],
382 first_face: 0,
383 num_faces: 0,
384 };
385 assert_eq!(z_node.get_axis_type(), BspAxisType::Z);
386 }
387
388 #[test]
389 fn test_empty_bsp() {
390 let bsp = BspTree::empty();
391 assert!(bsp.is_empty());
392 assert_eq!(bsp.query_point(&[0.0, 0.0, 0.0]), Vec::<usize>::new());
393 }
394
395 #[test]
396 fn test_single_leaf_bsp() {
397 let nodes = vec![WmoBspNode {
398 plane: make_plane(BspAxisType::X, 0.0),
399 children: [-1, -1],
400 first_face: 0,
401 num_faces: 1,
402 }];
403 let bsp = BspTree::new(nodes);
404
405 let leaves = bsp.query_point(&[0.0, 0.0, 0.0]);
406 assert_eq!(leaves.len(), 1);
407 assert_eq!(leaves[0], 0);
408 }
409
410 #[test]
411 fn test_simple_x_split() {
412 let nodes = vec![
414 WmoBspNode {
415 plane: make_plane(BspAxisType::X, 0.0),
416 children: [1, 2],
417 first_face: 0,
418 num_faces: 0,
419 },
420 WmoBspNode {
421 plane: make_plane(BspAxisType::X, 0.0),
422 children: [-1, -1],
423 first_face: 0,
424 num_faces: 1,
425 },
426 WmoBspNode {
427 plane: make_plane(BspAxisType::X, 0.0),
428 children: [-1, -1],
429 first_face: 1,
430 num_faces: 1,
431 },
432 ];
433 let bsp = BspTree::new(nodes);
434
435 let leaves_neg = bsp.query_point(&[-5.0, 0.0, 0.0]);
437 assert_eq!(leaves_neg, vec![1]);
438
439 let leaves_pos = bsp.query_point(&[5.0, 0.0, 0.0]);
441 assert_eq!(leaves_pos, vec![2]);
442 }
443
444 #[test]
445 fn test_z_split_traverses_both() {
446 let nodes = vec![
448 WmoBspNode {
449 plane: make_plane(BspAxisType::Z, 0.0),
450 children: [1, 2],
451 first_face: 0,
452 num_faces: 0,
453 },
454 WmoBspNode {
455 plane: make_plane(BspAxisType::X, 0.0),
456 children: [-1, -1],
457 first_face: 0,
458 num_faces: 1,
459 },
460 WmoBspNode {
461 plane: make_plane(BspAxisType::X, 0.0),
462 children: [-1, -1],
463 first_face: 1,
464 num_faces: 1,
465 },
466 ];
467 let bsp = BspTree::new(nodes);
468
469 let mut leaves = bsp.query_point(&[0.0, 0.0, 5.0]);
471 leaves.sort();
472 assert_eq!(leaves, vec![1, 2]);
473 }
474
475 #[test]
476 fn test_ray_triangle_intersect() {
477 let v0 = Vec3 {
479 x: -1.0,
480 y: -1.0,
481 z: 0.0,
482 };
483 let v1 = Vec3 {
484 x: 1.0,
485 y: -1.0,
486 z: 0.0,
487 };
488 let v2 = Vec3 {
489 x: 0.0,
490 y: 1.0,
491 z: 0.0,
492 };
493
494 let hit = ray_triangle_intersect_neg_z(&[0.0, 0.0, 5.0], &v0, &v1, &v2);
496 assert!(hit.is_some());
497 assert!((hit.unwrap() - 5.0).abs() < 0.001);
498
499 let miss = ray_triangle_intersect_neg_z(&[10.0, 10.0, 5.0], &v0, &v1, &v2);
501 assert!(miss.is_none());
502
503 let below = ray_triangle_intersect_neg_z(&[0.0, 0.0, -5.0], &v0, &v1, &v2);
505 assert!(below.is_none());
506 }
507}