veloren/common/src/ray.rs

105 lines
2.7 KiB
Rust
Raw Normal View History

use crate::vol::ReadVol;
use vek::*;
pub trait RayForEach<V> = FnMut(&V, Vec3<i32>);
pub struct Ray<'a, V: ReadVol, F: FnMut(&V::Vox) -> bool, G: RayForEach<V::Vox>> {
vol: &'a V,
from: Vec3<f32>,
to: Vec3<f32>,
until: F,
2019-08-07 17:17:04 +00:00
for_each: Option<G>,
max_iter: usize,
ignore_error: bool,
}
impl<'a, V: ReadVol, F: FnMut(&V::Vox) -> bool, G: RayForEach<V::Vox>> Ray<'a, V, F, G> {
pub fn new(vol: &'a V, from: Vec3<f32>, to: Vec3<f32>, until: F) -> Self {
Self {
vol,
from,
to,
until,
2019-08-07 17:17:04 +00:00
for_each: None,
max_iter: 100,
ignore_error: false,
}
}
pub fn until<H: FnMut(&V::Vox) -> bool>(self, f: H) -> Ray<'a, V, H, G> {
Ray {
vol: self.vol,
from: self.from,
to: self.to,
until: f,
for_each: self.for_each,
max_iter: self.max_iter,
ignore_error: self.ignore_error,
}
}
2020-07-05 12:39:28 +00:00
pub fn for_each<H: RayForEach<V::Vox>>(self, f: H) -> Ray<'a, V, F, H> {
2019-08-07 17:17:04 +00:00
Ray {
for_each: Some(f),
vol: self.vol,
from: self.from,
to: self.to,
until: self.until,
max_iter: self.max_iter,
ignore_error: self.ignore_error,
}
}
pub fn max_iter(mut self, max_iter: usize) -> Self {
self.max_iter = max_iter;
self
}
pub fn ignore_error(mut self) -> Self {
self.ignore_error = true;
self
}
common: Rework volume API See the doc comments in `common/src/vol.rs` for more information on the API itself. The changes include: * Consistent `Err`/`Error` naming. * Types are named `...Error`. * `enum` variants are named `...Err`. * Rename `VolMap{2d, 3d}` -> `VolGrid{2d, 3d}`. This is in preparation to an upcoming change where a “map” in the game related sense will be added. * Add volume iterators. There are two types of them: * _Position_ iterators obtained from the trait `IntoPosIterator` using the method `fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...` which returns an iterator over `Vec3<i32>`. * _Volume_ iterators obtained from the trait `IntoVolIterator` using the method `fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...` which returns an iterator over `(Vec3<i32>, &Self::Vox)`. Those traits will usually be implemented by references to volume types (i.e. `impl IntoVolIterator<'a> for &'a T` where `T` is some type which usually implements several volume traits, such as `Chunk`). * _Position_ iterators iterate over the positions valid for that volume. * _Volume_ iterators do the same but return not only the position but also the voxel at that position, in each iteration. * Introduce trait `RectSizedVol` for the use case which we have with `Chonk`: A `Chonk` is sized only in x and y direction. * Introduce traits `RasterableVol`, `RectRasterableVol` * `RasterableVol` represents a volume that is compile-time sized and has its lower bound at `(0, 0, 0)`. The name `RasterableVol` was chosen because such a volume can be used with `VolGrid3d`. * `RectRasterableVol` represents a volume that is compile-time sized at least in x and y direction and has its lower bound at `(0, 0, z)`. There's no requirement on he lower bound or size in z direction. The name `RectRasterableVol` was chosen because such a volume can be used with `VolGrid2d`.
2019-09-03 22:23:29 +00:00
pub fn cast(mut self) -> (f32, Result<Option<&'a V::Vox>, V::Error>) {
// TODO: Fully test this!
const PLANCK: f32 = 0.001;
let mut dist = 0.0;
let dir = (self.to - self.from).normalized();
let max = (self.to - self.from).magnitude();
for _ in 0..self.max_iter {
let pos = self.from + dir * dist;
let ipos = pos.map(|e| e.floor() as i32);
// Allow one iteration above max.
if dist > max {
break;
}
2020-07-05 12:39:28 +00:00
let vox = self.vol.get(ipos);
2019-08-07 17:17:04 +00:00
// for_each
if let Some(g) = &mut self.for_each {
2020-07-05 12:39:28 +00:00
if let Ok(vox) = vox {
g(vox, ipos);
}
2019-08-07 17:17:04 +00:00
}
2020-07-05 12:39:28 +00:00
match vox.map(|vox| (vox, (self.until)(vox))) {
Ok((vox, true)) => return (dist, Ok(Some(vox))),
Err(err) if !self.ignore_error => return (dist, Err(err)),
_ => {},
}
let deltas =
(dir.map(|e| if e < 0.0 { 0.0 } else { 1.0 }) - pos.map(|e| e.abs().fract())) / dir;
dist += deltas.reduce(f32::min).max(PLANCK);
}
(dist, Ok(None))
}
}