azalea_physics/collision/
discrete_voxel_shape.rs

1use std::cmp;
2
3use azalea_core::{
4    bitset::BitSet,
5    direction::{Axis, AxisCycle},
6    position::Vec3i,
7};
8
9use super::mergers::IndexMerger;
10
11pub trait IntLineConsumer = FnMut(u32, u32, u32, u32, u32, u32);
12
13#[derive(Clone, PartialEq, Eq, Debug)]
14pub enum DiscreteVoxelShape {
15    BitSet(BitSetDiscreteVoxelShape),
16}
17
18impl DiscreteVoxelShape {
19    #[inline]
20    pub fn size(&self, axis: Axis) -> u32 {
21        match self {
22            DiscreteVoxelShape::BitSet(shape) => shape.size(axis),
23        }
24    }
25
26    pub fn first_full(&self, axis: Axis) -> i32 {
27        match self {
28            DiscreteVoxelShape::BitSet(shape) => shape.first_full(axis),
29        }
30    }
31
32    pub fn last_full(&self, axis: Axis) -> i32 {
33        match self {
34            DiscreteVoxelShape::BitSet(shape) => shape.last_full(axis),
35        }
36    }
37
38    pub fn is_empty(&self) -> bool {
39        if self.first_full(Axis::X) >= self.last_full(Axis::X) {
40            return true;
41        }
42        if self.first_full(Axis::Y) >= self.last_full(Axis::Y) {
43            return true;
44        }
45        if self.first_full(Axis::Z) >= self.last_full(Axis::Z) {
46            return true;
47        }
48        false
49    }
50
51    pub fn is_full_wide(&self, pos: Vec3i) -> bool {
52        if pos.x < 0 || pos.y < 0 || pos.z < 0 {
53            return false;
54        }
55        let (x, y, z) = (pos.x as u32, pos.y as u32, pos.z as u32);
56
57        (x < self.size(Axis::X) && y < self.size(Axis::Y) && z < self.size(Axis::Z))
58            && (self.is_full(x, y, z))
59    }
60
61    pub fn is_full_wide_axis_cycle(&self, axis_cycle: AxisCycle, pos: Vec3i) -> bool {
62        self.is_full_wide(Vec3i {
63            x: axis_cycle.cycle_xyz(pos, Axis::X),
64            y: axis_cycle.cycle_xyz(pos, Axis::Y),
65            z: axis_cycle.cycle_xyz(pos, Axis::Z),
66        })
67    }
68
69    pub fn is_full(&self, x: u32, y: u32, z: u32) -> bool {
70        match self {
71            DiscreteVoxelShape::BitSet(shape) => shape.is_full(x, y, z),
72        }
73    }
74
75    pub fn for_all_boxes(&self, consumer: impl IntLineConsumer, swap: bool) {
76        BitSetDiscreteVoxelShape::for_all_boxes(self, consumer, swap);
77    }
78}
79
80#[derive(Default, Clone, PartialEq, Eq, Debug)]
81pub struct BitSetDiscreteVoxelShape {
82    x_size: u32,
83    y_size: u32,
84    z_size: u32,
85
86    storage: BitSet,
87    min: Vec3i,
88    max: Vec3i,
89}
90
91impl BitSetDiscreteVoxelShape {
92    pub fn new(x_min: u32, y_min: u32, z_min: u32) -> Self {
93        BitSetDiscreteVoxelShape {
94            x_size: x_min,
95            y_size: y_min,
96            z_size: z_min,
97
98            storage: BitSet::new((x_min * y_min * z_min).try_into().unwrap()),
99            min: Vec3i {
100                x: z_min.try_into().unwrap(),
101                y: z_min.try_into().unwrap(),
102                z: z_min.try_into().unwrap(),
103            },
104            max: Vec3i::ZERO,
105        }
106    }
107
108    // yeah don't really feel like fixing this one
109    #[allow(clippy::too_many_arguments)]
110    pub fn with_filled_bounds(
111        x_size: u32,
112        y_size: u32,
113        z_size: u32,
114        min: Vec3i,
115        max: Vec3i,
116    ) -> Self {
117        let mut shape = BitSetDiscreteVoxelShape::new(x_size, y_size, z_size);
118        shape.min = min;
119        shape.max = max;
120
121        for x in min.x..max.x {
122            for y in min.y..max.y {
123                for z in min.z..max.z {
124                    shape.fill_update_bounds(
125                        x.try_into().unwrap(),
126                        y.try_into().unwrap(),
127                        z.try_into().unwrap(),
128                        false,
129                    );
130                }
131            }
132        }
133
134        shape
135    }
136
137    fn fill_update_bounds(&mut self, x: u32, y: u32, z: u32, update: bool) {
138        self.storage.set(self.get_index(x, y, z));
139        if update {
140            let candidate_min = Vec3i::new(x as i32, y as i32, z as i32);
141            self.min = self.min.min(candidate_min);
142            let candidate_max = Vec3i::new((x + 1) as i32, (y + 1) as i32, (z + 1) as i32);
143            self.max = self.max.max(candidate_max);
144        }
145    }
146
147    pub fn fill(&mut self, x: u32, y: u32, z: u32) {
148        self.fill_update_bounds(x, y, z, true);
149    }
150
151    fn get_index_from_size(x: u32, y: u32, z: u32, y_size: u32, z_size: u32) -> usize {
152        ((x * y_size + y) * z_size + z) as usize
153    }
154    fn get_index(&self, x: u32, y: u32, z: u32) -> usize {
155        Self::get_index_from_size(x, y, z, self.y_size, self.z_size)
156    }
157
158    pub fn join(
159        var0: &DiscreteVoxelShape,
160        var1: &DiscreteVoxelShape,
161        var2: &IndexMerger,
162        var3: &IndexMerger,
163        var4: &IndexMerger,
164        op: impl Fn(bool, bool) -> bool,
165    ) -> Self {
166        let mut var6 = BitSetDiscreteVoxelShape::new(
167            (var2.size() - 1) as u32,
168            (var3.size() - 1) as u32,
169            (var4.size() - 1) as u32,
170        );
171        let mut var7: [i32; 6] = [
172            2147483647,
173            2147483647,
174            2147483647,
175            -2147483648,
176            -2147483648,
177            -2147483648,
178        ];
179        var2.for_merged_indexes(|x1: i32, x2: i32, x3: i32| {
180            let mut var10 = [false];
181            var3.for_merged_indexes(|y1: i32, y2: i32, y3: i32| {
182                let mut var13 = [false];
183                var4.for_merged_indexes(|z1: i32, z2: i32, z3: i32| {
184                    if op(
185                        var0.is_full_wide(Vec3i::new(x1, y1, z1)),
186                        var1.is_full_wide(Vec3i::new(x2, y2, z2)),
187                    ) {
188                        var6.storage.set(var6.get_index(
189                            x3.try_into().unwrap(),
190                            y3.try_into().unwrap(),
191                            z3.try_into().unwrap(),
192                        ));
193                        var7[2] = cmp::min(var7[2], z3);
194                        var7[5] = cmp::max(var7[5], z3);
195                        var13[0] = true;
196                    }
197
198                    true
199                });
200                if var13[0] {
201                    var7[1] = cmp::min(var7[1], y3);
202                    var7[4] = cmp::max(var7[4], y3);
203                    var10[0] = true;
204                }
205
206                true
207            });
208            if var10[0] {
209                var7[0] = cmp::min(var7[0], x3);
210                var7[3] = cmp::max(var7[3], x3);
211            }
212
213            true
214        });
215        var6.min.x = var7[0];
216        var6.min.y = var7[1];
217        var6.min.z = var7[2];
218        var6.max.x = var7[3] + 1;
219        var6.max.y = var7[4] + 1;
220        var6.max.z = var7[5] + 1;
221        var6
222    }
223
224    pub fn for_all_boxes(
225        var0: &DiscreteVoxelShape,
226        mut consumer: impl IntLineConsumer,
227        var2: bool,
228    ) {
229        let mut var3 = BitSetDiscreteVoxelShape::from(var0);
230        for y in 0..var3.y_size {
231            for x in 0..var3.x_size {
232                let mut var6 = None;
233                for z in 0..=var3.z_size {
234                    if var3.is_full_wide(x, y, z) {
235                        if var2 {
236                            if var6.is_none() {
237                                var6 = Some(z);
238                            }
239                        } else {
240                            consumer(x, y, z, x + 1, y + 1, z + 1);
241                        }
242                    } else if var6.is_some() {
243                        let mut var8 = x;
244                        let mut var9 = y;
245                        var3.clear_z_strip(var6.unwrap(), z, x, y);
246                        while var3.is_z_strip_full(var6.unwrap(), z, var8 + 1, y) {
247                            var3.clear_z_strip(var6.unwrap(), z, var8 + 1, y);
248                            var8 += 1;
249                        }
250                        while var3.is_xz_rectangle_full(x, var8 + 1, var6.unwrap(), z, var9 + 1) {
251                            for var10 in x..=var8 {
252                                var3.clear_z_strip(var6.unwrap(), z, var10, var9 + 1);
253                            }
254                            var9 += 1;
255                        }
256                        consumer(x, y, var6.unwrap(), var8 + 1, var9 + 1, z);
257                        var6 = None;
258                    }
259                }
260            }
261        }
262    }
263
264    fn is_z_strip_full(&self, var1: u32, var2: u32, var3: u32, var4: u32) -> bool {
265        if var3 < self.x_size && var4 < self.y_size {
266            self.storage
267                .next_clear_bit(self.get_index(var3, var4, var1))
268                >= self.get_index(var3, var4, var2)
269        } else {
270            false
271        }
272    }
273
274    fn is_xz_rectangle_full(&self, var1: u32, var2: u32, var3: u32, var4: u32, var5: u32) -> bool {
275        for var6 in var1..var2 {
276            if !self.is_z_strip_full(var3, var4, var6, var5) {
277                return false;
278            }
279        }
280        true
281    }
282
283    fn clear_z_strip(&mut self, var1: u32, var2: u32, var3: u32, var4: u32) {
284        self.storage
285            .clear(self.get_index(var3, var4, var1)..self.get_index(var3, var4, var2));
286    }
287}
288
289impl BitSetDiscreteVoxelShape {
290    #[inline]
291    fn size(&self, axis: Axis) -> u32 {
292        axis.choose(self.x_size, self.y_size, self.z_size)
293    }
294
295    fn first_full(&self, axis: Axis) -> i32 {
296        axis.choose(self.min.x, self.min.y, self.min.z)
297    }
298
299    fn last_full(&self, axis: Axis) -> i32 {
300        axis.choose(self.max.x, self.max.y, self.max.z)
301    }
302
303    fn is_full(&self, x: u32, y: u32, z: u32) -> bool {
304        self.storage.get(self.get_index(x, y, z)).unwrap_or(false)
305    }
306
307    fn is_full_wide(&self, x: u32, y: u32, z: u32) -> bool {
308        (x < self.size(Axis::X) && y < self.size(Axis::Y) && z < self.size(Axis::Z))
309            && (self.is_full(x, y, z))
310    }
311}
312
313impl From<&DiscreteVoxelShape> for BitSetDiscreteVoxelShape {
314    fn from(shape: &DiscreteVoxelShape) -> Self {
315        let x_size = shape.size(Axis::X);
316        let y_size = shape.size(Axis::Y);
317        let z_size = shape.size(Axis::Z);
318        let mut storage;
319        // more things could be added to DiscreteVoxelShape in the future
320        #[allow(irrefutable_let_patterns)]
321        if let DiscreteVoxelShape::BitSet(shape) = shape {
322            storage = shape.storage.clone();
323        } else {
324            storage = BitSet::new((x_size * y_size * z_size) as usize);
325            for x in 0..x_size {
326                for y in 0..y_size {
327                    for z in 0..z_size {
328                        if shape.is_full(x, y, z) {
329                            storage.set(Self::get_index_from_size(x, y, z, y_size, z_size));
330                        }
331                    }
332                }
333            }
334        }
335
336        Self {
337            x_size,
338            y_size,
339            z_size,
340            storage,
341            min: Vec3i {
342                x: shape.first_full(Axis::X),
343                y: shape.first_full(Axis::Y),
344                z: shape.first_full(Axis::Z),
345            },
346            max: Vec3i {
347                x: shape.last_full(Axis::X),
348                y: shape.last_full(Axis::Y),
349                z: shape.last_full(Axis::Z),
350            },
351        }
352    }
353}