From 2c0db0ac1e08599d4a81702f764d4c45ae024f1b Mon Sep 17 00:00:00 2001 From: Dom Williams Date: Sat, 4 Nov 2023 13:02:21 +0200 Subject: [PATCH] Use proper accessibility checks for everything except search --- world/src/chunk/chunk.rs | 31 +++++-------- world/src/lib.rs | 2 +- world/src/navigationv2/accessible.rs | 20 +++++++++ world/src/navigationv2/mod.rs | 14 +++++- world/src/world.rs | 67 ++++++++++++++++------------ 5 files changed, 82 insertions(+), 52 deletions(-) diff --git a/world/src/chunk/chunk.rs b/world/src/chunk/chunk.rs index f3adad89..21d99f77 100644 --- a/world/src/chunk/chunk.rs +++ b/world/src/chunk/chunk.rs @@ -17,7 +17,10 @@ use crate::navigationv2::{ }; use crate::neighbour::NeighbourOffset; use crate::world::LoadNotifier; -use crate::{BlockOcclusion, Slab, SliceRange, World, WorldArea, WorldAreaV2, WorldContext}; +use crate::{ + does_entity_fit_in_area, BlockOcclusion, Slab, SliceRange, World, WorldArea, WorldAreaV2, + WorldContext, +}; use enumflags2::bitflags; use parking_lot::RwLock; use petgraph::visit::Walker; @@ -445,7 +448,7 @@ impl Chunk { } } - /// Searches downwards in vertical space for area z + /// Searches downwards in vertical space for area z. Only checks height requirement, not accessibility pub fn find_area_for_block_with_height( &self, block: BlockPosition, @@ -463,7 +466,7 @@ impl Chunk { let info = self .area_info(slab_idx, a) .unwrap_or_else(|| panic!("unknown area {a:?} in chunk {:?}", self.pos)); - if info.fits_requirement(requirement) && info.contains(slice_block) { + if info.height >= requirement.height && info.contains(slice_block) { return Some((a, info)); } } @@ -510,13 +513,11 @@ impl AreaInfo { } /// Point is relative to this area - pub fn random_point(&self, xy_blocks: (f32, f32), random: &mut dyn RngCore) -> (f32, f32) { - debug_assert!(self.fits_xy(xy_blocks.0.powi(2) + xy_blocks.1.powi(2))); + pub fn random_point(&self, random: &mut dyn RngCore) -> (f32, f32) { let ((xmin, ymin), (xmax, ymax)) = self.range; - let half_width = xy_blocks.0.min(xy_blocks.1) * 0.5; ( - random.gen_range(xmin as f32 + half_width, xmax as f32 - half_width + 1.0) - half_width, - random.gen_range(ymin as f32 + half_width, ymax as f32 - half_width + 1.0) - half_width, + random.gen_range(xmin as f32, xmax as f32 + 1.0), + random.gen_range(ymin as f32, ymax as f32 + 1.0), ) } @@ -526,12 +527,11 @@ impl AreaInfo { pub fn random_world_point( &self, - xy_blocks: (f32, f32), slice: GlobalSliceIndex, chunk: ChunkLocation, random: &mut dyn RngCore, ) -> WorldPoint { - let (x, y) = self.random_point(xy_blocks, random); + let (x, y) = self.random_point(random); // TODO new BlockPoint for BlockPosition but floats. this conversion is gross let block_pos = BlockPosition::new_unchecked(x as BlockCoord, y as BlockCoord, slice); @@ -540,17 +540,6 @@ impl AreaInfo { world_pos + (x.fract(), y.fract(), 0.0) } - pub fn fits_xy(&self, xy_diagonal_sqrd: f32) -> bool { - let (x, y) = self.size(); - let x = (x as u32).pow(2); - let y = (y as u32).pow(2); - xy_diagonal_sqrd < (x + y) as f32 - } - - pub fn fits_requirement(&self, req: NavRequirement) -> bool { - self.height >= req.height && self.fits_xy(req.xy_diagonal_sqrd()) - } - fn pos_to_world( area: crate::navigationv2::world_graph::WorldArea, (x, y): (BlockCoord, BlockCoord), diff --git a/world/src/lib.rs b/world/src/lib.rs index d18342d9..106fcbfd 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -15,7 +15,7 @@ pub use self::context::{ pub use self::mesh::BaseVertex; pub use self::navigation::{EdgeCost, NavigationError, SearchGoal, WorldArea, WorldPath}; pub use self::navigationv2::{ - accessible::AccessibilityCalculator, + accessible::{does_entity_fit_in_area, AccessibilityCalculator}, world_graph::{ OngoingPathSearchFuture, Path, SearchError, SearchStatus, WorldArea as WorldAreaV2, }, diff --git a/world/src/navigationv2/accessible.rs b/world/src/navigationv2/accessible.rs index 08ce3124..58a8a882 100644 --- a/world/src/navigationv2/accessible.rs +++ b/world/src/navigationv2/accessible.rs @@ -10,6 +10,25 @@ use crate::navigationv2::world_graph::WorldGraphNodeIndex; use crate::navigationv2::WorldArea; use crate::{NavRequirement, World, WorldContext}; +pub fn does_entity_fit_in_area( + agent_bounds: &WorldPointRange, + agent_req: NavRequirement, + world: &World, + agent_area: WorldArea, + filter_fn: impl Fn(WorldGraphNodeIndex) -> bool, + save_debug_files: bool, +) -> bool { + AccessibilityCalculator::with_graph( + agent_bounds, + agent_req, + world, + agent_area, + filter_fn, + save_debug_files, + ) + .process_fully_and_check() +} + #[cfg_attr(feature = "debug-accessibility", derive(serde::Serialize))] #[derive(Clone)] struct Rect { @@ -23,6 +42,7 @@ pub struct AccessibilityCalculator { #[cfg(feature = "debug-accessibility")] dbg: RefCell>, } + impl AccessibilityCalculator { pub fn with_graph( agent_bounds: &WorldPointRange, diff --git a/world/src/navigationv2/mod.rs b/world/src/navigationv2/mod.rs index 49dacc28..d4cea574 100644 --- a/world/src/navigationv2/mod.rs +++ b/world/src/navigationv2/mod.rs @@ -9,7 +9,7 @@ use petgraph::stable_graph::EdgeIndex; use misc::Itertools; use unit::world::{ BlockCoord, ChunkLocation, GlobalSliceIndex, LocalSliceIndex, SlabIndex, SliceIndex, - BLOCKS_PER_METRE, CHUNK_SIZE, SLAB_SIZE, + WorldPoint, WorldPointRange, BLOCKS_PER_METRE, BLOCKS_SCALE, CHUNK_SIZE, SLAB_SIZE, }; pub use world_graph::{PathExistsResult, WorldArea}; @@ -544,6 +544,18 @@ impl NavRequirement { pub fn xy_diagonal_sqrd(&self) -> f32 { (self.dims.0 * self.dims.0) + (self.dims.1 * self.dims.1) } + + /// In blocks around given centre + pub fn max_rotated_aabb(&self, centre: WorldPoint) -> WorldPointRange { + let (w, h) = self.dims; + let hw = w * 0.5; + let hh = h * 0.5; + let diag = ((hw * hw) + (hh * hh)).sqrt(); + WorldPointRange::with_inclusive_range( + centre + (-diag, -diag, 0.0), + centre + (diag, diag, self.height as f32 * BLOCKS_SCALE), + ) + } } impl DirectionalSlabNavEdge<'_> { diff --git a/world/src/world.rs b/world/src/world.rs index 3e71d5c2..28eea153 100644 --- a/world/src/world.rs +++ b/world/src/world.rs @@ -1,18 +1,15 @@ use std::collections::HashSet; use std::iter::once; - -use enumflags2::BitFlags; -use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; +use enumflags2::BitFlags; use tokio::sync::broadcast; -use tokio::sync::broadcast::error::SendError; use misc::derive_more::Constructor; use misc::*; use unit::world::{ - BlockCoord, BlockPosition, ChunkLocation, GlobalSliceIndex, LocalSliceIndex, SlabIndex, - SlabLocation, SliceBlock, SliceIndex, WorldPosition, WorldPositionRange, + BlockPosition, ChunkLocation, GlobalSliceIndex, LocalSliceIndex, SlabLocation, SliceBlock, + SliceIndex, WorldPointRange, WorldPosition, WorldPositionRange, }; use unit::world::{WorldPoint, CHUNK_SIZE}; @@ -27,15 +24,16 @@ use crate::chunk::{ use crate::context::WorldContext; use crate::loader::{SlabTerrainUpdate, SlabVerticalSpace}; use crate::navigation::{ - AreaGraph, AreaGraphSearchContext, AreaNavEdge, AreaPath, BlockGraph, BlockGraphSearchContext, - BlockPath, ExploreResult, NavigationError, SearchGoal, WorldArea, WorldPath, WorldPathNode, + AreaGraph, AreaNavEdge, AreaPath, BlockPath, NavigationError, SearchGoal, WorldArea, WorldPath, + WorldPathNode, }; use crate::navigationv2::world_graph::WorldGraph; -use crate::navigationv2::{as_border_area, ChunkArea, NavRequirement, SlabArea, SlabNavEdge}; -use crate::neighbour::{NeighbourOffset, WorldNeighbours}; +use crate::navigationv2::{ChunkArea, NavRequirement}; +use crate::neighbour::WorldNeighbours; use crate::occlusion::NeighbourOpacity; use crate::{ - BlockOcclusion, BlockType, OcclusionFace, SearchError, Slab, SliceRange, WorldAreaV2, WorldRef, + does_entity_fit_in_area, AccessibilityCalculator, BlockOcclusion, BlockType, OcclusionFace, + Slab, SliceRange, WorldAreaV2, WorldRef, }; /// All mutable world changes must go through `loader.apply_terrain_updates` @@ -327,7 +325,9 @@ impl World { let ai = self .lookup_area_info(a) .unwrap_or_else(|| panic!("missing area info {:?}", a)); - (ai.fits_requirement(req)).then_some((a, ai)) + unreachable!(); + Some((a, ai)) + // (ai.fits_requirement(req)).then_some((a, ai)) }) .choose(&mut random) { @@ -632,13 +632,23 @@ impl World { let chunk = self.all_chunks().choose(random).unwrap(); // never empty // choose random area - let (a, ai) = chunk - .iter_areas_with_info() - .filter(|(a, ai)| ai.fits_requirement(requirement)) - .choose(random)?; + let (a, ai) = chunk.iter_areas_with_info().choose(random)?; // take random point in this area - Some(ai.random_world_point(requirement.dims, a.slice(), chunk.pos(), random)) + let centre = ai.random_world_point(a.slice(), chunk.pos(), random); + + // ensure adjacent areas can hold us + let bounds_here = requirement.max_rotated_aabb(centre); + + does_entity_fit_in_area( + &bounds_here, + requirement, + self, + a.to_world_area(chunk.pos()), + |_| true, + false, + ) + .then_some(centre) }) } @@ -1166,14 +1176,15 @@ impl AreaLookup { /// Helpers to create a world synchronously for tests and benchmarks pub mod helpers { - use color::Color; - use futures::future::BoxFuture; - use futures::FutureExt; use std::sync::Arc; use std::time::Duration; + use futures::future::BoxFuture; + use futures::FutureExt; + + use color::Color; use misc::Itertools; - use unit::world::{ChunkLocation, SlabLocation, WorldPoint}; + use unit::world::{ChunkLocation, SlabLocation}; use crate::block::{Block, BlockDurability, BlockOpacity}; use crate::chunk::slab::SlabGridImpl; @@ -1330,28 +1341,26 @@ pub mod helpers { //noinspection DuplicatedCode #[cfg(test)] mod tests { - use std::convert::TryFrom; use std::time::Duration; - use misc::{logging, thread_rng, Itertools, Rng, SeedableRng, StdRng}; - use unit::world::{all_slabs_in_range, SliceBlock, SliceIndex, WorldPoint, CHUNK_SIZE}; + use misc::{thread_rng, Itertools, Rng, SeedableRng, StdRng}; + use unit::world::{all_slabs_in_range, SliceIndex, WorldPoint, CHUNK_SIZE}; use unit::world::{ - BlockPosition, ChunkLocation, GlobalSliceIndex, SlabLocation, WorldPosition, - WorldPositionRange, SLAB_SIZE, + BlockPosition, ChunkLocation, GlobalSliceIndex, SlabLocation, WorldPositionRange, SLAB_SIZE, }; use crate::chunk::ChunkBuilder; use crate::helpers::{DummyBlockType, DummyWorldContext}; - use crate::loader::{AsyncWorkerPool, MemoryTerrainSource, WorldLoader, WorldTerrainUpdate}; + use crate::loader::{AsyncWorkerPool, WorldLoader, WorldTerrainUpdate}; use crate::navigation::EdgeCost; use crate::navigationv2::NavRequirement; - use crate::occlusion::{NeighbourOpacity, VertexOcclusion}; + use crate::occlusion::VertexOcclusion; use crate::presets::from_preset; use crate::world::helpers::{ apply_updates, loader_from_chunks_blocking, world_from_chunks_blocking, }; use crate::world::ContiguousChunkIterator; - use crate::{presets, BlockType, SearchGoal, World, WorldContext, WorldRef}; + use crate::{presets, BlockType, SearchGoal, World, WorldContext}; #[test] fn world_context() {