2019-01-13 20:53:55 +00:00
|
|
|
pub mod cell;
|
|
|
|
|
2019-06-06 14:48:41 +00:00
|
|
|
use self::cell::Cell;
|
2019-01-13 20:53:55 +00:00
|
|
|
use crate::{
|
2019-08-19 02:57:41 +00:00
|
|
|
util::chromify_srgb,
|
|
|
|
vol::{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) {
|
|
|
|
// TODO: Maybe don't ignore this error?
|
|
|
|
let _ = segment.set(
|
2019-07-01 20:42:43 +00:00
|
|
|
Vec3::new(voxel.x, voxel.y, voxel.z).map(|e| i32::from(e)),
|
2019-01-13 20:53:55 +00:00
|
|
|
Cell::new(color),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
segment
|
|
|
|
} else {
|
|
|
|
Segment::filled(Vec3::zero(), Cell::empty(), ())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-19 02:57:41 +00:00
|
|
|
|
|
|
|
impl Segment {
|
|
|
|
/// Replaces one cell with another
|
|
|
|
pub fn replace(mut self, old: Cell, new: Cell) -> Self {
|
|
|
|
for pos in self.iter_positions() {
|
|
|
|
if old == *self.get(pos).unwrap() {
|
|
|
|
self.set(pos, new);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
/// Preserve the luminance of all the colors but set the chomaticity to match the provided color
|
2019-08-24 23:18:47 +00:00
|
|
|
// TODO add more advanced recoloring and/or indexed based coloring
|
2019-08-19 02:57:41 +00:00
|
|
|
pub fn chromify(mut self, chroma: Rgb<u8>) -> Self {
|
|
|
|
let chroma = chroma.map(|e| e as f32 * 255.0);
|
|
|
|
for pos in self.iter_positions() {
|
|
|
|
match self.get(pos).unwrap() {
|
|
|
|
Cell::Filled(rgb) => self
|
|
|
|
.set(
|
|
|
|
pos,
|
|
|
|
Cell::Filled(
|
|
|
|
chromify_srgb(Rgb::from_slice(rgb).map(|e| e as f32 / 255.0), chroma)
|
|
|
|
.map(|e| (e * 255.0) as u8)
|
|
|
|
.into_array(),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.unwrap(),
|
|
|
|
Cell::Empty => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
2019-08-24 23:18:47 +00:00
|
|
|
|
|
|
|
/// A `Segment` builder that combines segments
|
|
|
|
pub struct SegmentUnionizer(Vec<(Segment, Vec3<i32>)>);
|
|
|
|
|
|
|
|
impl SegmentUnionizer {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
SegmentUnionizer(Vec::new())
|
|
|
|
}
|
|
|
|
pub fn add(mut self, segment: Segment, offset: Vec3<i32>) -> Self {
|
|
|
|
self.0.push((segment, offset));
|
|
|
|
self
|
|
|
|
}
|
|
|
|
pub fn maybe_add(self, maybe: Option<(Segment, Vec3<i32>)>) -> Self {
|
|
|
|
match maybe {
|
|
|
|
Some((segment, offset)) => self.add(segment, offset),
|
|
|
|
None => self,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn unify(self) -> (Segment, Vec3<i32>) {
|
|
|
|
if self.0.is_empty() {
|
|
|
|
return (
|
|
|
|
Segment::filled(Vec3::new(0, 0, 0), Cell::empty(), ()),
|
|
|
|
Vec3::new(0, 0, 0),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determine size of the new segment
|
|
|
|
let mut min_point = self.0[0].1;
|
|
|
|
let mut max_point = self.0[0].1 + self.0[0].0.get_size().map(|e| e as i32);
|
|
|
|
for (segment, offset) in self.0.iter().skip(1) {
|
|
|
|
let size = segment.get_size().map(|e| e as i32);
|
|
|
|
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
|
|
|
|
let mut combined = Segment::filled(new_size, Cell::empty(), ());
|
|
|
|
// Copy segments into combined
|
|
|
|
let origin = min_point.map(|e| e * -1);
|
|
|
|
for (segment, offset) in self.0 {
|
|
|
|
for pos in segment.iter_positions() {
|
|
|
|
if let Cell::Filled(col) = *segment.get(pos).unwrap() {
|
|
|
|
combined
|
|
|
|
.set(origin + offset + pos, Cell::Filled(col))
|
|
|
|
.unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(combined, origin)
|
|
|
|
}
|
|
|
|
}
|