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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
use std::sync::Arc;
use azalea_block::BlockState;
use azalea_core::{
cursor3d::{Cursor3d, CursorIterationType},
math::EPSILON,
position::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos, ChunkSectionPos},
};
use azalea_world::{Chunk, Instance};
use parking_lot::RwLock;
use super::{Shapes, BLOCK_SHAPE};
use crate::collision::{BlockWithShape, VoxelShape, AABB};
pub fn get_block_collisions(world: &Instance, aabb: AABB) -> Vec<VoxelShape> {
let mut state = BlockCollisionsState::new(world, aabb);
let mut block_collisions = Vec::new();
let initial_chunk_pos = ChunkPos::from(state.cursor.origin());
let initial_chunk = world.chunks.get(&initial_chunk_pos);
let initial_chunk = initial_chunk.as_deref().map(RwLock::read);
while let Some(item) = state.cursor.next() {
if item.iteration_type == CursorIterationType::Corner {
continue;
}
let item_chunk_pos = ChunkPos::from(item.pos);
let block_state: BlockState = if item_chunk_pos == initial_chunk_pos {
if let Some(initial_chunk) = &initial_chunk {
initial_chunk
.get(&ChunkBlockPos::from(item.pos), state.world.chunks.min_y)
.unwrap_or(BlockState::AIR)
} else {
BlockState::AIR
}
} else {
state.get_block_state(item.pos)
};
if block_state.is_air() {
// fast path since we can't collide with air
continue;
}
// TODO: continue if self.only_suffocating_blocks and the block is not
// suffocating
// if it's a full block do a faster collision check
if block_state.is_collision_shape_full() {
if !state.aabb.intersects_aabb(&AABB {
min: item.pos.to_vec3_floored(),
max: (item.pos + 1).to_vec3_floored(),
}) {
continue;
}
block_collisions.push(BLOCK_SHAPE.move_relative(item.pos.to_vec3_floored()));
continue;
}
let block_shape = state.get_block_shape(block_state);
let block_shape = block_shape.move_relative(item.pos.to_vec3_floored());
// if the entity shape and block shape don't collide, continue
if !Shapes::matches_anywhere(&block_shape, &state.entity_shape, |a, b| a && b) {
continue;
}
block_collisions.push(block_shape);
}
block_collisions
}
pub struct BlockCollisionsState<'a> {
pub world: &'a Instance,
pub aabb: AABB,
pub entity_shape: VoxelShape,
pub cursor: Cursor3d,
cached_sections: Vec<(ChunkSectionPos, azalea_world::Section)>,
cached_block_shapes: Vec<(BlockState, &'static VoxelShape)>,
}
impl<'a> BlockCollisionsState<'a> {
pub fn new(world: &'a Instance, aabb: AABB) -> Self {
let origin = BlockPos {
x: (aabb.min.x - EPSILON).floor() as i32 - 1,
y: (aabb.min.y - EPSILON).floor() as i32 - 1,
z: (aabb.min.z - EPSILON).floor() as i32 - 1,
};
let end = BlockPos {
x: (aabb.max.x + EPSILON).floor() as i32 + 1,
y: (aabb.max.y + EPSILON).floor() as i32 + 1,
z: (aabb.max.z + EPSILON).floor() as i32 + 1,
};
let cursor = Cursor3d::new(origin, end);
Self {
world,
aabb,
entity_shape: VoxelShape::from(aabb),
cursor,
cached_sections: Vec::new(),
cached_block_shapes: Vec::new(),
}
}
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)
}
fn get_block_state(&mut self, block_pos: BlockPos) -> BlockState {
if block_pos.y < self.world.chunks.min_y {
// below the world
return BlockState::AIR;
}
let section_pos = ChunkSectionPos::from(block_pos);
let section_block_pos = ChunkSectionBlockPos::from(block_pos);
for (cached_section_pos, cached_section) in &self.cached_sections {
if section_pos == *cached_section_pos {
return cached_section.get(section_block_pos);
}
}
let chunk = self.get_chunk(block_pos.x, block_pos.z);
let Some(chunk) = chunk else {
return BlockState::AIR;
};
let chunk = chunk.read();
let sections = &chunk.sections;
let section_index =
azalea_world::chunk_storage::section_index(block_pos.y, self.world.chunks.min_y)
as usize;
let Some(section) = sections.get(section_index) else {
return BlockState::AIR;
};
self.cached_sections.push((section_pos, section.clone()));
// println!("chunk section palette: {:?}", section.states.palette);
// println!("chunk section data: {:?}", section.states.storage.data);
// println!("biome length: {}", section.biomes.storage.data.len());
section.get(section_block_pos)
}
fn get_block_shape(&mut self, block_state: BlockState) -> &'static VoxelShape {
for (cached_block_state, cached_shape) in &self.cached_block_shapes {
if block_state == *cached_block_state {
return cached_shape;
}
}
let shape = block_state.collision_shape();
self.cached_block_shapes.push((block_state, shape));
shape
}
}
|