Skip to content

Commit

Permalink
Move raycast functions to main VoxelWorld system param
Browse files Browse the repository at this point in the history
  • Loading branch information
splashdust committed Apr 4, 2024
1 parent fc66f9b commit 262b124
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 72 deletions.
2 changes: 1 addition & 1 deletion examples/ray_cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ fn create_voxel_scene(mut voxel_world: VoxelWorld<MyMainWorld>) {
}

fn update_cursor_cube(
voxel_world_raycast: VoxelWorldRaycast<MyMainWorld>,
voxel_world_raycast: VoxelWorld<MyMainWorld>,
camera_info: Query<(&Camera, &GlobalTransform), With<VoxelWorldCamera>>,
mut cursor_evr: EventReader<CursorMoved>,
mut cursor_cube: Query<(&mut Transform, &mut CursorCube)>,
Expand Down
4 changes: 1 addition & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ pub mod prelude {
pub use crate::plugin::VoxelWorldPlugin;
pub use crate::voxel::WorldVoxel;
pub use crate::voxel_world::{ChunkWillDespawn, ChunkWillRemesh, ChunkWillSpawn};
pub use crate::voxel_world::{
VoxelRaycastResult, VoxelWorld, VoxelWorldCamera, VoxelWorldRaycast,
};
pub use crate::voxel_world::{VoxelRaycastResult, VoxelWorld, VoxelWorldCamera};
}

pub mod rendering {
Expand Down
45 changes: 21 additions & 24 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,30 +219,27 @@ fn raycast_finds_voxel() {

app.update();

app.add_systems(
Update,
move |voxel_world_raycast: VoxelWorldRaycast<DefaultWorld>| {
let test_voxel = crate::voxel::WorldVoxel::Solid(1);

let ray = Ray3d {
origin: Vec3::new(0.5, 0.5, 70.0),
direction: -Direction3d::Z,
};

let Some(result) = voxel_world_raycast.raycast(ray, &|(_pos, _vox)| true) else {
panic!("No voxel found")
};

assert_eq!(
result,
VoxelRaycastResult {
position: Vec3::ZERO,
normal: Vec3::new(0.0, 0.0, 1.0),
voxel: test_voxel
}
)
},
);
app.add_systems(Update, move |voxel_world: VoxelWorld<DefaultWorld>| {
let test_voxel = crate::voxel::WorldVoxel::Solid(1);

let ray = Ray3d {
origin: Vec3::new(0.5, 0.5, 70.0),
direction: -Direction3d::Z,
};

let Some(result) = voxel_world.raycast(ray, &|(_pos, _vox)| true) else {
panic!("No voxel found")
};

assert_eq!(
result,
VoxelRaycastResult {
position: Vec3::ZERO,
normal: Vec3::new(0.0, 0.0, 1.0),
voxel: test_voxel
}
)
});

app.update();
}
79 changes: 35 additions & 44 deletions src/voxel_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,46 @@ pub struct ChunkWillRemesh {
pub entity: Entity,
}

pub trait FilterFn {
fn call(&self, input: (Vec3, WorldVoxel)) -> bool;
}

impl<F: Fn((Vec3, WorldVoxel)) -> bool> FilterFn for F {
fn call(&self, input: (Vec3, WorldVoxel)) -> bool {
self(input)
}
}

pub type RaycastFn = dyn Fn(Ray3d, &dyn FilterFn) -> Option<VoxelRaycastResult> + Send + Sync;

#[derive(Default, Debug, PartialEq, Clone)]
pub struct VoxelRaycastResult {
pub position: Vec3,
pub normal: Vec3,
pub voxel: WorldVoxel,
}

impl VoxelRaycastResult {
/// Get the voxel position of the raycast result
pub fn voxel_pos(&self) -> IVec3 {
self.position.floor().as_ivec3()
}

/// Get the face normal of the ray hit
pub fn voxel_normal(&self) -> IVec3 {
self.normal.floor().as_ivec3()
}
}

const STEP_SIZE: f32 = 0.01;

/// Grants access to the VoxelWorld in systems
#[derive(SystemParam)]
pub struct VoxelWorld<'w, C: VoxelWorldConfig> {
chunk_map: Res<'w, ChunkMap<C>>,
modified_voxels: Res<'w, ModifiedVoxels<C>>,
voxel_write_buffer: ResMut<'w, VoxelWriteBuffer<C>>,
configuration: Res<'w, C>,
}

impl<'w, C: VoxelWorldConfig> VoxelWorld<'w, C> {
Expand Down Expand Up @@ -166,50 +200,7 @@ impl<'w, C: VoxelWorldConfig> VoxelWorld<'w, C> {
z: pos_2d.y.floor() as i32,
})
}
}

pub trait FilterFn {
fn call(&self, input: (Vec3, WorldVoxel)) -> bool;
}

impl<F: Fn((Vec3, WorldVoxel)) -> bool> FilterFn for F {
fn call(&self, input: (Vec3, WorldVoxel)) -> bool {
self(input)
}
}

pub type RaycastFn = dyn Fn(Ray3d, &dyn FilterFn) -> Option<VoxelRaycastResult> + Send + Sync;

#[derive(Default, Debug, PartialEq, Clone)]
pub struct VoxelRaycastResult {
pub position: Vec3,
pub normal: Vec3,
pub voxel: WorldVoxel,
}

impl VoxelRaycastResult {
/// Get the voxel position of the raycast result
pub fn voxel_pos(&self) -> IVec3 {
self.position.floor().as_ivec3()
}

/// Get the face normal of the ray hit
pub fn voxel_normal(&self) -> IVec3 {
self.normal.floor().as_ivec3()
}
}

/// SystemParam helper for raycasting into the voxel world
#[derive(SystemParam)]
pub struct VoxelWorldRaycast<'w, C: VoxelWorldConfig> {
configuration: Res<'w, C>,
chunk_map: Res<'w, ChunkMap<C>>,
voxel_world: VoxelWorld<'w, C>,
}

const STEP_SIZE: f32 = 0.01;

impl<'w, C: VoxelWorldConfig> VoxelWorldRaycast<'w, C> {
/// Get the first solid voxel intersecting with the given ray.
/// The `filter` function can be used to filter out voxels that should not be considered for the raycast.
///
Expand Down Expand Up @@ -257,7 +248,7 @@ impl<'w, C: VoxelWorldConfig> VoxelWorldRaycast<'w, C> {
pub fn raycast_fn(&self) -> Arc<RaycastFn> {
let chunk_map = self.chunk_map.get_map();
let spawning_distance = self.configuration.spawning_distance() as i32;
let get_voxel = self.voxel_world.get_voxel_fn();
let get_voxel = self.get_voxel_fn();

Arc::new(move |ray, filter| {
let chunk_map_read_lock = chunk_map.read().unwrap();
Expand Down

0 comments on commit 262b124

Please sign in to comment.