From d292234c475b2a15dd52f0ed0cf6ccdd5acbbddc Mon Sep 17 00:00:00 2001 From: Isse Date: Wed, 22 Feb 2023 10:11:57 +0100 Subject: [PATCH] lod for sprites on volumes --- common/src/comp/body/ship.rs | 8 +- common/src/figure/mod.rs | 25 +++--- common/src/states/behavior.rs | 7 +- common/src/states/mod.rs | 2 +- common/src/states/mount_sprite.rs | 23 +++-- common/src/states/utils.rs | 109 +++++++++++++++--------- common/src/terrain/block.rs | 16 ++-- common/src/terrain/sprite.rs | 45 ++++++---- common/src/terrain/structure.rs | 17 ++-- voxygen/src/hud/mod.rs | 3 +- voxygen/src/mesh/segment.rs | 2 +- voxygen/src/render/pipelines/terrain.rs | 7 +- voxygen/src/scene/figure/cache.rs | 37 ++++---- voxygen/src/scene/figure/load.rs | 14 +-- voxygen/src/scene/figure/mod.rs | 68 +++++++++------ voxygen/src/scene/mod.rs | 2 +- voxygen/src/scene/terrain.rs | 28 +++--- voxygen/src/session/mod.rs | 3 +- 18 files changed, 254 insertions(+), 162 deletions(-) diff --git a/common/src/comp/body/ship.rs b/common/src/comp/body/ship.rs index 0b61755aec..5629ed6741 100644 --- a/common/src/comp/body/ship.rs +++ b/common/src/comp/body/ship.rs @@ -173,13 +173,13 @@ pub mod figuredata { impl DeBlock { fn to_block(&self, color: Rgb) -> Block { - match self { - &DeBlock::Block(block) => Block::new(block, color), - &DeBlock::Air(sprite, ori) => { + match *self { + DeBlock::Block(block) => Block::new(block, color), + DeBlock::Air(sprite, ori) => { let block = Block::new(BlockKind::Air, color).with_sprite(sprite); block.with_ori(ori).unwrap_or(block) }, - &DeBlock::Water(sprite, ori) => { + DeBlock::Water(sprite, ori) => { let block = Block::new(BlockKind::Water, color).with_sprite(sprite); block.with_ori(ori).unwrap_or(block) }, diff --git a/common/src/figure/mod.rs b/common/src/figure/mod.rs index f9de47f922..8de36d2ce6 100644 --- a/common/src/figure/mod.rs +++ b/common/src/figure/mod.rs @@ -9,8 +9,9 @@ pub use self::{ }; use crate::{ + terrain::{Block, BlockKind}, vol::{IntoFullPosIterator, IntoFullVolIterator, ReadVol, SizedVol, Vox, WriteVol}, - volumes::dyna::Dyna, terrain::{Block, BlockKind}, + volumes::dyna::Dyna, }; use dot_vox::DotVoxData; use vek::*; @@ -19,19 +20,17 @@ pub type TerrainSegment = Dyna; impl From for TerrainSegment { fn from(value: Segment) -> Self { - TerrainSegment::from_fn(value.sz, (), |pos| { - match value.get(pos) { - Err(_) | Ok(Cell::Empty) => Block::empty(), - Ok(cell) => { - if cell.is_hollow() { - Block::empty() - } else if cell.is_glowy() { - Block::new(BlockKind::GlowingRock, cell.get_color().unwrap()) - } else { - Block::new(BlockKind::Misc, cell.get_color().unwrap()) - } + TerrainSegment::from_fn(value.sz, (), |pos| match value.get(pos) { + Err(_) | Ok(Cell::Empty) => Block::empty(), + Ok(cell) => { + if cell.is_hollow() { + Block::empty() + } else if cell.is_glowy() { + Block::new(BlockKind::GlowingRock, cell.get_color().unwrap()) + } else { + Block::new(BlockKind::Misc, cell.get_color().unwrap()) } - } + }, }) } } diff --git a/common/src/states/behavior.rs b/common/src/states/behavior.rs index edae6eed80..1ae00b204d 100644 --- a/common/src/states/behavior.rs +++ b/common/src/states/behavior.rs @@ -59,7 +59,12 @@ pub trait CharacterBehavior { fn talk(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate { StateUpdate::from(data) } - fn mount_sprite(&self, data: &JoinData, _output_events: &mut OutputEvents, _pos: Vec3) -> StateUpdate { + fn mount_sprite( + &self, + data: &JoinData, + _output_events: &mut OutputEvents, + _pos: Vec3, + ) -> StateUpdate { StateUpdate::from(data) } // start_input has custom implementation in the following character states that diff --git a/common/src/states/mod.rs b/common/src/states/mod.rs index e9f610c3b1..2cfe806c0b 100644 --- a/common/src/states/mod.rs +++ b/common/src/states/mod.rs @@ -22,6 +22,7 @@ pub mod glide_wield; pub mod idle; pub mod leap_melee; pub mod leap_shockwave; +pub mod mount_sprite; pub mod music; pub mod rapid_melee; pub mod repeater_ranged; @@ -40,4 +41,3 @@ pub mod use_item; pub mod utils; pub mod wallrun; pub mod wielding; -pub mod mount_sprite; diff --git a/common/src/states/mount_sprite.rs b/common/src/states/mount_sprite.rs index 54b02d425d..bbf885423a 100644 --- a/common/src/states/mount_sprite.rs +++ b/common/src/states/mount_sprite.rs @@ -1,9 +1,16 @@ -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; use vek::Vec3; -use crate::{comp::{character_state::OutputEvents, StateUpdate, CharacterState}, util::Dir}; +use crate::{ + comp::{character_state::OutputEvents, CharacterState, StateUpdate}, + util::Dir, +}; -use super::{behavior::{CharacterBehavior, JoinData}, utils::{handle_orientation, end_ability, handle_wield}, idle}; +use super::{ + behavior::{CharacterBehavior, JoinData}, + idle, + utils::{end_ability, handle_orientation, handle_wield}, +}; #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub struct StaticData { @@ -13,7 +20,6 @@ pub struct StaticData { pub sprite_pos: Vec3, } - #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub struct Data { /// Struct containing data that does not change over the course of the @@ -27,7 +33,12 @@ impl CharacterBehavior for Data { update.pos.0 = self.static_data.mount_pos; update.vel.0 = Vec3::zero(); - handle_orientation(data, &mut update, 1.0, Some(Dir::new(self.static_data.mount_dir))); + handle_orientation( + data, + &mut update, + 1.0, + Some(Dir::new(self.static_data.mount_dir)), + ); handle_wield(data, &mut update); @@ -46,4 +57,4 @@ impl CharacterBehavior for Data { update } -} \ No newline at end of file +} diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 4ae31e270c..ae240e4fdf 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -21,7 +21,7 @@ use crate::{ event::{LocalEvent, ServerEvent}, outcome::Outcome, states::{behavior::JoinData, utils::CharacterState::Idle, *}, - terrain::{TerrainGrid, UnlockKind, SpriteKind}, + terrain::{SpriteKind, TerrainGrid, UnlockKind}, util::Dir, vol::ReadVol, }; @@ -785,26 +785,40 @@ pub fn attempt_sit(data: &JoinData<'_>, update: &mut StateUpdate) { } } -pub fn sprite_mount_points(sprite_kind: SpriteKind, pos: Vec3, ori: u8) -> impl ExactSizeIterator, Vec3)> { +pub fn sprite_mount_points( + sprite_kind: SpriteKind, + pos: Vec3, + ori: u8, +) -> impl ExactSizeIterator, Vec3)> { let mat = Mat4::identity() - .rotated_z(std::f32::consts::PI * 0.25 * ori as f32) - .translated_3d( - pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0) - ); - sprite_kind.mount_offsets().iter().map(move |(pos, dir)| { - ((mat * pos.with_w(1.0)).xyz(), (mat * dir.with_w(0.0)).xyz()) - }) -} + .rotated_z(std::f32::consts::PI * 0.25 * ori as f32) + .translated_3d(pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0)); + sprite_kind + .mount_offsets() + .iter() + .map(move |(pos, dir)| ((mat * pos.with_w(1.0)).xyz(), (mat * dir.with_w(0.0)).xyz())) +} const SPRITE_MOUNT_RANGE: f32 = 2.0; pub const SPRITE_MOUNT_RANGE_SQR: f32 = SPRITE_MOUNT_RANGE * SPRITE_MOUNT_RANGE; pub fn attempt_mount_sprite(data: &JoinData<'_>, update: &mut StateUpdate, pos: Vec3) { - if let Some((kind, ori)) = data.terrain.get(pos).ok().and_then(|block| block.get_sprite().zip(block.get_ori())) { - if let Some((mount_pos, mount_dir)) = sprite_mount_points(kind, pos, ori).min_by_key(|(pos, _)| { - OrderedFloat(data.pos.0.distance_squared(*pos)) - }) { - if reach_block(data.pos.0, pos, SPRITE_MOUNT_RANGE_SQR, data.body, data.terrain) { + if let Some((kind, ori)) = data + .terrain + .get(pos) + .ok() + .and_then(|block| block.get_sprite().zip(block.get_ori())) + { + if let Some((mount_pos, mount_dir)) = sprite_mount_points(kind, pos, ori) + .min_by_key(|(pos, _)| OrderedFloat(data.pos.0.distance_squared(*pos))) + { + if reach_block( + data.pos.0, + pos, + SPRITE_MOUNT_RANGE_SQR, + data.body, + data.terrain, + ) { update.character = CharacterState::MountSprite(mount_sprite::Data { static_data: mount_sprite::StaticData { mount_pos, @@ -887,13 +901,18 @@ pub fn attempt_swap_equipped_weapons(data: &JoinData<'_>, update: &mut StateUpda } } -fn reach_block(player_pos: Vec3, block_pos: Vec3, range: f32, body: &Body, terrain: &TerrainGrid) -> bool { +fn reach_block( + player_pos: Vec3, + block_pos: Vec3, + range: f32, + body: &Body, + terrain: &TerrainGrid, +) -> bool { let block_pos_f32 = block_pos.map(|x| x as f32 + 0.5); // Closure to check if distance between a point and the block is less than // MAX_PICKUP_RANGE and the radius of the body let block_range_check = |pos: Vec3| { - (block_pos_f32 - pos).magnitude_squared() - < (range + body.max_radius()).powi(2) + (block_pos_f32 - pos).magnitude_squared() < (range + body.max_radius()).powi(2) }; // Checks if player's feet or head is near to block @@ -903,12 +922,10 @@ fn reach_block(player_pos: Vec3, block_pos: Vec3, range: f32, body: &B // Do a check that a path can be found between sprite and entity // interacting with sprite Use manhattan distance * 1.5 for number // of iterations - let iters = - (3.0 * (block_pos_f32 - player_pos).map(|x| x.abs()).sum()) as usize; + let iters = (3.0 * (block_pos_f32 - player_pos).map(|x| x.abs()).sum()) as usize; // Heuristic compares manhattan distance of start and end pos - let heuristic = move |pos: &Vec3, _: &Vec3| { - (block_pos - pos).map(|x| x.abs()).sum() as f32 - }; + let heuristic = + move |pos: &Vec3, _: &Vec3| (block_pos - pos).map(|x| x.abs()).sum() as f32; let mut astar = Astar::new( iters, @@ -994,18 +1011,27 @@ pub fn handle_manipulate_loadout( }, InventoryAction::Collect(sprite_pos) => { // First, get sprite data for position, if there is a sprite - let sprite_at_pos = data.terrain + let sprite_at_pos = data + .terrain .get(sprite_pos) .ok() .copied() .and_then(|b| b.get_sprite()); // Checks if position has a collectible sprite as well as what sprite is at the // position - let sprite_interact = sprite_at_pos.and_then(Option::::from); + let sprite_interact = + sprite_at_pos.and_then(Option::::from); if let Some(sprite_interact) = sprite_interact { - if reach_block(data.pos.0, sprite_pos, MAX_PICKUP_RANGE, data.body, data.terrain) { + if reach_block( + data.pos.0, + sprite_pos, + MAX_PICKUP_RANGE, + data.body, + data.terrain, + ) { let sprite_chunk_pos = TerrainGrid::chunk_offs(sprite_pos); - let sprite_cfg = data.terrain + let sprite_cfg = data + .terrain .pos_chunk(sprite_pos) .and_then(|chunk| chunk.meta().sprite_cfg_at(sprite_chunk_pos)); let required_item = @@ -1034,21 +1060,20 @@ pub fn handle_manipulate_loadout( let (buildup_duration, use_duration, recover_duration) = sprite_interact.durations(); - update.character = - CharacterState::SpriteInteract(sprite_interact::Data { - static_data: sprite_interact::StaticData { - buildup_duration, - use_duration, - recover_duration, - sprite_pos, - sprite_kind: sprite_interact, - was_wielded: data.character.is_wield(), - was_sneak: data.character.is_stealthy(), - required_item, - }, - timer: Duration::default(), - stage_section: StageSection::Buildup, - }) + update.character = CharacterState::SpriteInteract(sprite_interact::Data { + static_data: sprite_interact::StaticData { + buildup_duration, + use_duration, + recover_duration, + sprite_pos, + sprite_kind: sprite_interact, + was_wielded: data.character.is_wield(), + was_sneak: data.character.is_stealthy(), + required_item, + }, + timer: Duration::default(), + stage_section: StageSection::Buildup, + }) } else { output_events.emit_local(LocalEvent::CreateOutcome( Outcome::FailedSpriteUnlock { pos: sprite_pos }, diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 79e5fc3139..2bfb9daf6b 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -3,7 +3,8 @@ use crate::{ comp::{fluid_dynamics::LiquidKind, tool::ToolKind}, consts::FRIC_GROUND, lottery::LootSpec, - make_case_elim, rtsim, vol::Vox, + make_case_elim, rtsim, + vol::Vox, }; use num_derive::FromPrimitive; use num_traits::FromPrimitive; @@ -118,13 +119,9 @@ pub struct Block { } impl Vox for Block { - fn empty() -> Self { - Block::empty() - } + fn empty() -> Self { Block::empty() } - fn is_empty(&self) -> bool { - self.is_air() && self.get_sprite().is_none() - } + fn is_empty(&self) -> bool { self.is_air() && self.get_sprite().is_none() } } impl Deref for Block { @@ -431,10 +428,7 @@ impl Block { } #[inline] - pub fn is_mountable(&self) -> bool { - self.get_sprite() - .map_or(false, |s| s.is_mountable()) - } + pub fn is_mountable(&self) -> bool { self.get_sprite().map_or(false, |s| s.is_mountable()) } #[inline] pub fn is_bonkable(&self) -> bool { diff --git a/common/src/terrain/sprite.rs b/common/src/terrain/sprite.rs index c3c446a658..cb692073fc 100644 --- a/common/src/terrain/sprite.rs +++ b/common/src/terrain/sprite.rs @@ -484,29 +484,38 @@ impl SpriteKind { z: 0.0, }; match self { - SpriteKind::ChairSingle => &[(Vec3 { - x: 0.0, - y: 0.0, - z: 0.5, - }, UNIT_Y)], - SpriteKind::ChairDouble => &[(Vec3 { - x: -0.5, - y: 0.0, - z: 0.6, - }, UNIT_Y), - (Vec3 { - x: 0.5, - y: -0.1, - z: 0.6, - }, UNIT_Y)], + SpriteKind::ChairSingle => &[( + Vec3 { + x: 0.0, + y: 0.0, + z: 0.5, + }, + UNIT_Y, + )], + SpriteKind::ChairDouble => &[ + ( + Vec3 { + x: -0.5, + y: 0.0, + z: 0.6, + }, + UNIT_Y, + ), + ( + Vec3 { + x: 0.5, + y: -0.1, + z: 0.6, + }, + UNIT_Y, + ), + ], _ => &[], } } #[inline] - pub fn is_mountable(&self) -> bool { - !self.mount_offsets().is_empty() - } + pub fn is_mountable(&self) -> bool { !self.mount_offsets().is_empty() } /// Which tool (if any) is needed to collect this sprite? #[inline] diff --git a/common/src/terrain/structure.rs b/common/src/terrain/structure.rs index 3e74103fb1..1b10821f12 100644 --- a/common/src/terrain/structure.rs +++ b/common/src/terrain/structure.rs @@ -46,9 +46,7 @@ make_case_elim!( ); impl Default for StructureBlock { - fn default() -> Self { - StructureBlock::None - } + fn default() -> Self { StructureBlock::None } } #[derive(Debug)] @@ -86,7 +84,9 @@ impl assets::Compound for StructuresGroup { .0 .iter() .map(|sp| { - let base = cache.load::>>(&sp.specifier)?.cloned(); + let base = cache + .load::>>(&sp.specifier)? + .cloned(); Ok(Structure { center: Vec3::from(sp.center), base, @@ -145,7 +145,10 @@ impl ReadVol for Structure { } } -pub(crate) fn load_base_structure(dot_vox_data: &DotVoxData, mut to_block: impl FnMut(Rgb) -> B) -> BaseStructure { +pub(crate) fn load_base_structure( + dot_vox_data: &DotVoxData, + mut to_block: impl FnMut(Rgb) -> B, +) -> BaseStructure { let mut palette = std::array::from_fn(|_| B::default()); if let Some(model) = dot_vox_data.models.get(0) { for (i, col) in dot_vox_data @@ -184,7 +187,9 @@ impl assets::Compound for BaseStructure { let dot_vox_data = cache.load::(specifier)?.read(); let dot_vox_data = &dot_vox_data.0; - Ok(load_base_structure(dot_vox_data, |col| StructureBlock::Filled(BlockKind::Misc, col))) + Ok(load_base_structure(dot_vox_data, |col| { + StructureBlock::Filled(BlockKind::Misc, col) + })) } } diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 1387af642f..3338446461 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -71,9 +71,10 @@ use crate::{ render::UiDrawer, scene::camera::{self, Camera}, session::{ + interactable::{BlockInteraction, Interactable}, settings_change::{ Audio, Chat as ChatChange, Interface as InterfaceChange, SettingsChange, - }, interactable::{Interactable, BlockInteraction}, + }, }, settings::chat::ChatFilter, ui::{ diff --git a/voxygen/src/mesh/segment.rs b/voxygen/src/mesh/segment.rs index c200addc0a..b46b9d1856 100644 --- a/voxygen/src/mesh/segment.rs +++ b/voxygen/src/mesh/segment.rs @@ -177,7 +177,7 @@ where }; let get_opacity = |vol: &mut V, pos: Vec3| vol.get(pos).map_or(true, |vox| vox.is_fluid()); let should_draw = |vol: &mut V, pos: Vec3, delta: Vec3, _uv| { - super::terrain::should_draw_greedy(pos, delta, &|vox| { + super::terrain::should_draw_greedy(pos, delta, |vox| { vol.get(vox) .map(|vox| *vox) .unwrap_or_else(|_| Block::empty()) diff --git a/voxygen/src/render/pipelines/terrain.rs b/voxygen/src/render/pipelines/terrain.rs index fdcfe24475..6a0cddd601 100644 --- a/voxygen/src/render/pipelines/terrain.rs +++ b/voxygen/src/render/pipelines/terrain.rs @@ -147,7 +147,12 @@ pub struct Locals { } impl Locals { - pub fn new(model_offs: Vec3, ori: Quaternion, atlas_offs: Vec2, load_time: f32) -> Self { + pub fn new( + model_offs: Vec3, + ori: Quaternion, + atlas_offs: Vec2, + load_time: f32, + ) -> Self { let mat = Mat4::from(ori).translated_3d(model_offs); let mat_arr = mat.into_col_arrays(); diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index a0d6d622dc..fc8ca44bad 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -8,8 +8,9 @@ use crate::{ segment::{generate_mesh_base_vol_figure, generate_mesh_base_vol_terrain}, }, render::{ - pipelines::terrain::{BoundLocals as BoundTerrainLocals, Locals as TerrainLocals}, BoneMeshes, ColLightInfo, - FigureModel, Instances, Mesh, Renderer, SpriteInstance, TerrainVertex, + pipelines::terrain::{BoundLocals as BoundTerrainLocals, Locals as TerrainLocals}, + BoneMeshes, ColLightInfo, FigureModel, Instances, Mesh, Renderer, SpriteInstance, + TerrainVertex, }, scene::{ camera::CameraMode, @@ -36,7 +37,7 @@ use core::{hash::Hash, ops::Range}; use crossbeam_utils::atomic; use hashbrown::{hash_map::Entry, HashMap}; use serde::Deserialize; -use std::{sync::Arc, array::from_fn}; +use std::{array::from_fn, sync::Arc}; use vek::*; /// A type produced by mesh worker threads corresponding to the information @@ -321,16 +322,20 @@ where /// the model, we don't return skeleton data. pub fn get_model<'b>( &'b self, - // TODO: If we ever convert to using an atlas here, use this. + // TODO: If we ever convert to using an atlas here, use this. _col_lights: &super::FigureColLights, body: Skel::Body, inventory: Option<&Inventory>, - // TODO: Consider updating the tick by putting it in a Cell. + // TODO: Consider updating the tick by putting it in a Cell. _tick: u64, camera_mode: CameraMode, character_state: Option<&CharacterState>, item_key: Option, - ) -> Option<&<::ModelEntryFuture as ModelEntryFuture>::ModelEntry>{ + ) -> Option< + &'b <::ModelEntryFuture as ModelEntryFuture< + LOD_COUNT, + >>::ModelEntry, + > { // TODO: Use raw entries to avoid lots of allocation (among other things). let key = FigureKey { body, @@ -851,10 +856,7 @@ where } } - pub fn get_blocks_of_interest<'c>( - &'c self, - body: Skel::Body, - ) -> Option<&'c BlocksOfInterest> { + pub fn get_blocks_of_interest(&self, body: Skel::Body) -> Option<&BlocksOfInterest> { let key = FigureKey { body, item_key: None, @@ -869,12 +871,12 @@ where }) } - pub fn get_sprites<'c>( - &'c self, + pub fn get_sprites( + &self, body: Skel::Body, ) -> Option<( - &'c BoundTerrainLocals, - &'c [Instances; SPRITE_LOD_LEVELS], + &BoundTerrainLocals, + &[Instances; SPRITE_LOD_LEVELS], )> { let key = FigureKey { body, @@ -909,7 +911,12 @@ where None } }) { - renderer.update_consts(&mut *model.terrain_locals, &[TerrainLocals::new(pos, ori, Vec2::zero(), 0.0)]) + renderer.update_consts(&mut *model.terrain_locals, &[TerrainLocals::new( + pos, + ori, + Vec2::zero(), + 0.0, + )]) } } } diff --git a/voxygen/src/scene/figure/load.rs b/voxygen/src/scene/figure/load.rs index 604e8ece47..1109d9478f 100644 --- a/voxygen/src/scene/figure/load.rs +++ b/voxygen/src/scene/figure/load.rs @@ -1,4 +1,6 @@ -use super::cache::{FigureKey, ToolKey, TerrainModelEntryFuture, FigureModelEntryFuture, ModelEntryFuture}; +use super::cache::{ + FigureKey, FigureModelEntryFuture, ModelEntryFuture, TerrainModelEntryFuture, ToolKey, +}; use common::{ assets::{self, AssetExt, AssetHandle, DotVoxAsset, ReloadWatcher, Ron}, comp::{ @@ -24,7 +26,9 @@ use common::{ theropod::{self, BodyType as TBodyType, Species as TSpecies}, }, figure::{Cell, DynaUnionizer, MatCell, MatSegment, Material, Segment}, - vol::{IntoFullPosIterator, ReadVol, Vox}, terrain::Block, volumes::dyna::Dyna, + terrain::Block, + vol::{IntoFullPosIterator, ReadVol, Vox}, + volumes::dyna::Dyna, }; use hashbrown::HashMap; use serde::{Deserialize, Deserializer}; @@ -5273,7 +5277,7 @@ fn mesh_ship_bone<'a, K: fmt::Debug + Eq + Hash, V, F: Fn(&V) -> Option<&'a Voxe Some(spec) => spec, None => { error!("No specification exists for {:?}", obj); - + return None; }, }; @@ -5283,11 +5287,11 @@ fn mesh_ship_bone<'a, K: fmt::Debug + Eq + Hash, V, F: Fn(&V) -> Option<&'a Voxe } impl BodySpec for ship::Body { + type BoneMesh = ShipBoneMeshes; type Extra = (); type Manifests = AssetHandle; - type Spec = ShipSpec; - type BoneMesh = ShipBoneMeshes; type ModelEntryFuture = TerrainModelEntryFuture; + type Spec = ShipSpec; fn load_spec() -> Result { Self::Spec::load("") } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index f10d13a656..a6c7fe17df 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -14,9 +14,9 @@ use crate::{ terrain::{BoundLocals as BoundTerrainLocals, Locals as TerrainLocals}, trail, ColLights, }, - ColLightInfo, FigureBoneData, FigureDrawer, FigureLocals, FigureModel, FigureShadowDrawer, - Instances, Mesh, Quad, RenderError, Renderer, SpriteDrawer, SpriteInstance, SubModel, - TerrainVertex, CullingMode, AltIndices, + AltIndices, ColLightInfo, CullingMode, FigureBoneData, FigureDrawer, FigureLocals, + FigureModel, FigureShadowDrawer, Instances, Mesh, Quad, RenderError, Renderer, + SpriteDrawer, SpriteInstance, SubModel, TerrainVertex, }, scene::{ camera::{Camera, CameraMode, Dependents}, @@ -6312,7 +6312,7 @@ impl FigureMgr { &'a self, drawer: &mut SpriteDrawer<'_, 'a>, state: &State, - focus_pos: Vec3, + cam_pos: Vec3, sprite_render_distance: f32, ) { span!(_guard, "render", "FigureManager::render_sprites"); @@ -6322,28 +6322,48 @@ impl FigureMgr { let sprite_hid_detail_distance = sprite_render_distance * 0.35; let sprite_high_detail_distance = sprite_render_distance * 0.15; - for (entity, pos, body, _, inventory, scale, collider) in ( + for (entity, pos, body, _, collider) in ( &ecs.entities(), &ecs.read_storage::(), &ecs.read_storage::(), ecs.read_storage::().maybe(), - ecs.read_storage::().maybe(), - ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), ) .join() // Don't render dead entities - .filter(|(_, _, _, health, _, _, _)| health.map_or(true, |h| !h.is_dead)) + .filter(|(_, _, _, health, _)| health.map_or(true, |h| !h.is_dead)) { - if let Some((data, sprite_instances)) = self.get_sprite_instances( - entity, - body, - collider, - ) { - let focus_dist_sqrd = pos.0.distance_squared(focus_pos); + if let Some((data, sprite_instances)) = + self.get_sprite_instances(entity, body, collider) + { + let dist_sqrd = cam_pos.distance_squared(pos.0); + let radius = collider.map_or(f32::INFINITY, |collider| collider.bounding_radius()); - // TODO NOW: LOD - drawer.draw(data, &sprite_instances[0], &AltIndices { deep_end: 0, underground_end: 0 }, CullingMode::None) + let dist_sqrd = dist_sqrd - radius * radius; + + if dist_sqrd < sprite_render_distance.powi(2) { + let lod_level = if dist_sqrd < sprite_high_detail_distance.powi(2) { + 0 + } else if dist_sqrd < sprite_hid_detail_distance.powi(2) { + 1 + } else if dist_sqrd < sprite_mid_detail_distance.powi(2) { + 2 + } else if dist_sqrd < sprite_low_detail_distance.powi(2) { + 3 + } else { + 4 + }; + + drawer.draw( + data, + &sprite_instances[lod_level], + &AltIndices { + deep_end: 0, + underground_end: 0, + }, + CullingMode::None, + ) + } } } } @@ -6935,13 +6955,9 @@ impl FigureMgr { entity, mut_count: vol.mut_count, }; - self.volume_model_cache.get_sprites( - vk, - ) + self.volume_model_cache.get_sprites(vk) } else { - self.ship_model_cache.get_sprites( - *body, - ) + self.ship_model_cache.get_sprites(*body) } }, _ => None, @@ -7157,8 +7173,12 @@ impl FigureColLights { ); }); - let terrain_locals = - renderer.create_terrain_bound_locals(&[TerrainLocals::new(pos, ori, Vec2::zero(), 0.0)]); + let terrain_locals = renderer.create_terrain_bound_locals(&[TerrainLocals::new( + pos, + ori, + Vec2::zero(), + 0.0, + )]); let sprite_instances = sprite_instances.map(|instances| renderer.create_instances(&instances)); diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 94abb2e3ac..7185435de2 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -1355,7 +1355,7 @@ impl Scene { self.figure_mgr.render_sprites( &mut sprite_drawer, state, - focus_pos, + cam_pos, scene_data.sprite_render_distance, ); self.terrain.render_sprites( diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 60581402f0..3a967dc06f 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -10,9 +10,10 @@ use crate::{ }, render::{ pipelines::{self, ColLights}, - AltIndices, ColLightInfo, FirstPassDrawer, FluidVertex, GlobalModel, Instances, LodData, Mesh, Model, - RenderError, Renderer, SpriteDrawer, SpriteGlobalsBindGroup, SpriteInstance, SpriteVertex, - SpriteVerts, TerrainLocals, TerrainShadowDrawer, TerrainVertex, SPRITE_VERT_PAGE_SIZE, CullingMode, + AltIndices, ColLightInfo, CullingMode, FirstPassDrawer, FluidVertex, GlobalModel, + Instances, LodData, Mesh, Model, RenderError, Renderer, SpriteDrawer, + SpriteGlobalsBindGroup, SpriteInstance, SpriteVertex, SpriteVerts, TerrainLocals, + TerrainShadowDrawer, TerrainVertex, SPRITE_VERT_PAGE_SIZE, }, }; @@ -260,7 +261,13 @@ pub fn get_sprite_instances<'a, I: 'a>( }; let wpos = to_wpos(rel_pos); - let seed = (wpos.x as u64).overflowing_mul(3).0.overflowing_add((wpos.y as u64).overflowing_mul(7).0).0.overflowing_add((wpos.x as u64).overflowing_mul(wpos.y as u64).0).0; // Awful PRNG + let seed = (wpos.x as u64) + .overflowing_mul(3) + .0 + .overflowing_add((wpos.y as u64).overflowing_mul(7).0) + .0 + .overflowing_add((wpos.x as u64).overflowing_mul(wpos.y as u64).0) + .0; // Awful PRNG let ori = (block.get_ori().unwrap_or((seed % 4) as u8 * 2)) & 0b111; let variation = seed as usize % cfg.variations.len(); @@ -360,7 +367,9 @@ fn mesh_worker( let mesh = mesh.as_ref().unwrap(); (&*mesh.light_map, &*mesh.glow_map) }; - let to_wpos = |rel_pos: Vec3| Vec3::from(pos * TerrainChunk::RECT_SIZE.map(|e: u32| e as i32)) + rel_pos.as_(); + let to_wpos = |rel_pos: Vec3| { + Vec3::from(pos * TerrainChunk::RECT_SIZE.map(|e: u32| e as i32)) + rel_pos.as_() + }; MeshWorkerResponse { pos, // Extract sprite locations from volume @@ -394,7 +403,8 @@ fn mesh_worker( (0..TerrainChunk::RECT_SIZE.x as i32) .flat_map(|x| { (0..TerrainChunk::RECT_SIZE.y as i32).flat_map(move |y| { - (z_bounds.0 as i32..z_bounds.1 as i32).map(move |z| Vec3::new(x, y, z).as_()) + (z_bounds.0 as i32..z_bounds.1 as i32) + .map(move |z| Vec3::new(x, y, z).as_()) }) }) .filter_map(|rel_pos| Some((rel_pos, *volume.get(to_wpos(rel_pos)).ok()?))), @@ -1228,11 +1238,7 @@ impl Terrain { let sprite_instances = response.sprite_instances.map(|(instances, alt_indices)| { - ( - renderer - .create_instances(&instances), - alt_indices, - ) + (renderer.create_instances(&instances), alt_indices) }); if let Some(mesh) = response.mesh { diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 7ca1ae450b..0c30f89370 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -892,7 +892,8 @@ impl PlayState for SessionState { if let Some(interactable) = &self.interactable { match interactable { Interactable::Block(_, pos, interaction) => { - if matches!(interaction, BlockInteraction::Mount(_)) { + if matches!(interaction, BlockInteraction::Mount(_)) + { client.mount_sprite(*pos) } },