2019-01-13 20:53:55 +00:00
|
|
|
pub mod cell;
|
2019-08-25 21:31:08 +00:00
|
|
|
pub mod mat_cell;
|
|
|
|
pub use mat_cell::Material;
|
2019-01-13 20:53:55 +00:00
|
|
|
|
2019-06-06 14:48:41 +00:00
|
|
|
use self::cell::Cell;
|
2019-08-25 21:31:08 +00:00
|
|
|
use self::mat_cell::MatCell;
|
2019-01-13 20:53:55 +00:00
|
|
|
use crate::{
|
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
|
|
|
vol::{IntoFullPosIterator, IntoFullVolIterator, ReadVol, SizedVol, Vox, WriteVol},
|
2019-01-13 20:53:55 +00:00
|
|
|
volumes::dyna::Dyna,
|
|
|
|
};
|
2019-06-06 14:48:41 +00:00
|
|
|
use dot_vox::DotVoxData;
|
|
|
|
use vek::*;
|
2019-01-13 20:53:55 +00:00
|
|
|
|
|
|
|
/// A type representing a volume that may be part of an animated figure.
|
|
|
|
///
|
|
|
|
/// Figures are used to represent things like characters, NPCs, mobs, etc.
|
|
|
|
pub type Segment = Dyna<Cell, ()>;
|
|
|
|
|
2019-04-28 02:12:30 +00:00
|
|
|
impl From<&DotVoxData> for Segment {
|
|
|
|
fn from(dot_vox_data: &DotVoxData) -> Self {
|
2019-01-13 20:53:55 +00:00
|
|
|
if let Some(model) = dot_vox_data.models.get(0) {
|
|
|
|
let palette = dot_vox_data
|
|
|
|
.palette
|
|
|
|
.iter()
|
|
|
|
.map(|col| Rgba::from(col.to_ne_bytes()).into())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
let mut segment = Segment::filled(
|
2019-04-29 20:37:19 +00:00
|
|
|
Vec3::new(model.size.x, model.size.y, model.size.z),
|
2019-01-13 20:53:55 +00:00
|
|
|
Cell::empty(),
|
|
|
|
(),
|
|
|
|
);
|
|
|
|
|
|
|
|
for voxel in &model.voxels {
|
|
|
|
if let Some(&color) = palette.get(voxel.i as usize) {
|
2019-08-25 21:31:08 +00:00
|
|
|
segment
|
|
|
|
.set(
|
|
|
|
Vec3::new(voxel.x, voxel.y, voxel.z).map(|e| i32::from(e)),
|
|
|
|
Cell::new(color),
|
|
|
|
)
|
|
|
|
.unwrap();
|
2019-01-13 20:53:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
segment
|
|
|
|
} else {
|
|
|
|
Segment::filled(Vec3::zero(), Cell::empty(), ())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-19 02:57:41 +00:00
|
|
|
|
|
|
|
impl Segment {
|
2019-08-29 02:48:06 +00:00
|
|
|
/// Transform cells
|
|
|
|
pub fn map(mut self, transform: impl Fn(Cell) -> Option<Cell>) -> 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
|
|
|
for pos in self.full_pos_iter() {
|
2019-08-29 02:48:06 +00:00
|
|
|
if let Some(new) = transform(*self.get(pos).unwrap()) {
|
2019-08-25 21:31:08 +00:00
|
|
|
self.set(pos, new).unwrap();
|
2019-08-19 02:57:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
2019-08-29 02:48:06 +00:00
|
|
|
/// Transform cell colors
|
|
|
|
pub fn map_rgb(self, transform: impl Fn(Rgb<u8>) -> Rgb<u8>) -> Self {
|
|
|
|
self.map(|cell| cell.get_color().map(|rgb| Cell::new(transform(rgb))))
|
|
|
|
}
|
2019-08-19 02:57:41 +00:00
|
|
|
}
|
2019-08-24 23:18:47 +00:00
|
|
|
|
2019-08-25 21:31:08 +00:00
|
|
|
// TODO: move
|
|
|
|
/// A `Dyna` builder that combines Dynas
|
|
|
|
pub struct DynaUnionizer<V: Vox>(Vec<(Dyna<V, ()>, Vec3<i32>)>);
|
2019-08-24 23:18:47 +00:00
|
|
|
|
2019-08-25 21:31:08 +00:00
|
|
|
impl<V: Vox + Copy> DynaUnionizer<V> {
|
2019-08-24 23:18:47 +00:00
|
|
|
pub fn new() -> Self {
|
2019-08-25 21:31:08 +00:00
|
|
|
DynaUnionizer(Vec::new())
|
2019-08-24 23:18:47 +00:00
|
|
|
}
|
2019-08-25 21:31:08 +00:00
|
|
|
pub fn add(mut self, dyna: Dyna<V, ()>, offset: Vec3<i32>) -> Self {
|
|
|
|
self.0.push((dyna, offset));
|
2019-08-24 23:18:47 +00:00
|
|
|
self
|
|
|
|
}
|
2019-08-25 21:31:08 +00:00
|
|
|
pub fn maybe_add(self, maybe: Option<(Dyna<V, ()>, Vec3<i32>)>) -> Self {
|
2019-08-24 23:18:47 +00:00
|
|
|
match maybe {
|
2019-08-25 21:31:08 +00:00
|
|
|
Some((dyna, offset)) => self.add(dyna, offset),
|
2019-08-24 23:18:47 +00:00
|
|
|
None => self,
|
|
|
|
}
|
|
|
|
}
|
2019-08-25 21:31:08 +00:00
|
|
|
pub fn unify(self) -> (Dyna<V, ()>, Vec3<i32>) {
|
2019-08-24 23:18:47 +00:00
|
|
|
if self.0.is_empty() {
|
2019-08-25 21:31:08 +00:00
|
|
|
return (Dyna::filled(Vec3::zero(), V::empty(), ()), Vec3::zero());
|
2019-08-24 23:18:47 +00:00
|
|
|
}
|
|
|
|
|
2019-08-25 21:31:08 +00:00
|
|
|
// Determine size of the new Dyna
|
2019-08-24 23:18:47 +00:00
|
|
|
let mut min_point = self.0[0].1;
|
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
|
|
|
let mut max_point = self.0[0].1 + self.0[0].0.size().map(|e| e as i32);
|
2019-08-25 21:31:08 +00:00
|
|
|
for (dyna, offset) in self.0.iter().skip(1) {
|
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
|
|
|
let size = dyna.size().map(|e| e as i32);
|
2019-08-24 23:18:47 +00:00
|
|
|
min_point = min_point.map2(*offset, std::cmp::min);
|
|
|
|
max_point = max_point.map2(offset + size, std::cmp::max);
|
|
|
|
}
|
|
|
|
let new_size = (max_point - min_point).map(|e| e as u32);
|
|
|
|
// Allocate new segment
|
2019-08-25 21:31:08 +00:00
|
|
|
let mut combined = Dyna::filled(new_size, V::empty(), ());
|
2019-08-24 23:18:47 +00:00
|
|
|
// Copy segments into combined
|
|
|
|
let origin = min_point.map(|e| e * -1);
|
2019-08-25 21:31:08 +00:00
|
|
|
for (dyna, offset) in self.0 {
|
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
|
|
|
for (pos, vox) in dyna.full_vol_iter() {
|
2019-08-25 21:31:08 +00:00
|
|
|
if !vox.is_empty() {
|
|
|
|
combined.set(origin + offset + pos, *vox).unwrap();
|
2019-08-24 23:18:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(combined, origin)
|
|
|
|
}
|
|
|
|
}
|
2019-08-25 21:31:08 +00:00
|
|
|
|
|
|
|
pub type MatSegment = Dyna<MatCell, ()>;
|
|
|
|
|
|
|
|
impl MatSegment {
|
|
|
|
pub fn to_segment(&self, map: impl Fn(Material) -> Rgb<u8>) -> Segment {
|
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
|
|
|
let mut vol = Dyna::filled(self.size(), Cell::empty(), ());
|
|
|
|
for (pos, vox) in self.full_vol_iter() {
|
|
|
|
let rgb = match vox {
|
2019-08-25 21:31:08 +00:00
|
|
|
MatCell::None => continue,
|
|
|
|
MatCell::Mat(mat) => map(*mat),
|
|
|
|
MatCell::Normal(rgb) => *rgb,
|
|
|
|
};
|
|
|
|
vol.set(pos, Cell::new(rgb)).unwrap();
|
|
|
|
}
|
|
|
|
vol
|
|
|
|
}
|
2019-09-18 16:46:12 +00:00
|
|
|
/// Transform cells
|
|
|
|
pub fn map(mut self, transform: impl Fn(MatCell) -> Option<MatCell>) -> Self {
|
|
|
|
for pos in self.full_pos_iter() {
|
|
|
|
if let Some(new) = transform(*self.get(pos).unwrap()) {
|
|
|
|
self.set(pos, new).unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
/// Transform cell colors
|
|
|
|
pub fn map_rgb(self, transform: impl Fn(Rgb<u8>) -> Rgb<u8>) -> Self {
|
|
|
|
self.map(|cell| match cell {
|
|
|
|
MatCell::Normal(rgb) => Some(MatCell::Normal(transform(rgb))),
|
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
}
|
2019-08-25 21:31:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<&DotVoxData> for MatSegment {
|
|
|
|
fn from(dot_vox_data: &DotVoxData) -> Self {
|
|
|
|
if let Some(model) = dot_vox_data.models.get(0) {
|
|
|
|
let palette = dot_vox_data
|
|
|
|
.palette
|
|
|
|
.iter()
|
|
|
|
.map(|col| Rgba::from(col.to_ne_bytes()).into())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
let mut vol = Dyna::filled(
|
|
|
|
Vec3::new(model.size.x, model.size.y, model.size.z),
|
|
|
|
MatCell::empty(),
|
|
|
|
(),
|
|
|
|
);
|
|
|
|
|
|
|
|
for voxel in &model.voxels {
|
|
|
|
let block = match voxel.i {
|
|
|
|
0 => MatCell::Mat(Material::Skin),
|
|
|
|
1 => MatCell::Mat(Material::Hair),
|
|
|
|
2 => MatCell::Mat(Material::EyeDark),
|
|
|
|
3 => MatCell::Mat(Material::EyeLight),
|
2019-10-04 18:27:12 +00:00
|
|
|
4 => MatCell::Mat(Material::SkinDark),
|
|
|
|
5 => MatCell::Mat(Material::SkinLight),
|
2019-08-25 21:31:08 +00:00
|
|
|
7 => MatCell::Mat(Material::EyeWhite),
|
|
|
|
//6 => MatCell::Mat(Material::Clothing),
|
|
|
|
index => {
|
|
|
|
let color = palette
|
|
|
|
.get(index as usize)
|
|
|
|
.copied()
|
|
|
|
.unwrap_or_else(|| Rgb::broadcast(0));
|
|
|
|
MatCell::Normal(color)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
vol.set(
|
|
|
|
Vec3::new(voxel.x, voxel.y, voxel.z).map(|e| i32::from(e)),
|
|
|
|
block,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
vol
|
|
|
|
} else {
|
|
|
|
Dyna::filled(Vec3::zero(), MatCell::empty(), ())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|