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