aboutsummaryrefslogtreecommitdiff
path: root/azalea-physics/src/collision/world_collisions.rs
blob: aa55150e67421ae496cb8307dbf1799b68e6f715 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use super::Shapes;
use crate::collision::{BlockWithShape, VoxelShape, AABB};
use azalea_block::BlockState;
use azalea_core::{ChunkPos, ChunkSectionPos, Cursor3d, CursorIterationType, EPSILON};
use azalea_world::{Chunk, Instance};
use parking_lot::RwLock;
use std::sync::Arc;

pub fn get_block_collisions(world: &Instance, aabb: AABB) -> BlockCollisions<'_> {
    BlockCollisions::new(world, aabb)
}

pub struct BlockCollisions<'a> {
    pub world: &'a Instance,
    pub aabb: AABB,
    pub entity_shape: VoxelShape,
    pub cursor: Cursor3d,
    pub only_suffocating_blocks: bool,
}

impl<'a> BlockCollisions<'a> {
    pub fn new(world: &'a Instance, aabb: AABB) -> Self {
        let origin_x = (aabb.min_x - EPSILON).floor() as i32 - 1;
        let origin_y = (aabb.min_y - EPSILON).floor() as i32 - 1;
        let origin_z = (aabb.min_z - EPSILON).floor() as i32 - 1;

        let end_x = (aabb.max_x + EPSILON).floor() as i32 + 1;
        let end_y = (aabb.max_y + EPSILON).floor() as i32 + 1;
        let end_z = (aabb.max_z + EPSILON).floor() as i32 + 1;

        let cursor = Cursor3d::new(origin_x, origin_y, origin_z, end_x, end_y, end_z);

        Self {
            world,
            aabb,
            entity_shape: VoxelShape::from(aabb),
            cursor,
            only_suffocating_blocks: false,
        }
    }

    fn get_chunk(&self, block_x: i32, block_z: i32) -> Option<Arc<RwLock<Chunk>>> {
        let chunk_x = ChunkSectionPos::block_to_section_coord(block_x);
        let chunk_z = ChunkSectionPos::block_to_section_coord(block_z);
        let chunk_pos = ChunkPos::new(chunk_x, chunk_z);

        // TODO: minecraft caches chunk here
        // int chunkX = SectionPos.blockToSectionCoord(blockX);
        // int chunkZ = SectionPos.blockToSectionCoord(blockZ);
        // long chunkPosLong = ChunkPos.asLong(chunkX, chunkZ);
        // if (this.cachedBlockGetter != null && this.cachedBlockGetterPos == var5) {
        //    return this.cachedBlockGetter;
        // } else {
        //    BlockGetter var7 = this.collisionGetter.getChunkForCollisions(chunkX,
        // chunkZ);    this.cachedBlockGetter = var7;
        //    this.cachedBlockGetterPos = chunkPosLong;
        //    return var7;
        // }

        self.world.chunks.get(&chunk_pos)
    }
}

impl<'a> Iterator for BlockCollisions<'a> {
    type Item = VoxelShape;

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(item) = self.cursor.next() {
            if item.iteration_type == CursorIterationType::Corner {
                continue;
            }

            let chunk = self.get_chunk(item.pos.x, item.pos.z);
            let Some(chunk) = chunk else {
                continue
            };

            let pos = item.pos;
            let block_state: BlockState = chunk
                .read()
                .get(&(&pos).into(), self.world.chunks.min_y)
                .unwrap_or(BlockState::AIR);

            // TODO: continue if self.only_suffocating_blocks and the block is not
            // suffocating

            let block_shape = block_state.shape();

            // if it's a full block do a faster collision check
            if block_shape == &crate::collision::block_shape() {
                if !self.aabb.intersects_aabb(&AABB {
                    min_x: item.pos.x as f64,
                    min_y: item.pos.y as f64,
                    min_z: item.pos.z as f64,
                    max_x: (item.pos.x + 1) as f64,
                    max_y: (item.pos.y + 1) as f64,
                    max_z: (item.pos.z + 1) as f64,
                }) {
                    continue;
                }

                return Some(block_shape.move_relative(
                    item.pos.x as f64,
                    item.pos.y as f64,
                    item.pos.z as f64,
                ));
            }

            let block_shape =
                block_shape.move_relative(item.pos.x as f64, item.pos.y as f64, item.pos.z as f64);
            // if the entity shape and block shape don't collide, continue
            if !Shapes::matches_anywhere(&block_shape, &self.entity_shape, |a, b| a && b) {
                continue;
            }

            return Some(block_shape);
        }

        None
    }
}