azalea_physics/collision/
discrete_voxel_shape.rs

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