diff --git a/examples/voxel-collisions.rs b/examples/voxel-collisions.rs index d157440..46b344a 100644 --- a/examples/voxel-collisions.rs +++ b/examples/voxel-collisions.rs @@ -113,7 +113,7 @@ fn update_snow( model.global_point_to_voxel_space(snowflake_xform.translation, item_xform); // check whether snowflake has landed on something solid let pos_below_snowflake = vox_pos - IVec3::Y; - let Some(voxel) = model.get_voxel_at_point(pos_below_snowflake) else { continue }; + let Ok(voxel) = model.get_voxel_at_point(pos_below_snowflake) else { continue }; if voxel == Voxel::EMPTY { continue; }; @@ -129,7 +129,7 @@ fn update_snow( move |pos, voxel, model| { // a signed distance field for a sphere, but _only_ drawing it on empty cells directly above solid voxels if *voxel == Voxel::EMPTY && pos.distance_squared(vox_pos) <= radius_squared { - if let Some(voxel_below) = model.get_voxel_at_point(pos - IVec3::Y) { + if let Ok(voxel_below) = model.get_voxel_at_point(pos - IVec3::Y) { if voxel_below != Voxel::EMPTY { // draw our snow material return Voxel(234); diff --git a/src/model/mod.rs b/src/model/mod.rs index 0e74e80..c66492a 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -46,8 +46,6 @@ impl VoxelModel { } /// The voxel data used to create a mesh and a material. -/// -/// Note that all coordinates are in Bevy's right-handed Y-up space pub struct VoxelData { pub(crate) shape: RuntimeShape, pub(crate) voxels: Vec, diff --git a/src/model/queryable.rs b/src/model/queryable.rs index 1d7cc2a..38da768 100644 --- a/src/model/queryable.rs +++ b/src/model/queryable.rs @@ -5,6 +5,9 @@ use bevy::{ }; use ndshape::Shape; +#[derive(Debug, Clone, PartialEq)] +pub struct OutOfBoundsError; + /// Methods for converting from global and local to voxel-space coordinates, getting the size of a voxel model, and the voxel at a given point pub trait VoxelQueryable { /// The size of the voxel model. @@ -49,11 +52,11 @@ pub trait VoxelQueryable { } /// If the voxel-space `point` is within the bounds of the model, it will be returned as a [`bevy::math::UVec3`]. - fn point_in_model(&self, point: IVec3) -> Option { + fn point_in_model(&self, point: IVec3) -> Result { if point.greater_than_or_equal(self.size()).any() { - return None; + return Err(OutOfBoundsError); }; - UVec3::try_from(point).ok() + UVec3::try_from(point).map_err(|_| OutOfBoundsError) } /// Returns the [`Voxel`] at the point (given in voxel space) /// @@ -61,8 +64,8 @@ pub trait VoxelQueryable { /// * `position` - the position in voxel space /// /// ### Returns - /// the voxel at this point. If the point lies outside the bounds of the model, it will return [`None`]. - fn get_voxel_at_point(&self, position: IVec3) -> Option; + /// the voxel at this point. If the point lies outside the bounds of the model, it will return [`OutOfBoundsError`]. + fn get_voxel_at_point(&self, position: IVec3) -> Result; } impl VoxelQueryable for VoxelModel { @@ -71,7 +74,7 @@ impl VoxelQueryable for VoxelModel { self.data.size() } - fn get_voxel_at_point(&self, position: IVec3) -> Option { + fn get_voxel_at_point(&self, position: IVec3) -> Result { self.data.get_voxel_at_point(position) } } @@ -84,19 +87,16 @@ impl VoxelQueryable for VoxelData { IVec3::try_from(padded).unwrap_or(IVec3::ZERO) } - fn get_voxel_at_point(&self, position: IVec3) -> Option { + fn get_voxel_at_point(&self, position: IVec3) -> Result { let position = self.point_in_model(position)?; let leading_padding = UVec3::splat(self.padding() / 2); let index = self.shape.linearize((position + leading_padding).into()) as usize; - let raw_voxel = self.voxels.get(index)?; + let raw_voxel = self.voxels.get(index).ok_or(OutOfBoundsError)?; let voxel: Voxel = raw_voxel.clone().into(); - Some(voxel) + Ok(voxel) } } -#[derive(Debug, Clone)] -pub struct OutOfBoundsError; - impl VoxelData { /// Writes a voxel to a point in the model /// @@ -105,11 +105,9 @@ impl VoxelData { /// * `point` - the position at which the voxel will be written /// /// ### Returns - /// `Ok(())` if the operation was successful, or [`OutOfBoundsError`] if `point` lies outside the model + /// [`Result::Ok`] if the operation was successful, or [`OutOfBoundsError`] if `point` lies outside the model pub fn set_voxel(&mut self, voxel: Voxel, point: Vec3) -> Result<(), OutOfBoundsError> { - let position = self - .point_in_model(point.as_ivec3()) - .ok_or(OutOfBoundsError)?; + let position = self.point_in_model(point.as_ivec3())?; let leading_padding = UVec3::splat(self.padding() / 2); let index = self.shape.linearize((position + leading_padding).into()) as usize; let raw_voxel: RawVoxel = voxel.into(); diff --git a/src/tests.rs b/src/tests.rs index a943597..9d8585b 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,5 +1,9 @@ use super::*; -use crate::{model::RawVoxel, scene::VoxelModelInstance, VoxScenePlugin, VoxelRegion}; +use crate::{ + model::{queryable::OutOfBoundsError, RawVoxel}, + scene::VoxelModelInstance, + VoxScenePlugin, VoxelRegion, +}; use bevy::{ app::App, asset::{AssetApp, AssetPlugin, AssetServer, Assets, Handle, LoadState}, @@ -279,12 +283,12 @@ async fn test_modify_voxels() { .expect("retrieve model from Res"); assert_eq!( model.get_voxel_at_point(IVec3::splat(4)), - None, + Err(OutOfBoundsError), "Max coordinate should be 3,3,3" ); assert_eq!( model.get_voxel_at_point(IVec3::splat(-1)), - None, + Err(OutOfBoundsError), "Min coordinate should be 0,0,0" ); let voxel = model