mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Define different sprite configurations per-attributes
This commit is contained in:
parent
bedb46ac48
commit
4781134d7e
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,7 @@
|
|||||||
|
pub mod sprite;
|
||||||
mod watcher;
|
mod watcher;
|
||||||
|
|
||||||
|
use self::sprite::SpriteSpec;
|
||||||
pub use self::watcher::{BlocksOfInterest, FireplaceType, Interaction};
|
pub use self::watcher::{BlocksOfInterest, FireplaceType, Interaction};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -15,6 +17,7 @@ use crate::{
|
|||||||
SpriteGlobalsBindGroup, SpriteInstance, SpriteVertex, SpriteVerts, TerrainAtlasData,
|
SpriteGlobalsBindGroup, SpriteInstance, SpriteVertex, SpriteVerts, TerrainAtlasData,
|
||||||
TerrainLocals, TerrainShadowDrawer, TerrainVertex, SPRITE_VERT_PAGE_SIZE,
|
TerrainLocals, TerrainShadowDrawer, TerrainVertex, SPRITE_VERT_PAGE_SIZE,
|
||||||
},
|
},
|
||||||
|
scene::terrain::sprite::SpriteModelConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@ -22,7 +25,7 @@ use super::{
|
|||||||
math, SceneData, RAIN_THRESHOLD,
|
math, SceneData, RAIN_THRESHOLD,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets::{self, AssetExt, DotVoxAsset},
|
assets::{AssetExt, DotVoxAsset},
|
||||||
figure::Segment,
|
figure::Segment,
|
||||||
spiral::Spiral2d,
|
spiral::Spiral2d,
|
||||||
terrain::{Block, SpriteKind, TerrainChunk},
|
terrain::{Block, SpriteKind, TerrainChunk},
|
||||||
@ -34,7 +37,6 @@ use core::{f32, fmt::Debug, marker::PhantomData, time::Duration};
|
|||||||
use crossbeam_channel as channel;
|
use crossbeam_channel as channel;
|
||||||
use guillotiere::AtlasAllocator;
|
use guillotiere::AtlasAllocator;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use serde::Deserialize;
|
|
||||||
use std::sync::{
|
use std::sync::{
|
||||||
atomic::{AtomicU64, Ordering},
|
atomic::{AtomicU64, Ordering},
|
||||||
Arc,
|
Arc,
|
||||||
@ -151,49 +153,6 @@ struct MeshWorkerResponse {
|
|||||||
blocks_of_interest: BlocksOfInterest,
|
blocks_of_interest: BlocksOfInterest,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
/// Configuration data for an individual sprite model.
|
|
||||||
struct SpriteModelConfig<Model> {
|
|
||||||
/// Data for the .vox model associated with this sprite.
|
|
||||||
model: Model,
|
|
||||||
/// Sprite model center (as an offset from 0 in the .vox file).
|
|
||||||
offset: (f32, f32, f32),
|
|
||||||
/// LOD axes (how LOD gets applied along each axis, when we switch
|
|
||||||
/// to an LOD model).
|
|
||||||
lod_axes: (f32, f32, f32),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
/// Configuration data for a group of sprites (currently associated with a
|
|
||||||
/// particular SpriteKind).
|
|
||||||
struct SpriteConfig<Model> {
|
|
||||||
/// All possible model variations for this sprite.
|
|
||||||
// NOTE: Could make constant per sprite type, but eliminating this indirection and
|
|
||||||
// allocation is probably not that important considering how sprites are used.
|
|
||||||
variations: Vec<SpriteModelConfig<Model>>,
|
|
||||||
/// The extent to which the sprite sways in the window.
|
|
||||||
///
|
|
||||||
/// 0.0 is normal.
|
|
||||||
wind_sway: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: reduce llvm IR lines from this
|
|
||||||
/// Configuration data for all sprite models.
|
|
||||||
///
|
|
||||||
/// NOTE: Model is an asset path to the appropriate sprite .vox model.
|
|
||||||
// TODO: Overhaul this entirely to work with the new sprite attribute system. We'll probably be
|
|
||||||
// wanting a way to specify inexact mappings between sprite models and sprite configurations. For
|
|
||||||
// example, the ability to use a model for a range of plant growth states.
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
#[serde(try_from = "HashMap<SpriteKind, Option<SpriteConfig<String>>>")]
|
|
||||||
pub struct SpriteSpec(HashMap<SpriteKind, Option<SpriteConfig<String>>>);
|
|
||||||
|
|
||||||
impl SpriteSpec {
|
|
||||||
fn get(&self, kind: SpriteKind) -> Option<&SpriteConfig<String>> {
|
|
||||||
self.0.get(&kind).and_then(Option::as_ref)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Conversion of SpriteSpec from a hashmap failed because some sprites were
|
/// Conversion of SpriteSpec from a hashmap failed because some sprites were
|
||||||
/// missing.
|
/// missing.
|
||||||
struct SpritesMissing(Vec<SpriteKind>);
|
struct SpritesMissing(Vec<SpriteKind>);
|
||||||
@ -210,42 +169,6 @@ impl fmt::Display for SpritesMissing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here we ensure all variants have an entry in the config.
|
|
||||||
impl TryFrom<HashMap<SpriteKind, Option<SpriteConfig<String>>>> for SpriteSpec {
|
|
||||||
type Error = SpritesMissing;
|
|
||||||
|
|
||||||
fn try_from(
|
|
||||||
map: HashMap<SpriteKind, Option<SpriteConfig<String>>>,
|
|
||||||
) -> Result<Self, Self::Error> {
|
|
||||||
Ok(Self(map))
|
|
||||||
|
|
||||||
/*
|
|
||||||
let mut array = [(); 65536].map(|()| None);
|
|
||||||
let sprites_missing = SpriteKind::iter()
|
|
||||||
.filter(|kind| match map.remove(kind) {
|
|
||||||
Some(config) => {
|
|
||||||
array[*kind as usize] = config;
|
|
||||||
false
|
|
||||||
},
|
|
||||||
None => true,
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
if sprites_missing.is_empty() {
|
|
||||||
Ok(Self(array))
|
|
||||||
} else {
|
|
||||||
Err(SpritesMissing(sprites_missing))
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl assets::Asset for SpriteSpec {
|
|
||||||
type Loader = assets::RonLoader;
|
|
||||||
|
|
||||||
const EXTENSION: &'static str = "ron";
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_sprite_instances<'a, I: 'a>(
|
pub fn get_sprite_instances<'a, I: 'a>(
|
||||||
lod_levels: &'a mut [I; SPRITE_LOD_LEVELS],
|
lod_levels: &'a mut [I; SPRITE_LOD_LEVELS],
|
||||||
set_instance: impl Fn(&mut I, SpriteInstance, Vec3<i32>),
|
set_instance: impl Fn(&mut I, SpriteInstance, Vec3<i32>),
|
||||||
@ -253,7 +176,7 @@ pub fn get_sprite_instances<'a, I: 'a>(
|
|||||||
mut to_wpos: impl FnMut(Vec3<f32>) -> Vec3<i32>,
|
mut to_wpos: impl FnMut(Vec3<f32>) -> Vec3<i32>,
|
||||||
mut light_map: impl FnMut(Vec3<i32>) -> f32,
|
mut light_map: impl FnMut(Vec3<i32>) -> f32,
|
||||||
mut glow_map: impl FnMut(Vec3<i32>) -> f32,
|
mut glow_map: impl FnMut(Vec3<i32>) -> f32,
|
||||||
sprite_data: &HashMap<(SpriteKind, usize), [SpriteData; SPRITE_LOD_LEVELS]>,
|
sprite_data: &HashMap<(SpriteKind, usize, usize), [SpriteData; SPRITE_LOD_LEVELS]>,
|
||||||
sprite_config: &SpriteSpec,
|
sprite_config: &SpriteSpec,
|
||||||
) {
|
) {
|
||||||
prof_span!("extract sprite_instances");
|
prof_span!("extract sprite_instances");
|
||||||
@ -262,7 +185,7 @@ pub fn get_sprite_instances<'a, I: 'a>(
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(cfg) = sprite_config.get(sprite) else {
|
let Some((cfg_i, cfg)) = sprite_config.get_for_block(&block) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -287,7 +210,7 @@ pub fn get_sprite_instances<'a, I: 'a>(
|
|||||||
4 => (((seed.overflowing_add(wpos.x as u64).0) as usize % 7) + 1) / 2,
|
4 => (((seed.overflowing_add(wpos.x as u64).0) as usize % 7) + 1) / 2,
|
||||||
_ => seed as usize % cfg.variations.len(),
|
_ => seed as usize % cfg.variations.len(),
|
||||||
};
|
};
|
||||||
let key = (sprite, variation);
|
let key = (sprite, variation, cfg_i);
|
||||||
|
|
||||||
// NOTE: Safe because we called sprite_config_for already.
|
// NOTE: Safe because we called sprite_config_for already.
|
||||||
// NOTE: Safe because 0 ≤ ori < 8
|
// NOTE: Safe because 0 ≤ ori < 8
|
||||||
@ -340,7 +263,7 @@ fn mesh_worker(
|
|||||||
max_texture_size: u16,
|
max_texture_size: u16,
|
||||||
chunk: Arc<TerrainChunk>,
|
chunk: Arc<TerrainChunk>,
|
||||||
range: Aabb<i32>,
|
range: Aabb<i32>,
|
||||||
sprite_data: &HashMap<(SpriteKind, usize), [SpriteData; SPRITE_LOD_LEVELS]>,
|
sprite_data: &HashMap<(SpriteKind, usize, usize), [SpriteData; SPRITE_LOD_LEVELS]>,
|
||||||
sprite_config: &SpriteSpec,
|
sprite_config: &SpriteSpec,
|
||||||
) -> MeshWorkerResponse {
|
) -> MeshWorkerResponse {
|
||||||
span!(_guard, "mesh_worker");
|
span!(_guard, "mesh_worker");
|
||||||
@ -539,7 +462,7 @@ pub struct SpriteRenderState {
|
|||||||
/// value would break something
|
/// value would break something
|
||||||
pub sprite_config: Arc<SpriteSpec>,
|
pub sprite_config: Arc<SpriteSpec>,
|
||||||
// Maps sprite kind + variant to data detailing how to render it
|
// Maps sprite kind + variant to data detailing how to render it
|
||||||
pub sprite_data: Arc<HashMap<(SpriteKind, usize), [SpriteData; SPRITE_LOD_LEVELS]>>,
|
pub sprite_data: Arc<HashMap<(SpriteKind, usize, usize), [SpriteData; SPRITE_LOD_LEVELS]>>,
|
||||||
pub sprite_atlas_textures: Arc<AtlasTextures<pipelines::sprite::Locals, FigureSpriteAtlasData>>,
|
pub sprite_atlas_textures: Arc<AtlasTextures<pipelines::sprite::Locals, FigureSpriteAtlasData>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,7 +480,7 @@ impl SpriteRenderContext {
|
|||||||
|
|
||||||
struct SpriteWorkerResponse {
|
struct SpriteWorkerResponse {
|
||||||
sprite_config: Arc<SpriteSpec>,
|
sprite_config: Arc<SpriteSpec>,
|
||||||
sprite_data: HashMap<(SpriteKind, usize), [SpriteData; SPRITE_LOD_LEVELS]>,
|
sprite_data: HashMap<(SpriteKind, usize, usize), [SpriteData; SPRITE_LOD_LEVELS]>,
|
||||||
sprite_atlas_texture_data: FigureSpriteAtlasData,
|
sprite_atlas_texture_data: FigureSpriteAtlasData,
|
||||||
sprite_atlas_size: Vec2<u16>,
|
sprite_atlas_size: Vec2<u16>,
|
||||||
sprite_mesh: Mesh<SpriteVertex>,
|
sprite_mesh: Mesh<SpriteVertex>,
|
||||||
@ -576,9 +499,14 @@ impl SpriteRenderContext {
|
|||||||
);
|
);
|
||||||
let mut sprite_mesh = Mesh::new();
|
let mut sprite_mesh = Mesh::new();
|
||||||
// NOTE: Tracks the start vertex of the next model to be meshed.
|
// NOTE: Tracks the start vertex of the next model to be meshed.
|
||||||
let sprite_data: HashMap<(SpriteKind, usize), _> = SpriteKind::iter()
|
let sprite_data: HashMap<(SpriteKind, usize, usize), _> = SpriteKind::iter()
|
||||||
.filter_map(|kind| Some((kind, sprite_config.get(kind)?)))
|
.flat_map(|kind| {
|
||||||
.flat_map(|(kind, sprite_config)| {
|
sprite_config
|
||||||
|
.get(kind)
|
||||||
|
.enumerate()
|
||||||
|
.map(move |(i, (v, _))| (kind, v, i))
|
||||||
|
})
|
||||||
|
.flat_map(|(kind, sprite_config, filter_variant)| {
|
||||||
sprite_config.variations.iter().enumerate().map(
|
sprite_config.variations.iter().enumerate().map(
|
||||||
move |(
|
move |(
|
||||||
variation,
|
variation,
|
||||||
@ -661,7 +589,7 @@ impl SpriteRenderContext {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
((kind, variation), lod_sprite_data)
|
((kind, variation, filter_variant), lod_sprite_data)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
150
voxygen/src/scene/terrain/sprite.rs
Normal file
150
voxygen/src/scene/terrain/sprite.rs
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
use common::{
|
||||||
|
assets,
|
||||||
|
terrain::{sprite, Block, SpriteKind},
|
||||||
|
};
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
/// Configuration data for an individual sprite model.
|
||||||
|
pub struct SpriteModelConfig<Model> {
|
||||||
|
/// Data for the .vox model associated with this sprite.
|
||||||
|
pub model: Model,
|
||||||
|
/// Sprite model center (as an offset from 0 in the .vox file).
|
||||||
|
pub offset: (f32, f32, f32),
|
||||||
|
/// LOD axes (how LOD gets applied along each axis, when we switch
|
||||||
|
/// to an LOD model).
|
||||||
|
pub lod_axes: (f32, f32, f32),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
/// Configuration data for a group of sprites (currently associated with a
|
||||||
|
/// particular SpriteKind).
|
||||||
|
pub struct SpriteConfig<Model> {
|
||||||
|
/// All possible model variations for this sprite.
|
||||||
|
// NOTE: Could make constant per sprite type, but eliminating this indirection and
|
||||||
|
// allocation is probably not that important considering how sprites are used.
|
||||||
|
pub variations: Vec<SpriteModelConfig<Model>>,
|
||||||
|
/// The extent to which the sprite sways in the window.
|
||||||
|
///
|
||||||
|
/// 0.0 is normal.
|
||||||
|
pub wind_sway: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: reduce llvm IR lines from this
|
||||||
|
/// Configuration data for all sprite models.
|
||||||
|
///
|
||||||
|
/// NOTE: Model is an asset path to the appropriate sprite .vox model.
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct SpriteSpec(HashMap<(SpriteKind, SpriteAttributeFilters), Option<SpriteConfig<String>>>);
|
||||||
|
|
||||||
|
macro_rules! impl_sprite_attribute_filter {
|
||||||
|
(
|
||||||
|
$(#[$meta:meta])*
|
||||||
|
$vis:vis struct $n:ident {
|
||||||
|
$($attr:ident $field_name:ident = |$filter_arg:ident: $filter_ty:ty, $value_arg:ident| $filter:block),+
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
$(#[$meta])*
|
||||||
|
$vis struct $n {
|
||||||
|
$(
|
||||||
|
pub $field_name: Option<$filter_ty>,
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $n {
|
||||||
|
fn sprite_attribute_score(&self, block: &Block) -> Option<usize> {
|
||||||
|
if $(
|
||||||
|
self.$field_name.as_ref().map_or(true, |$filter_arg| {
|
||||||
|
block
|
||||||
|
.get_attr::<sprite::$attr>()
|
||||||
|
.map_or(false, |$value_arg| $filter)
|
||||||
|
})
|
||||||
|
)&&+ {
|
||||||
|
Some(
|
||||||
|
[$(self.$field_name.is_some()),+]
|
||||||
|
.into_iter()
|
||||||
|
.filter(|o| *o)
|
||||||
|
.count(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn is_valid_for_category(&self, category: sprite::Category) -> Result<(), &'static str> {
|
||||||
|
$(if self.$field_name.is_some() && !category.has_attr::<sprite::$attr>() {
|
||||||
|
return Err(::std::any::type_name::<sprite::$attr>());
|
||||||
|
})*
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_sprite_attribute_filter!(
|
||||||
|
#[derive(Debug, Clone, Deserialize, Default, PartialEq, Eq, Hash)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct SpriteAttributeFilters {
|
||||||
|
Growth growth_stage = |filter: Range<u8>, growth| { filter.contains(&growth.0) },
|
||||||
|
LightEnabled light_enabled = |filter: bool, light_enabled| { *filter == light_enabled.0 }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
impl assets::Asset for SpriteSpec {
|
||||||
|
type Loader = assets::RonLoader;
|
||||||
|
|
||||||
|
const EXTENSION: &'static str = "ron";
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpriteSpec {
|
||||||
|
pub fn get(
|
||||||
|
&self,
|
||||||
|
kind: SpriteKind,
|
||||||
|
) -> impl Iterator<Item = (&SpriteConfig<String>, &SpriteAttributeFilters)> + '_ {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.filter_map(move |((sprite_kind, filters), v)| {
|
||||||
|
(*sprite_kind == kind).then_some((v.as_ref()?, filters))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_for_block(&self, block: &Block) -> Option<(usize, &SpriteConfig<String>)> {
|
||||||
|
let sprite = block.get_sprite()?;
|
||||||
|
|
||||||
|
self.get(sprite)
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(cfg_i, (cfg, filter))| {
|
||||||
|
Some((cfg_i, cfg, filter.sprite_attribute_score(block)?))
|
||||||
|
})
|
||||||
|
.max_by_key(|(_, _, score)| *score)
|
||||||
|
.map(|(cfg_i, cfg, _)| (cfg_i, cfg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use common_assets::AssetExt;
|
||||||
|
|
||||||
|
use super::SpriteSpec;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sprite_spec_valid() {
|
||||||
|
let spec = SpriteSpec::load_expect("voxygen.voxel.sprite_manifest").read();
|
||||||
|
|
||||||
|
for (sprite, filter) in spec.0.keys() {
|
||||||
|
if let Err(invalid_attribute) = filter.is_valid_for_category(sprite.category()) {
|
||||||
|
panic!(
|
||||||
|
"Sprite category '{:?}' does not have attribute '{}' (in sprite config for \
|
||||||
|
{:?})",
|
||||||
|
sprite.category(),
|
||||||
|
invalid_attribute,
|
||||||
|
sprite,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user