mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
3869cdf1d2
This results in an extremely visually noticeable improvement in latency when adding or removing sprite data and makes the game feel more responsive. This happens, for instance, when picking up a sprite like an apple or flower from the environment. We check to make sure that for items with lighting (like Velorite) or changes that otherwise affect meshing (like changing from fluid to nonfluid) this doesn't trigger.
300 lines
8.8 KiB
Rust
300 lines
8.8 KiB
Rust
use crate::{ray::Ray, volumes::scaled::Scaled};
|
|
use std::fmt::Debug;
|
|
use vek::*;
|
|
|
|
/// Used to specify a volume's compile-time size. This exists as a substitute
|
|
/// until const generics are implemented.
|
|
pub trait VolSize: Clone {
|
|
const SIZE: Vec3<u32>;
|
|
}
|
|
|
|
pub trait RectVolSize: Clone {
|
|
const RECT_SIZE: Vec2<u32>;
|
|
}
|
|
|
|
/// A voxel.
|
|
pub trait Vox: Sized + Clone + PartialEq {
|
|
fn empty() -> Self;
|
|
fn is_empty(&self) -> bool;
|
|
|
|
fn or(self, other: Self) -> Self { if self.is_empty() { other } else { self } }
|
|
}
|
|
|
|
/// A volume that contains voxel data.
|
|
pub trait BaseVol {
|
|
type Vox;
|
|
type Error: Debug;
|
|
|
|
fn scaled_by(self, scale: Vec3<f32>) -> Scaled<Self>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
Scaled { inner: self, scale }
|
|
}
|
|
}
|
|
|
|
/// Implementing `BaseVol` for any `&'a BaseVol` makes it possible to implement
|
|
/// `IntoVolIterator` for references.
|
|
impl<'a, T: BaseVol> BaseVol for &'a T {
|
|
type Error = T::Error;
|
|
type Vox = T::Vox;
|
|
}
|
|
|
|
// Utility types
|
|
|
|
/// A volume that is a cuboid.
|
|
pub trait SizedVol: BaseVol {
|
|
/// Returns the (inclusive) lower bound of the volume.
|
|
fn lower_bound(&self) -> Vec3<i32>;
|
|
|
|
/// Returns the (exclusive) upper bound of the volume.
|
|
fn upper_bound(&self) -> Vec3<i32>;
|
|
|
|
/// Returns the size of the volume.
|
|
fn size(&self) -> Vec3<u32> { (self.upper_bound() - self.lower_bound()).map(|e| e as u32) }
|
|
}
|
|
|
|
/// 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`.
|
|
pub trait RasterableVol: BaseVol {
|
|
const SIZE: Vec3<u32>;
|
|
}
|
|
|
|
impl<V: RasterableVol> SizedVol for V {
|
|
fn lower_bound(&self) -> Vec3<i32> { Vec3::zero() }
|
|
|
|
fn upper_bound(&self) -> Vec3<i32> { V::SIZE.map(|e| e as i32) }
|
|
}
|
|
|
|
/// A volume whose cross section with the XY-plane is a rectangle.
|
|
pub trait RectSizedVol: BaseVol {
|
|
fn lower_bound_xy(&self) -> Vec2<i32>;
|
|
|
|
fn upper_bound_xy(&self) -> Vec2<i32>;
|
|
|
|
fn size_xy(&self) -> Vec2<u32> {
|
|
(self.upper_bound_xy() - self.lower_bound_xy()).map(|e| e as u32)
|
|
}
|
|
}
|
|
|
|
/// A volume that is compile-time sized in x and y direction and has its lower
|
|
/// bound at `(0, 0, z)`. In z direction there's no restriction on the lower
|
|
/// or upper bound. The name `RectRasterableVol` was chosen because such a
|
|
/// volume can be used with `VolGrid2d`.
|
|
pub trait RectRasterableVol: BaseVol {
|
|
const RECT_SIZE: Vec2<u32>;
|
|
}
|
|
|
|
impl<V: RectRasterableVol> RectSizedVol for V {
|
|
fn lower_bound_xy(&self) -> Vec2<i32> { Vec2::zero() }
|
|
|
|
fn upper_bound_xy(&self) -> Vec2<i32> { V::RECT_SIZE.map(|e| e as i32) }
|
|
}
|
|
|
|
/// A volume that provides read access to its voxel data.
|
|
pub trait ReadVol: BaseVol {
|
|
/// Get a reference to the voxel at the provided position in the volume.
|
|
fn get(&self, pos: Vec3<i32>) -> Result<&Self::Vox, Self::Error>;
|
|
|
|
#[allow(clippy::type_complexity)] // TODO: Pending review in #587
|
|
/// NOTE: By default, this ray will simply run from `from` to `to` without
|
|
/// stopping. To make something interesting happen, call `until` or
|
|
/// `for_each`.
|
|
fn ray(
|
|
&self,
|
|
from: Vec3<f32>,
|
|
to: Vec3<f32>,
|
|
) -> Ray<Self, fn(&Self::Vox) -> bool, fn(&Self::Vox, Vec3<i32>)>
|
|
where
|
|
Self: Sized,
|
|
{
|
|
Ray::new(self, from, to, |_| true)
|
|
}
|
|
}
|
|
|
|
/// A volume that provides the ability to sample (i.e., clone a section of) its
|
|
/// voxel data.
|
|
///
|
|
/// TODO (haslersn): Do we still need this now that we have `IntoVolIterator`?
|
|
pub trait SampleVol<I>: BaseVol {
|
|
type Sample: BaseVol + ReadVol;
|
|
/// Take a sample of the volume by cloning voxels within the provided range.
|
|
///
|
|
/// Note that value and accessibility of voxels outside the bounds of the
|
|
/// sample is implementation-defined and should not be used.
|
|
///
|
|
/// Note that the resultant volume has a coordinate space relative to the
|
|
/// sample, not the original volume.
|
|
fn sample(&self, range: I) -> Result<Self::Sample, Self::Error>;
|
|
}
|
|
|
|
/// A volume that provides write access to its voxel data.
|
|
pub trait WriteVol: BaseVol {
|
|
/// Set the voxel at the provided position in the volume to the provided
|
|
/// value.
|
|
fn set(&mut self, pos: Vec3<i32>, vox: Self::Vox) -> Result<Self::Vox, Self::Error>;
|
|
|
|
/// Map a voxel to another using the provided function.
|
|
// TODO: Is `map` the right name? Implies a change in type.
|
|
fn map<F: FnOnce(Self::Vox) -> Self::Vox>(
|
|
&mut self,
|
|
pos: Vec3<i32>,
|
|
f: F,
|
|
) -> Result<Self::Vox, Self::Error>
|
|
where
|
|
Self: ReadVol,
|
|
Self::Vox: Clone,
|
|
{
|
|
// This is *deliberately* not using a get_mut since this might trigger a
|
|
// repr change of the underlying volume
|
|
self.set(pos, f(self.get(pos)?.clone()))
|
|
}
|
|
}
|
|
|
|
/// A volume (usually rather a reference to a volume) that is convertible into
|
|
/// an iterator to a cuboid subsection of the volume.
|
|
pub trait IntoVolIterator<'a>: BaseVol
|
|
where
|
|
Self::Vox: 'a,
|
|
{
|
|
type IntoIter: Iterator<Item = (Vec3<i32>, &'a Self::Vox)>;
|
|
|
|
fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> Self::IntoIter;
|
|
}
|
|
|
|
pub trait IntoPosIterator: BaseVol {
|
|
type IntoIter: Iterator<Item = Vec3<i32>>;
|
|
|
|
fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> Self::IntoIter;
|
|
}
|
|
|
|
// Helpers
|
|
|
|
/// A volume (usually rather a reference to a volume) that is convertible into
|
|
/// an iterator.
|
|
pub trait IntoFullVolIterator<'a>: BaseVol
|
|
where
|
|
Self::Vox: 'a,
|
|
{
|
|
type IntoIter: Iterator<Item = (Vec3<i32>, &'a Self::Vox)>;
|
|
|
|
fn full_vol_iter(self) -> Self::IntoIter;
|
|
}
|
|
|
|
/// For any `&'a SizedVol: IntoVolIterator` we implement `IntoFullVolIterator`.
|
|
/// Unfortunately we can't just implement `IntoIterator` in this generic way
|
|
/// because it's defined in another crate. That's actually the only reason why
|
|
/// the trait `IntoFullVolIterator` exists.
|
|
// TODO: See whether relaxed orphan rules permit this to be replaced now
|
|
impl<'a, T: 'a + SizedVol> IntoFullVolIterator<'a> for &'a T
|
|
where
|
|
Self: IntoVolIterator<'a>,
|
|
{
|
|
type IntoIter = <Self as IntoVolIterator<'a>>::IntoIter;
|
|
|
|
fn full_vol_iter(self) -> Self::IntoIter {
|
|
self.vol_iter(self.lower_bound(), self.upper_bound())
|
|
}
|
|
}
|
|
|
|
pub trait IntoFullPosIterator: BaseVol {
|
|
type IntoIter: Iterator<Item = Vec3<i32>>;
|
|
|
|
fn full_pos_iter(self) -> Self::IntoIter;
|
|
}
|
|
|
|
impl<'a, T: 'a + SizedVol> IntoFullPosIterator for &'a T
|
|
where
|
|
Self: IntoPosIterator,
|
|
{
|
|
type IntoIter = <Self as IntoPosIterator>::IntoIter;
|
|
|
|
fn full_pos_iter(self) -> Self::IntoIter {
|
|
self.pos_iter(self.lower_bound(), self.upper_bound())
|
|
}
|
|
}
|
|
|
|
// Defaults
|
|
|
|
/// Convenience iterator type that can be used to quickly implement
|
|
/// `IntoPosIterator`.
|
|
pub struct DefaultPosIterator {
|
|
current: Vec3<i32>,
|
|
begin: Vec2<i32>,
|
|
end: Vec3<i32>,
|
|
}
|
|
|
|
impl DefaultPosIterator {
|
|
pub fn new(lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> Self {
|
|
debug_assert!(lower_bound.map2(upper_bound, |l, u| l <= u).reduce_and());
|
|
let end = if lower_bound.map2(upper_bound, |l, u| l < u).reduce_and() {
|
|
upper_bound
|
|
} else {
|
|
// Special case because our implementation doesn't handle empty ranges for x or
|
|
// y:
|
|
lower_bound
|
|
};
|
|
Self {
|
|
current: lower_bound,
|
|
begin: From::from(lower_bound),
|
|
end,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Iterator for DefaultPosIterator {
|
|
type Item = Vec3<i32>;
|
|
|
|
fn next(&mut self) -> Option<Vec3<i32>> {
|
|
if self.current.z == self.end.z {
|
|
return None;
|
|
}
|
|
let ret = self.current;
|
|
self.current.x += 1;
|
|
if self.current.x == self.end.x {
|
|
self.current.x = self.begin.x;
|
|
self.current.y += 1;
|
|
if self.current.y == self.end.y {
|
|
self.current.y = self.begin.y;
|
|
self.current.z += 1;
|
|
}
|
|
}
|
|
Some(ret)
|
|
}
|
|
}
|
|
|
|
/// Convenience iterator type that can be used to quickly implement
|
|
/// `IntoVolIterator`.
|
|
pub struct DefaultVolIterator<'a, T: ReadVol> {
|
|
vol: &'a T,
|
|
pos_iter: DefaultPosIterator,
|
|
}
|
|
|
|
impl<'a, T: ReadVol> DefaultVolIterator<'a, T> {
|
|
pub fn new(vol: &'a T, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> Self {
|
|
Self {
|
|
vol,
|
|
pos_iter: DefaultPosIterator::new(lower_bound, upper_bound),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, T: ReadVol> Iterator for DefaultVolIterator<'a, T> {
|
|
type Item = (Vec3<i32>, &'a T::Vox);
|
|
|
|
fn next(&mut self) -> Option<(Vec3<i32>, &'a T::Vox)> {
|
|
while let Some(pos) = self.pos_iter.next() {
|
|
if let Ok(vox) = self.vol.get(pos) {
|
|
return Some((pos, vox));
|
|
}
|
|
}
|
|
None
|
|
}
|
|
}
|
|
|
|
impl<'b, T: ReadVol> ReadVol for &'b T {
|
|
#[inline(always)]
|
|
fn get(&self, pos: Vec3<i32>) -> Result<&'_ Self::Vox, Self::Error> { (*self).get(pos) }
|
|
}
|