mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add basic index based coloring for figures
This commit is contained in:
parent
a826eb76ec
commit
9d086949d1
34
common/src/figure/mat_cell.rs
Normal file
34
common/src/figure/mat_cell.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use crate::vol::Vox;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Material {
|
||||
Skin,
|
||||
Hair,
|
||||
EyeDark,
|
||||
EyeLight,
|
||||
EyeWhite,
|
||||
//HairLight,
|
||||
//HairDark,
|
||||
//Clothing,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum MatCell {
|
||||
None,
|
||||
Mat(Material),
|
||||
Normal(Rgb<u8>),
|
||||
}
|
||||
|
||||
impl Vox for MatCell {
|
||||
fn empty() -> Self {
|
||||
MatCell::None
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
MatCell::None => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
pub mod cell;
|
||||
pub mod mat_cell;
|
||||
pub use mat_cell::Material;
|
||||
|
||||
use self::cell::Cell;
|
||||
use self::mat_cell::MatCell;
|
||||
use crate::{
|
||||
util::chromify_srgb,
|
||||
vol::{ReadVol, SizedVol, Vox, WriteVol},
|
||||
@ -31,11 +34,12 @@ impl From<&DotVoxData> for Segment {
|
||||
|
||||
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(
|
||||
Vec3::new(voxel.x, voxel.y, voxel.z).map(|e| i32::from(e)),
|
||||
Cell::new(color),
|
||||
);
|
||||
segment
|
||||
.set(
|
||||
Vec3::new(voxel.x, voxel.y, voxel.z).map(|e| i32::from(e)),
|
||||
Cell::new(color),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +55,7 @@ impl Segment {
|
||||
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.set(pos, new).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,69 +66,62 @@ impl Segment {
|
||||
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 => (),
|
||||
}
|
||||
let cell = match self.get(pos).unwrap() {
|
||||
Cell::Filled(rgb) => 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(),
|
||||
),
|
||||
Cell::Empty => continue,
|
||||
};
|
||||
self.set(pos, cell).unwrap();
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A `Segment` builder that combines segments
|
||||
pub struct SegmentUnionizer(Vec<(Segment, Vec3<i32>)>);
|
||||
// TODO: move
|
||||
/// A `Dyna` builder that combines Dynas
|
||||
pub struct DynaUnionizer<V: Vox>(Vec<(Dyna<V, ()>, Vec3<i32>)>);
|
||||
|
||||
impl SegmentUnionizer {
|
||||
impl<V: Vox + Copy> DynaUnionizer<V> {
|
||||
pub fn new() -> Self {
|
||||
SegmentUnionizer(Vec::new())
|
||||
DynaUnionizer(Vec::new())
|
||||
}
|
||||
pub fn add(mut self, segment: Segment, offset: Vec3<i32>) -> Self {
|
||||
self.0.push((segment, offset));
|
||||
pub fn add(mut self, dyna: Dyna<V, ()>, offset: Vec3<i32>) -> Self {
|
||||
self.0.push((dyna, offset));
|
||||
self
|
||||
}
|
||||
pub fn maybe_add(self, maybe: Option<(Segment, Vec3<i32>)>) -> Self {
|
||||
pub fn maybe_add(self, maybe: Option<(Dyna<V, ()>, Vec3<i32>)>) -> Self {
|
||||
match maybe {
|
||||
Some((segment, offset)) => self.add(segment, offset),
|
||||
Some((dyna, offset)) => self.add(dyna, offset),
|
||||
None => self,
|
||||
}
|
||||
}
|
||||
pub fn unify(self) -> (Segment, Vec3<i32>) {
|
||||
pub fn unify(self) -> (Dyna<V, ()>, Vec3<i32>) {
|
||||
if self.0.is_empty() {
|
||||
return (
|
||||
Segment::filled(Vec3::new(0, 0, 0), Cell::empty(), ()),
|
||||
Vec3::new(0, 0, 0),
|
||||
);
|
||||
return (Dyna::filled(Vec3::zero(), V::empty(), ()), Vec3::zero());
|
||||
}
|
||||
|
||||
// Determine size of the new segment
|
||||
// Determine size of the new Dyna
|
||||
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);
|
||||
for (dyna, offset) in self.0.iter().skip(1) {
|
||||
let size = dyna.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(), ());
|
||||
let mut combined = Dyna::filled(new_size, V::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();
|
||||
for (dyna, offset) in self.0 {
|
||||
for pos in dyna.iter_positions() {
|
||||
let vox = dyna.get(pos).unwrap();
|
||||
if !vox.is_empty() {
|
||||
combined.set(origin + offset + pos, *vox).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -132,3 +129,68 @@ impl SegmentUnionizer {
|
||||
(combined, origin)
|
||||
}
|
||||
}
|
||||
|
||||
pub type MatSegment = Dyna<MatCell, ()>;
|
||||
|
||||
impl MatSegment {
|
||||
pub fn to_segment(&self, map: impl Fn(Material) -> Rgb<u8>) -> Segment {
|
||||
let mut vol = Dyna::filled(self.get_size(), Cell::empty(), ());
|
||||
for pos in self.iter_positions() {
|
||||
let rgb = match self.get(pos).unwrap() {
|
||||
MatCell::None => continue,
|
||||
MatCell::Mat(mat) => map(*mat),
|
||||
MatCell::Normal(rgb) => *rgb,
|
||||
};
|
||||
vol.set(pos, Cell::new(rgb)).unwrap();
|
||||
}
|
||||
vol
|
||||
}
|
||||
}
|
||||
|
||||
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),
|
||||
7 => MatCell::Mat(Material::EyeWhite),
|
||||
//1 => MatCell::Mat(Material::HairLight),
|
||||
//1 => MatCell::Mat(Material::HairDark),
|
||||
//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(), ())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ use common::{
|
||||
item::Tool,
|
||||
object, quadruped, quadruped_medium, Item,
|
||||
},
|
||||
figure::{Segment, SegmentUnionizer},
|
||||
figure::{DynaUnionizer, MatSegment, Material, Segment},
|
||||
};
|
||||
use dot_vox::DotVoxData;
|
||||
use hashbrown::HashMap;
|
||||
@ -25,7 +25,7 @@ pub fn load_segment(mesh_name: &str) -> Segment {
|
||||
let full_specifier: String = ["voxygen.voxel.", mesh_name].concat();
|
||||
Segment::from(assets::load_expect::<DotVoxData>(full_specifier.as_str()).as_ref())
|
||||
}
|
||||
pub fn graceful_load_segment(mesh_name: &str) -> Segment {
|
||||
pub fn graceful_load_mat_segment(mesh_name: &str) -> MatSegment {
|
||||
let full_specifier: String = ["voxygen.voxel.", mesh_name].concat();
|
||||
let dot_vox = match assets::load::<DotVoxData>(full_specifier.as_str()) {
|
||||
Ok(dot_vox) => dot_vox,
|
||||
@ -34,7 +34,7 @@ pub fn graceful_load_segment(mesh_name: &str) -> Segment {
|
||||
assets::load_expect::<DotVoxData>("voxygen.voxel.not_found")
|
||||
}
|
||||
};
|
||||
Segment::from(dot_vox.as_ref())
|
||||
MatSegment::from(dot_vox.as_ref())
|
||||
}
|
||||
pub fn load_mesh(mesh_name: &str, position: Vec3<f32>) -> Mesh<FigurePipeline> {
|
||||
Meshable::<FigurePipeline, FigurePipeline>::generate_mesh(&load_segment(mesh_name), position).0
|
||||
@ -75,7 +75,7 @@ impl HumHeadSpec {
|
||||
beard: Beard,
|
||||
eye_color: EyeColor,
|
||||
skin: Skin,
|
||||
eyebrows: Eyebrows,
|
||||
_eyebrows: Eyebrows,
|
||||
accessory: Accessory,
|
||||
) -> Mesh<FigurePipeline> {
|
||||
let spec = match self.0.get(&(race, body_type)) {
|
||||
@ -85,15 +85,14 @@ impl HumHeadSpec {
|
||||
"No head specification exists for the combination of {:?} and {:?}",
|
||||
race, body_type
|
||||
);
|
||||
return load_mesh("not_found", Vec3::new(-5.0, -5.0, -5.0));
|
||||
return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5));
|
||||
}
|
||||
};
|
||||
// TODO: color hair(via index or recoloring), color skin(via index)
|
||||
// Load segment pieces
|
||||
let bare_head = graceful_load_segment(&spec.head.0);
|
||||
let eyes = graceful_load_segment(&spec.eyes.0);
|
||||
let bare_head = graceful_load_mat_segment(&spec.head.0);
|
||||
let eyes = graceful_load_mat_segment(&spec.eyes.0);
|
||||
let hair = match spec.hair.get(&hair_style) {
|
||||
Some(Some(spec)) => Some((graceful_load_segment(&spec.0), Vec3::from(spec.1))),
|
||||
Some(Some(spec)) => Some((graceful_load_mat_segment(&spec.0), Vec3::from(spec.1))),
|
||||
Some(None) => None,
|
||||
None => {
|
||||
warn!("No specification for this hair style: {:?}", hair_style);
|
||||
@ -101,7 +100,7 @@ impl HumHeadSpec {
|
||||
}
|
||||
};
|
||||
let beard = match spec.beard.get(&beard) {
|
||||
Some(Some(spec)) => Some((graceful_load_segment(&spec.0), Vec3::from(spec.1))),
|
||||
Some(Some(spec)) => Some((graceful_load_mat_segment(&spec.0), Vec3::from(spec.1))),
|
||||
Some(None) => None,
|
||||
None => {
|
||||
warn!("No specification for this beard: {:?}", beard);
|
||||
@ -109,7 +108,7 @@ impl HumHeadSpec {
|
||||
}
|
||||
};
|
||||
let accessory = match spec.accessory.get(&accessory) {
|
||||
Some(Some(spec)) => Some((graceful_load_segment(&spec.0), Vec3::from(spec.1))),
|
||||
Some(Some(spec)) => Some((graceful_load_mat_segment(&spec.0), Vec3::from(spec.1))),
|
||||
Some(None) => None,
|
||||
None => {
|
||||
warn!("No specification for this accessory: {:?}", accessory);
|
||||
@ -117,7 +116,7 @@ impl HumHeadSpec {
|
||||
}
|
||||
};
|
||||
|
||||
let (head, origin_offset) = SegmentUnionizer::new()
|
||||
let (head, origin_offset) = DynaUnionizer::new()
|
||||
.add(bare_head, spec.head.1.into())
|
||||
.add(eyes, spec.eyes.1.into())
|
||||
.maybe_add(hair)
|
||||
@ -125,8 +124,58 @@ impl HumHeadSpec {
|
||||
.maybe_add(accessory)
|
||||
.unify();
|
||||
|
||||
// TODO move this code to a fn
|
||||
// TODO move some of the colors to rgb
|
||||
let colored = head.to_segment(|mat| match mat {
|
||||
Material::Skin => match skin {
|
||||
// TODO include Race in match
|
||||
Skin::Light => Rgb::new(243, 198, 165),
|
||||
Skin::Medium => Rgb::new(203, 128, 97),
|
||||
Skin::Dark => Rgb::new(151, 91, 67),
|
||||
Skin::Rainbow => {
|
||||
use rand::{seq::SliceRandom, thread_rng};
|
||||
*[
|
||||
Rgb::new(240, 4, 4),
|
||||
Rgb::new(240, 140, 4),
|
||||
Rgb::new(240, 235, 4),
|
||||
Rgb::new(50, 240, 5),
|
||||
Rgb::new(4, 4, 240),
|
||||
Rgb::new(150, 0, 175),
|
||||
]
|
||||
.choose(&mut thread_rng())
|
||||
.unwrap()
|
||||
}
|
||||
},
|
||||
Material::Hair => match hair_color {
|
||||
HairColor::Red => Rgb::new(255, 20, 20),
|
||||
HairColor::Green => Rgb::new(20, 255, 20),
|
||||
HairColor::Blue => Rgb::new(20, 20, 255),
|
||||
HairColor::Brown => Rgb::new(50, 50, 0),
|
||||
HairColor::Black => Rgb::new(10, 10, 20),
|
||||
},
|
||||
Material::EyeLight => match eye_color {
|
||||
EyeColor::Black => Rgb::new(0, 0, 0),
|
||||
EyeColor::Blue => Rgb::new(0, 0, 200),
|
||||
EyeColor::Green => Rgb::new(0, 200, 0),
|
||||
EyeColor::Brown => Rgb::new(150, 150, 0),
|
||||
EyeColor::Red => Rgb::new(255, 0, 0),
|
||||
EyeColor::White => Rgb::new(255, 255, 255),
|
||||
},
|
||||
Material::EyeDark => match eye_color {
|
||||
EyeColor::Black => Rgb::new(0, 0, 0),
|
||||
EyeColor::Blue => Rgb::new(0, 0, 100),
|
||||
EyeColor::Green => Rgb::new(0, 100, 0),
|
||||
EyeColor::Brown => Rgb::new(50, 50, 0),
|
||||
EyeColor::Red => Rgb::new(200, 0, 0),
|
||||
EyeColor::White => Rgb::new(255, 255, 255),
|
||||
},
|
||||
Material::EyeWhite => match eye_color {
|
||||
EyeColor::White => Rgb::new(0, 0, 0),
|
||||
_ => Rgb::new(255, 255, 255),
|
||||
},
|
||||
});
|
||||
Meshable::<FigurePipeline, FigurePipeline>::generate_mesh(
|
||||
&head,
|
||||
&colored,
|
||||
Vec3::from(spec.offset) + origin_offset.map(|e| e as f32 * -1.0),
|
||||
)
|
||||
.0
|
||||
@ -199,7 +248,7 @@ pub fn mesh_chest(chest: Chest) -> Mesh<FigurePipeline> {
|
||||
|
||||
let bare_chest = load_segment("figure.body.chest");
|
||||
let chest_armor = load_segment("armor.chest.generic");
|
||||
let chest = SegmentUnionizer::new()
|
||||
let chest = DynaUnionizer::new()
|
||||
.add(bare_chest, Vec3::new(0, 0, 0))
|
||||
.add(chest_armor.chromify(Rgb::from(color)), Vec3::new(0, 0, 0))
|
||||
.unify()
|
||||
|
Loading…
Reference in New Issue
Block a user