1use crate::{
2 block_hit_result::BlockHitResult,
3 direction::{Axis, Direction},
4 math::EPSILON,
5 position::{BlockPos, Vec3},
6};
7
8#[derive(Copy, Clone, Debug, PartialEq, Default)]
10pub struct AABB {
11 pub min: Vec3,
12 pub max: Vec3,
13}
14
15pub struct ClipPointOpts<'a> {
16 pub t: &'a mut f64,
17 pub approach_dir: Option<Direction>,
18 pub delta: &'a Vec3,
19 pub begin: f64,
20 pub min_x: f64,
21 pub min_z: f64,
22 pub max_x: f64,
23 pub max_z: f64,
24 pub result_dir: Direction,
25 pub start: &'a Vec3,
26}
27
28impl AABB {
29 pub fn contract(&self, x: f64, y: f64, z: f64) -> AABB {
30 let mut min = self.min;
31 let mut max = self.max;
32
33 if x < 0.0 {
34 min.x -= x;
35 } else if x > 0.0 {
36 max.x -= x;
37 }
38
39 if y < 0.0 {
40 min.y -= y;
41 } else if y > 0.0 {
42 max.y -= y;
43 }
44
45 if z < 0.0 {
46 min.z -= z;
47 } else if z > 0.0 {
48 max.z -= z;
49 }
50
51 AABB { min, max }
52 }
53
54 pub fn expand_towards(&self, other: &Vec3) -> AABB {
55 let mut min_x = self.min.x;
56 let mut min_y = self.min.y;
57 let mut min_z = self.min.z;
58
59 let mut max_x = self.max.x;
60 let mut max_y = self.max.y;
61 let mut max_z = self.max.z;
62
63 if other.x < 0.0 {
64 min_x += other.x;
65 } else if other.x > 0.0 {
66 max_x += other.x;
67 }
68
69 if other.y < 0.0 {
70 min_y += other.y;
71 } else if other.y > 0.0 {
72 max_y += other.y;
73 }
74
75 if other.z < 0.0 {
76 min_z += other.z;
77 } else if other.z > 0.0 {
78 max_z += other.z;
79 }
80
81 AABB {
82 min: Vec3::new(min_x, min_y, min_z),
83 max: Vec3::new(max_x, max_y, max_z),
84 }
85 }
86
87 pub fn inflate(&self, x: f64, y: f64, z: f64) -> AABB {
88 let min_x = self.min.x - x;
89 let min_y = self.min.y - y;
90 let min_z = self.min.z - z;
91
92 let max_x = self.max.x + x;
93 let max_y = self.max.y + y;
94 let max_z = self.max.z + z;
95
96 AABB {
97 min: Vec3::new(min_x, min_y, min_z),
98 max: Vec3::new(max_x, max_y, max_z),
99 }
100 }
101
102 pub fn intersect(&self, other: &AABB) -> AABB {
103 let min_x = self.min.x.max(other.min.x);
104 let min_y = self.min.y.max(other.min.y);
105 let min_z = self.min.z.max(other.min.z);
106
107 let max_x = self.max.x.min(other.max.x);
108 let max_y = self.max.y.min(other.max.y);
109 let max_z = self.max.z.min(other.max.z);
110
111 AABB {
112 min: Vec3::new(min_x, min_y, min_z),
113 max: Vec3::new(max_x, max_y, max_z),
114 }
115 }
116
117 pub fn minmax(&self, other: &AABB) -> AABB {
118 let min_x = self.min.x.min(other.min.x);
119 let min_y = self.min.y.min(other.min.y);
120 let min_z = self.min.z.min(other.min.z);
121
122 let max_x = self.max.x.max(other.max.x);
123 let max_y = self.max.y.max(other.max.y);
124 let max_z = self.max.z.max(other.max.z);
125
126 AABB {
127 min: Vec3::new(min_x, min_y, min_z),
128 max: Vec3::new(max_x, max_y, max_z),
129 }
130 }
131
132 pub fn move_relative(&self, delta: Vec3) -> AABB {
133 AABB {
134 min: self.min + delta,
135 max: self.max + delta,
136 }
137 }
138
139 pub fn intersects_aabb(&self, other: &AABB) -> bool {
140 self.min.x < other.max.x
141 && self.max.x > other.min.x
142 && self.min.y < other.max.y
143 && self.max.y > other.min.y
144 && self.min.z < other.max.z
145 && self.max.z > other.min.z
146 }
147 pub fn intersects_vec3(&self, other: &Vec3, other2: &Vec3) -> bool {
148 self.intersects_aabb(&AABB {
149 min: Vec3::new(
150 other.x.min(other2.x),
151 other.y.min(other2.y),
152 other.z.min(other2.z),
153 ),
154 max: Vec3::new(
155 other.x.max(other2.x),
156 other.y.max(other2.y),
157 other.z.max(other2.z),
158 ),
159 })
160 }
161
162 pub fn contains(&self, point: &Vec3) -> bool {
163 point.x >= self.min.x
164 && point.x < self.max.x
165 && point.y >= self.min.y
166 && point.y < self.max.y
167 && point.z >= self.min.z
168 && point.z < self.max.z
169 }
170
171 pub fn size(&self) -> f64 {
172 let x = self.get_size(Axis::X);
173 let y = self.get_size(Axis::Y);
174 let z = self.get_size(Axis::Z);
175 (x + y + z) / 3.0
176 }
177
178 pub fn get_size(&self, axis: Axis) -> f64 {
179 axis.choose(
180 self.max.x - self.min.x,
181 self.max.y - self.min.y,
182 self.max.z - self.min.z,
183 )
184 }
185
186 pub fn deflate(&mut self, x: f64, y: f64, z: f64) -> AABB {
187 self.inflate(-x, -y, -z)
188 }
189
190 pub fn deflate_all(&mut self, amount: f64) -> AABB {
191 self.deflate(amount, amount, amount)
192 }
193
194 pub fn clip(&self, min: &Vec3, max: &Vec3) -> Option<Vec3> {
195 let mut t = 1.0;
196 let delta = max - min;
197 let _dir = Self::get_direction_aabb(self, min, &mut t, None, &delta)?;
198 Some(min + &(delta * t))
199 }
200
201 pub fn clip_with_from_and_to(min: &Vec3, max: &Vec3, from: &Vec3, to: &Vec3) -> Option<Vec3> {
202 let mut t = 1.0;
203 let delta = to - from;
204 let _dir = Self::get_direction(min, max, from, &mut t, None, &delta)?;
205 Some(from + &(delta * t))
206 }
207
208 pub fn clip_iterable(
209 boxes: &Vec<AABB>,
210 from: &Vec3,
211 to: &Vec3,
212 pos: &BlockPos,
213 ) -> Option<BlockHitResult> {
214 let mut t = 1.0;
215 let mut dir = None;
216 let delta = to - from;
217
218 for aabb in boxes {
219 dir = Self::get_direction_aabb(
220 &aabb.move_relative(pos.to_vec3_floored()),
221 from,
222 &mut t,
223 dir,
224 &delta,
225 );
226 }
227 let dir = dir?;
228 Some(BlockHitResult {
229 location: from + &(delta * t),
230 direction: dir,
231 block_pos: *pos,
232 inside: false,
233 miss: false,
234 world_border: false,
235 })
236 }
237
238 fn get_direction_aabb(
239 &self,
240 from: &Vec3,
241 t: &mut f64,
242 dir: Option<Direction>,
243 delta: &Vec3,
244 ) -> Option<Direction> {
245 AABB::get_direction(&self.min, &self.max, from, t, dir, delta)
246 }
247
248 fn get_direction(
249 min: &Vec3,
250 max: &Vec3,
251 from: &Vec3,
252 t: &mut f64,
253 mut dir: Option<Direction>,
254 delta: &Vec3,
255 ) -> Option<Direction> {
256 if delta.x > EPSILON {
257 dir = Self::clip_point(ClipPointOpts {
258 t,
259 approach_dir: dir,
260 delta,
261 begin: min.x,
262 min_x: min.y,
263 max_x: max.y,
264 min_z: min.z,
265 max_z: max.z,
266 result_dir: Direction::West,
267 start: from,
268 });
269 } else if delta.x < -EPSILON {
270 dir = Self::clip_point(ClipPointOpts {
271 t,
272 approach_dir: dir,
273 delta,
274 begin: max.x,
275 min_x: min.y,
276 max_x: max.y,
277 min_z: min.z,
278 max_z: max.z,
279 result_dir: Direction::East,
280 start: from,
281 });
282 }
283
284 if delta.y > EPSILON {
285 dir = Self::clip_point(ClipPointOpts {
286 t,
287 approach_dir: dir,
288 delta: &Vec3 {
289 x: delta.y,
290 y: delta.z,
291 z: delta.x,
292 },
293 begin: min.y,
294 min_x: min.z,
295 max_x: max.z,
296 min_z: min.x,
297 max_z: max.x,
298 result_dir: Direction::Down,
299 start: &Vec3 {
300 x: from.y,
301 y: from.z,
302 z: from.x,
303 },
304 });
305 } else if delta.y < -EPSILON {
306 dir = Self::clip_point(ClipPointOpts {
307 t,
308 approach_dir: dir,
309 delta: &Vec3 {
310 x: delta.y,
311 y: delta.z,
312 z: delta.x,
313 },
314 begin: max.y,
315 min_x: min.z,
316 max_x: max.z,
317 min_z: min.x,
318 max_z: max.x,
319 result_dir: Direction::Up,
320 start: &Vec3 {
321 x: from.y,
322 y: from.z,
323 z: from.x,
324 },
325 });
326 }
327
328 if delta.z > EPSILON {
329 dir = Self::clip_point(ClipPointOpts {
330 t,
331 approach_dir: dir,
332 delta: &Vec3 {
333 x: delta.z,
334 y: delta.x,
335 z: delta.y,
336 },
337 begin: min.z,
338 min_x: min.x,
339 max_x: max.x,
340 min_z: min.y,
341 max_z: max.y,
342 result_dir: Direction::North,
343 start: &Vec3 {
344 x: from.z,
345 y: from.x,
346 z: from.y,
347 },
348 });
349 } else if delta.z < -EPSILON {
350 dir = Self::clip_point(ClipPointOpts {
351 t,
352 approach_dir: dir,
353 delta: &Vec3 {
354 x: delta.z,
355 y: delta.x,
356 z: delta.y,
357 },
358 begin: max.z,
359 min_x: min.x,
360 max_x: max.x,
361 min_z: min.y,
362 max_z: max.y,
363 result_dir: Direction::South,
364 start: &Vec3 {
365 x: from.z,
366 y: from.x,
367 z: from.y,
368 },
369 });
370 }
371
372 dir
373 }
374
375 fn clip_point(opts: ClipPointOpts) -> Option<Direction> {
376 let d = (opts.begin - opts.start.x) / opts.delta.x;
377 let e = opts.start.y + d * opts.delta.y;
378 let f = opts.start.z + d * opts.delta.z;
379 if 0.0 < d
380 && d < *opts.t
381 && opts.min_x - EPSILON < e
382 && e < opts.max_x + EPSILON
383 && opts.min_z - EPSILON < f
384 && f < opts.max_z + EPSILON
385 {
386 *opts.t = d;
387 Some(opts.result_dir)
388 } else {
389 opts.approach_dir
390 }
391 }
392
393 pub fn has_nan(&self) -> bool {
394 self.min.x.is_nan()
395 || self.min.y.is_nan()
396 || self.min.z.is_nan()
397 || self.max.x.is_nan()
398 || self.max.y.is_nan()
399 || self.max.z.is_nan()
400 }
401
402 pub fn get_center(&self) -> Vec3 {
403 Vec3::new(
404 (self.min.x + self.max.x) / 2.0,
405 (self.min.y + self.max.y) / 2.0,
406 (self.min.z + self.max.z) / 2.0,
407 )
408 }
409
410 pub fn of_size(center: Vec3, dx: f64, dy: f64, dz: f64) -> AABB {
411 AABB {
412 min: Vec3::new(
413 center.x - dx / 2.0,
414 center.y - dy / 2.0,
415 center.z - dz / 2.0,
416 ),
417 max: Vec3::new(
418 center.x + dx / 2.0,
419 center.y + dy / 2.0,
420 center.z + dz / 2.0,
421 ),
422 }
423 }
424
425 pub fn max(&self, axis: &Axis) -> f64 {
426 axis.choose(self.max.x, self.max.y, self.max.z)
427 }
428 pub fn min(&self, axis: &Axis) -> f64 {
429 axis.choose(self.min.x, self.min.y, self.min.z)
430 }
431
432 pub fn collided_along_vector(&self, vector: Vec3, boxes: &Vec<AABB>) -> bool {
433 let center = self.get_center();
434 let new_center = center + vector;
435
436 for aabb in boxes {
437 let inflated = aabb.inflate(
438 self.get_size(Axis::X) * 0.5,
439 self.get_size(Axis::Y) * 0.5,
440 self.get_size(Axis::Z) * 0.5,
441 );
442 if inflated.contains(&new_center) || inflated.contains(¢er) {
443 return true;
444 }
445
446 if inflated.clip(¢er, &new_center).is_some() {
447 return true;
448 }
449 }
450
451 false
452 }
453}
454
455impl BlockPos {
456 pub fn between_closed_aabb(aabb: &AABB) -> Vec<BlockPos> {
457 BlockPos::between_closed(BlockPos::from(aabb.min), BlockPos::from(aabb.max))
458 }
459
460 pub fn between_closed(min: BlockPos, max: BlockPos) -> Vec<BlockPos> {
461 assert!(min.x <= max.x);
462 assert!(min.y <= max.y);
463 assert!(min.z <= max.z);
464
465 let length_x = max.x - min.x + 1;
466 let length_y = max.y - min.y + 1;
467 let length_z = max.z - min.z + 1;
468 let volume = length_x * length_y * length_z;
469
470 let mut result = Vec::with_capacity(volume as usize);
471 for index in 0..volume {
472 let index_x = index % length_x;
473 let remaining_after_x = index / length_x;
474 let index_y = remaining_after_x % length_y;
475 let index_z = remaining_after_x / length_y;
476 result.push(BlockPos::new(
477 min.x + index_x,
478 min.y + index_y,
479 min.z + index_z,
480 ));
481 }
482
483 result
484 }
485}
486
487#[cfg(test)]
488mod tests {
489 use super::*;
490
491 #[test]
492 fn test_aabb_clip_iterable() {
493 assert_ne!(
494 AABB::clip_iterable(
495 &vec![AABB {
496 min: Vec3::new(0., 0., 0.),
497 max: Vec3::new(1., 1., 1.),
498 }],
499 &Vec3::new(-1., -1., -1.),
500 &Vec3::new(1., 1., 1.),
501 &BlockPos::new(0, 0, 0),
502 ),
503 None
504 );
505 }
506}