use super::{RawVoxel, Voxel, VoxelData, VoxelModel};
use bevy::{
math::{BVec3, IVec3, UVec3, Vec3},
transform::components::GlobalTransform,
};
use ndshape::Shape;
#[derive(Debug, Clone, PartialEq)]
pub struct OutOfBoundsError;
pub trait VoxelQueryable {
fn size(&self) -> IVec3;
fn global_point_to_voxel_space(
&self,
global_point: Vec3,
global_xform: &GlobalTransform,
) -> IVec3 {
let local_position = global_xform
.affine()
.inverse()
.transform_point3(global_point);
self.local_point_to_voxel_space(local_position)
}
fn local_point_to_voxel_space(&self, local_point: Vec3) -> IVec3 {
let size = self.size();
let half_extents = Vec3::new(size.x as f32, size.y as f32, size.z as f32) * 0.5;
let voxel_postition = local_point + half_extents;
IVec3::new(
voxel_postition.x as i32,
voxel_postition.y as i32,
voxel_postition.z as i32,
)
}
fn point_in_model(&self, point: IVec3) -> Result<UVec3, OutOfBoundsError> {
if point.greater_than_or_equal(self.size()).any() {
return Err(OutOfBoundsError);
};
UVec3::try_from(point).map_err(|_| OutOfBoundsError)
}
fn get_voxel_at_point(&self, position: IVec3) -> Result<Voxel, OutOfBoundsError>;
}
impl VoxelQueryable for VoxelModel {
fn size(&self) -> IVec3 {
self.data.size()
}
fn get_voxel_at_point(&self, position: IVec3) -> Result<Voxel, OutOfBoundsError> {
self.data.get_voxel_at_point(position)
}
}
impl VoxelQueryable for VoxelData {
fn size(&self) -> IVec3 {
self._size()
}
fn get_voxel_at_point(&self, position: IVec3) -> Result<Voxel, OutOfBoundsError> {
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).ok_or(OutOfBoundsError)?;
let voxel: Voxel = raw_voxel.clone().into();
Ok(voxel)
}
}
impl VoxelData {
pub fn set_voxel(&mut self, voxel: Voxel, point: Vec3) -> Result<(), 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();
self.voxels[index] = raw_voxel;
Ok(())
}
}
trait BitwiseComparable {
fn less_than(&self, other: Self) -> BVec3;
fn greater_than_or_equal(&self, other: Self) -> BVec3;
}
impl BitwiseComparable for IVec3 {
fn less_than(&self, other: IVec3) -> BVec3 {
BVec3::new(self.x < other.x, self.y < other.y, self.z < other.z)
}
fn greater_than_or_equal(&self, other: IVec3) -> BVec3 {
!self.less_than(other)
}
}