From 4a89d88e9bfe9aedf9306a9cdd54dfbba18071ac Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sun, 21 Jan 2024 19:51:37 +0000 Subject: [PATCH] Added default sprite attribute state --- common/src/terrain/block.rs | 63 +++++++++---------- common/src/terrain/mod.rs | 2 +- common/src/terrain/sprite.rs | 18 ++++-- common/src/terrain/sprite/magic.rs | 19 ++++-- voxygen/src/mesh/terrain.rs | 2 +- voxygen/src/scene/terrain/watcher.rs | 4 +- world/src/layer/mod.rs | 2 +- .../settlement/building/archetype/keep.rs | 2 +- 8 files changed, 64 insertions(+), 48 deletions(-) diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 0a8b89e940..d367effb9a 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -156,50 +156,44 @@ impl Block { /* Constructors */ - // TODO: Rename to `filled` #[inline] - pub const fn new(kind: BlockKind, color: Rgb) -> Self { - // TODO: we should probably assert this, overwriting the data fields with a - // colour is bad news - /* - #[cfg(debug_assertions)] - assert!(kind.is_filled()); - */ + pub(super) const fn from_raw(kind: BlockKind, data: [u8; 3]) -> Self { Self { kind, data } } - Self { - kind, - // Colours are only valid for non-fluids - data: if kind.is_filled() { - [color.r, color.g, color.b] - } else { - [0; 3] - }, + // TODO: Rename to `filled`, make caller guarantees stronger + #[inline] + #[track_caller] + pub const fn new(kind: BlockKind, color: Rgb) -> Self { + if kind.is_filled() { + Self::from_raw(kind, [color.r, color.g, color.b]) + } else { + // Works because `SpriteKind::Empty` has no attributes + let data = (SpriteKind::Empty as u32).to_be_bytes(); + Self::from_raw(kind, [data[1], data[2], data[3]]) } } // Only valid if `block_kind` is unfilled, so this is just a private utility // method #[inline] - const fn unfilled(kind: BlockKind, sprite: SpriteKind) -> Self { + pub fn unfilled(kind: BlockKind, sprite: SpriteKind) -> Self { #[cfg(debug_assertions)] assert!(!kind.is_filled()); - let sprite_bytes = (sprite as u32).to_be_bytes(); - - Self { - kind, - data: [sprite_bytes[1], sprite_bytes[2], sprite_bytes[3]], - } + Self::from_raw(kind, sprite.to_initial_bytes()) } #[inline] - pub const fn air(sprite: SpriteKind) -> Self { Self::unfilled(BlockKind::Air, sprite) } + pub fn air(sprite: SpriteKind) -> Self { Self::unfilled(BlockKind::Air, sprite) } #[inline] - pub const fn empty() -> Self { Self::air(SpriteKind::Empty) } + pub const fn empty() -> Self { + // Works because `SpriteKind::Empty` has no attributes + let data = (SpriteKind::Empty as u32).to_be_bytes(); + Self::from_raw(BlockKind::Air, [data[1], data[2], data[3]]) + } #[inline] - pub const fn water(sprite: SpriteKind) -> Self { Self::unfilled(BlockKind::Water, sprite) } + pub fn water(sprite: SpriteKind) -> Self { Self::unfilled(BlockKind::Water, sprite) } /* Sprite decoding */ @@ -258,6 +252,9 @@ impl Block { } } + #[inline(always)] + pub(super) const fn data(&self) -> [u8; 3] { self.data } + #[inline(always)] pub(super) const fn with_data(mut self, data: [u8; 3]) -> Self { self.data = data; @@ -428,12 +425,12 @@ impl Block { }; if self - .get_attr::() - .map_or(false, |l| l.0) + .get_attr::() + .map_or(true, |l| l.0) { - None - } else { Some(glow_level) + } else { + None } } @@ -636,7 +633,7 @@ impl Block { /// Apply a light toggle to this block, if possible pub fn with_toggle_light(self, enable: bool) -> Option { - self.with_attr(sprite::LightDisabled(!enable)).ok() + self.with_attr(sprite::LightEnabled(enable)).ok() } #[inline] @@ -662,7 +659,7 @@ impl Block { #[must_use] pub fn into_vacant(self) -> Self { if self.is_fluid() { - Block::new(self.kind(), Rgb::zero()) + Block::unfilled(self.kind(), SpriteKind::Empty) } else { // FIXME: Figure out if there's some sensible way to determine what medium to // replace a filled block with if it's removed. @@ -698,7 +695,7 @@ mod tests { #[test] fn convert_u32() { for bk in BlockKind::iter() { - let block = Block::new(bk, Rgb::new(165, 90, 204)); // Pretty unique bit patterns + let block = Block::from_raw(bk, [165, 90, 204]); // Pretty unique bit patterns if bk.is_filled() { assert_eq!(Block::from_u32(block.to_u32()), Some(block)); } else { diff --git a/common/src/terrain/mod.rs b/common/src/terrain/mod.rs index 93ec4833f2..5e6e0fd8e6 100644 --- a/common/src/terrain/mod.rs +++ b/common/src/terrain/mod.rs @@ -286,7 +286,7 @@ impl TerrainChunk { pub fn water(sea_level: i32) -> TerrainChunk { TerrainChunk::new( sea_level, - Block::new(BlockKind::Water, Rgb::zero()), + Block::water(SpriteKind::Empty), Block::air(SpriteKind::Empty), TerrainChunkMeta::void(), ) diff --git a/common/src/terrain/sprite.rs b/common/src/terrain/sprite.rs index 5b5f265444..1e574af545 100644 --- a/common/src/terrain/sprite.rs +++ b/common/src/terrain/sprite.rs @@ -349,7 +349,7 @@ sprites! { SeaDecorPillar = 0x1E, MagicalSeal = 0x1F, }, - Lamp = 7 has Ori, LightDisabled { + Lamp = 7 has Ori, LightEnabled { // Standalone lights Lantern = 0, StreetLamp = 1, @@ -362,20 +362,28 @@ sprites! { attributes! { Ori { bits: 4, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Ori(x)| x as u16 }, Growth { bits: 4, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Growth(x)| x as u16 }, - LightDisabled { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |LightDisabled(x)| x as u16 }, + LightEnabled { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |LightEnabled(x)| x as u16 }, } // The orientation of the sprite, 0..16 -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] pub struct Ori(pub u8); // The growth of the plant, 0..16 #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Growth(pub u8); -// Whether a light has been toggled off. +impl Default for Growth { + fn default() -> Self { Self(15) } +} + +// Whether a light has been toggled on or off. #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct LightDisabled(pub bool); +pub struct LightEnabled(pub bool); + +impl Default for LightEnabled { + fn default() -> Self { Self(true) } +} impl SpriteKind { #[inline] diff --git a/common/src/terrain/sprite/magic.rs b/common/src/terrain/sprite/magic.rs index 91729d97b5..9009cac475 100644 --- a/common/src/terrain/sprite/magic.rs +++ b/common/src/terrain/sprite/magic.rs @@ -158,6 +158,15 @@ macro_rules! sprites { },)* } } + + #[inline] pub(super) fn to_initial_bytes(self) -> [u8; 3] { + let sprite_bytes = (*self as u32).to_be_bytes(); + let block = Block::from_raw(super::BlockKind::Air, [sprite_bytes[1], sprite_bytes[2], sprite_bytes[3]]); + match self.category() { + $(Category::$category_name => block$($(.with_attr($attr::default()).unwrap())*)?,)* + } + .data() + } } }; } @@ -170,12 +179,14 @@ pub enum AttributeError { Attribute(E), } -pub trait Attribute: Sized { - /// The unique index assigned to this attribute, used to index offset arrays +pub trait Attribute: Default + Sized { + /// The unique index assigned to this attribute, used to index offset + /// arrays. const INDEX: usize; - /// The number of bits required to represent this attribute + /// The number of bits required to represent this attribute. const BITS: u8; - type Error; + /// The error that might occur when decoding the attribute from bits. + type Error: core::fmt::Debug; fn from_bits(bits: u16) -> Result; fn into_bits(self) -> u16; } diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index c571227102..5c715f2d61 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -294,7 +294,7 @@ pub fn generate_mesh<'a>( let d = d + 2; let flat = { let mut volume = vol.cached(); - const AIR: Block = Block::air(common::terrain::sprite::SpriteKind::Empty); + const AIR: Block = Block::empty(); // TODO: Once we can manage it sensibly, consider using something like // Option instead of just assuming air. let mut flat = vec![AIR; (w * h * d) as usize]; diff --git a/voxygen/src/scene/terrain/watcher.rs b/voxygen/src/scene/terrain/watcher.rs index 1b018ce016..9c0ba54f01 100644 --- a/voxygen/src/scene/terrain/watcher.rs +++ b/voxygen/src/scene/terrain/watcher.rs @@ -142,8 +142,8 @@ impl BlocksOfInterest { _ => { if let Some(sprite) = block.get_sprite() { if sprite.category() == sprite::Category::Lamp { - if let Ok(sprite::LightDisabled(disabled)) = block.get_attr() { - interactables.push((pos, Interaction::LightToggle(disabled))); + if let Ok(sprite::LightEnabled(enabled)) = block.get_attr() { + interactables.push((pos, Interaction::LightToggle(!enabled))); } } diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 9798dde111..171cdf396e 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -47,7 +47,7 @@ pub struct Colors { pub vein: (u8, u8, u8), } -const EMPTY_AIR: Block = Block::air(SpriteKind::Empty); +const EMPTY_AIR: Block = Block::empty(); pub struct PathLocals { pub riverless_alt: f32, diff --git a/world/src/site/settlement/building/archetype/keep.rs b/world/src/site/settlement/building/archetype/keep.rs index 530dd288fe..486f807daa 100644 --- a/world/src/site/settlement/building/archetype/keep.rs +++ b/world/src/site/settlement/building/archetype/keep.rs @@ -167,7 +167,7 @@ impl Archetype for Keep { make_block(colors.pole.0, colors.pole.1, colors.pole.2).with_priority(important_layer); let flag = make_block(flag_color.0, flag_color.1, flag_color.2).with_priority(important_layer); - const AIR: Block = Block::air(SpriteKind::Empty); + const AIR: Block = Block::empty(); const EMPTY: BlockMask = BlockMask::nothing(); let internal = BlockMask::new(AIR, internal_layer);