mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 17:21:49 -10:00
Keep no face shapes from the load-time passes over the collision and support tables
analyse_shape and the cached support flags computed every shape's six faces through face_shape, which caches them on the shape for good: about 39 MB that nothing reads again. They now use face_shape_transient; runtime queries still cache. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
c48d6773c5
commit
c8ee23f2b8
3 files changed
+62
-9
No files matched your search
@@ -16,6 +16,24 @@ impl VoxelShape {
|
||||
.get_or_init(|| self.calculate_face(direction))
|
||||
.clone()
|
||||
}
|
||||
/// [`Self::face_shape`] that keeps no face it had to calculate on this
|
||||
/// shape: for a one-off pass over a whole table at load, whose faces
|
||||
/// nothing asks for again (kept, they held tens of MB).
|
||||
pub fn face_shape_transient(&self, direction: Direction) -> Result<Self, ShapeError> {
|
||||
let (axis, _) = face_axis(direction);
|
||||
if self.is_empty() || self.cube_like_along(axis) {
|
||||
return Ok(self.clone());
|
||||
}
|
||||
match self
|
||||
.identity
|
||||
.faces
|
||||
.get()
|
||||
.and_then(|faces| faces[direction as usize].get())
|
||||
{
|
||||
Some(face) => face.clone(),
|
||||
None => self.calculate_face(direction),
|
||||
}
|
||||
}
|
||||
fn cube_like_along(&self, axis: Axis) -> bool {
|
||||
let coords = self.coordinates(axis);
|
||||
coords.len() == 2
|
||||
@@ -120,4 +138,31 @@ mod tests {
|
||||
assert!(weak.upgrade().is_none());
|
||||
assert!(!face.is_empty());
|
||||
}
|
||||
|
||||
/// A transient face is the cached one's geometry, keeps nothing, and
|
||||
/// returns the cached face once there is one.
|
||||
#[test]
|
||||
fn transient_faces_are_not_kept() {
|
||||
let shape = VoxelShape::from_cube(
|
||||
[2, 2, 2],
|
||||
&[true, false, false, false, false, false, false, false],
|
||||
)
|
||||
.unwrap();
|
||||
for direction in Direction::ALL {
|
||||
let kept = |shape: &VoxelShape| {
|
||||
shape
|
||||
.identity
|
||||
.faces
|
||||
.get()
|
||||
.is_some_and(|faces| faces[direction as usize].get().is_some())
|
||||
};
|
||||
let transient = shape.face_shape_transient(direction).unwrap();
|
||||
assert!(!kept(&shape), "{direction:?}");
|
||||
let cached = shape.face_shape(direction).unwrap();
|
||||
assert!(kept(&shape));
|
||||
assert_eq!(transient.boxes(), cached.boxes(), "{direction:?}");
|
||||
let again = shape.face_shape_transient(direction).unwrap();
|
||||
assert!(again.same_identity(&cached));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,10 @@ impl SupportType {
|
||||
shape: &VoxelShape,
|
||||
direction: Direction,
|
||||
) -> Result<bool, ShapeError> {
|
||||
let face = shape.face_shape(direction)?;
|
||||
self.supported_by(&shape.face_shape(direction)?)
|
||||
}
|
||||
/// [`Self::is_supporting`] given the shape's face in that direction.
|
||||
fn supported_by(self, face: &VoxelShape) -> Result<bool, ShapeError> {
|
||||
let (required, operation) = match self {
|
||||
Self::Full => (VoxelShape::block(), ShapeOp::XOR),
|
||||
Self::Center => {
|
||||
@@ -92,13 +95,17 @@ impl<'r> CachedBlockSupportRegistry<'r> {
|
||||
*flags
|
||||
} else {
|
||||
let mut flags = [0; 3];
|
||||
for (i, support) in [SupportType::Full, SupportType::Center, SupportType::Rigid]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
for direction in Direction::ALL {
|
||||
for direction in Direction::ALL {
|
||||
// Not kept on the shape: only these flags are.
|
||||
let face = shape
|
||||
.face_shape_transient(direction)
|
||||
.map_err(|e| invalid(e.to_string()))?;
|
||||
for (i, support) in [SupportType::Full, SupportType::Center, SupportType::Rigid]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
if support
|
||||
.is_supporting(shape, direction)
|
||||
.supported_by(&face)
|
||||
.map_err(|e| invalid(e.to_string()))?
|
||||
{
|
||||
flags[i] |= 1 << direction as u8;
|
||||
|
||||
@@ -116,8 +116,9 @@ pub(crate) fn analyse_shape(shape: &VoxelShape) -> io::Result<ShapeInfo> {
|
||||
let err = |e: horizon_core::geometry::ShapeError| invalid(e.to_string());
|
||||
let mut info = ShapeInfo::default();
|
||||
let block = VoxelShape::block();
|
||||
// The faces are not kept on the shape: only these facts are.
|
||||
for direction in Direction::ALL {
|
||||
let face = shape.face_shape(direction).map_err(err)?;
|
||||
let face = shape.face_shape_transient(direction).map_err(err)?;
|
||||
if !block.join_is_not_empty(&face, ShapeOp::XOR).map_err(err)? {
|
||||
info.full_faces |= 1 << direction as u8;
|
||||
}
|
||||
@@ -125,7 +126,7 @@ pub(crate) fn analyse_shape(shape: &VoxelShape) -> io::Result<ShapeInfo> {
|
||||
info.nonempty_faces |= 1 << direction as u8;
|
||||
}
|
||||
}
|
||||
let above = shape.face_shape(Direction::Down).map_err(err)?;
|
||||
let above = shape.face_shape_transient(Direction::Down).map_err(err)?;
|
||||
let px = |v: f64| v / 16.0;
|
||||
let tests = [
|
||||
(7.0, 7.0, 9.0, 9.0),
|
||||
|
||||
Reference in new issue
Block a user