diff --git a/assets/common/manifests/ship_manifest.ron b/assets/common/manifests/ship_manifest.ron index 95e688fd7d..3a50799ba5 100644 --- a/assets/common/manifests/ship_manifest.ron +++ b/assets/common/manifests/ship_manifest.ron @@ -18,7 +18,7 @@ ), custom_indices: { - 1: Air(Door, 4), + 1: Air(ChairSingle, 4), }, ), AirBalloon: ( diff --git a/voxygen/src/scene/figure/cache.rs b/voxygen/src/scene/figure/cache.rs index fc8ca44bad..efdc980b1e 100644 --- a/voxygen/src/scene/figure/cache.rs +++ b/voxygen/src/scene/figure/cache.rs @@ -31,7 +31,7 @@ use common::{ }, figure::{Segment, TerrainSegment}, slowjob::SlowJobPool, - vol::{BaseVol, IntoVolIterator}, + vol::{BaseVol, IntoVolIterator, ReadVol}, }; use core::{hash::Hash, ops::Range}; use crossbeam_utils::atomic; @@ -58,6 +58,7 @@ pub struct TerrainMeshWorkerResponse { vertex_range: [Range; N], sprite_instances: [Vec; SPRITE_LOD_LEVELS], blocks_of_interest: BlocksOfInterest, + blocks_offset: Vec3, } /// NOTE: To test this cell for validity, we currently first use @@ -655,6 +656,7 @@ where vertex_range, sprite_instances, blocks_of_interest, + blocks_offset, }) = Arc::get_mut(recv).take().and_then(|cell| cell.take()) { let model_entry = col_lights.create_terrain( @@ -666,6 +668,7 @@ where ori, sprite_instances, blocks_of_interest, + blocks_offset, ); *model = TerrainModelEntryFuture::Done(model_entry); // NOTE: Borrow checker isn't smart enough to figure this out. @@ -834,13 +837,16 @@ where lod.push(instance); }, block_iter.clone().map(|(pos, block)| (pos.as_() + *offset, block)), - |p| p.as_(), |_| 1.0, |_| 0.0, + |p| p.as_(), + |_| 1.0, + |pos| dyna.get(pos).ok().and_then(|block| block.get_glow()).map(|glow| glow as f32 / 255.0).unwrap_or(0.0), &sprite_data, &sprite_config, ); instances }, blocks_of_interest: BlocksOfInterest::from_blocks(block_iter, 0.0, 10.0, 0.0), + blocks_offset: *offset, })); }); @@ -856,7 +862,10 @@ where } } - pub fn get_blocks_of_interest(&self, body: Skel::Body) -> Option<&BlocksOfInterest> { + pub fn get_blocks_of_interest( + &self, + body: Skel::Body, + ) -> Option<(&BlocksOfInterest, Vec3)> { let key = FigureKey { body, item_key: None, @@ -867,7 +876,7 @@ where return None; }; - Some(&model.blocks_of_interest) + Some((&model.blocks_of_interest, model.blocks_offset)) }) } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index a6c7fe17df..673ec5230e 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -143,6 +143,8 @@ pub struct TerrainModelEntry { lod_vertex_ranges: [Range; N], model: FigureModel, + blocks_offset: Vec3, + terrain_locals: BoundTerrainLocals, sprite_instances: [Instances; SPRITE_LOD_LEVELS], @@ -6939,14 +6941,14 @@ impl FigureMgr { } } - fn get_sprite_instances( - &self, + fn get_sprite_instances<'a>( + &'a self, entity: EcsEntity, body: &Body, collider: Option<&Collider>, ) -> Option<( - &BoundTerrainLocals, - &[Instances; SPRITE_LOD_LEVELS], + &'a BoundTerrainLocals, + &'a [Instances; SPRITE_LOD_LEVELS], )> { match body { Body::Ship(body) => { @@ -6964,6 +6966,28 @@ impl FigureMgr { } } + pub fn get_blocks_of_interest<'a>( + &'a self, + entity: EcsEntity, + body: &Body, + collider: Option<&Collider>, + ) -> Option<(&'a BlocksOfInterest, Vec3)> { + match body { + Body::Ship(body) => { + if let Some(Collider::Volume(vol)) = collider { + let vk = VolumeKey { + entity, + mut_count: vol.mut_count, + }; + self.volume_model_cache.get_blocks_of_interest(vk) + } else { + self.ship_model_cache.get_blocks_of_interest(*body) + } + }, + _ => None, + } + } + pub fn viewpoint_offset(&self, scene_data: &SceneData, entity: EcsEntity) -> Vec3 { scene_data .state @@ -7151,6 +7175,7 @@ impl FigureColLights { ori: Quaternion, sprite_instances: [Vec; SPRITE_LOD_LEVELS], blocks_of_interest: BlocksOfInterest, + blocks_offset: Vec3, ) -> TerrainModelEntry { span!(_guard, "create_figure", "FigureColLights::create_figure"); let atlas = &mut self.atlas; @@ -7192,6 +7217,7 @@ impl FigureColLights { terrain_locals, sprite_instances, blocks_of_interest, + blocks_offset, } } diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 7185435de2..dbfd1346ec 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -655,8 +655,13 @@ impl Scene { lights.clear(); // Maintain the particles. - self.particle_mgr - .maintain(renderer, scene_data, &self.terrain, lights); + self.particle_mgr.maintain( + renderer, + scene_data, + &self.terrain, + &self.figure_mgr, + lights, + ); // Maintain the trails. self.trail_mgr.maintain(renderer, scene_data); diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index d134f9350e..7e54b207b6 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -1,4 +1,4 @@ -use super::{terrain::BlocksOfInterest, SceneData, Terrain}; +use super::{terrain::BlocksOfInterest, FigureMgr, SceneData, Terrain}; use crate::{ ecs::comp::Interpolated, mesh::{greedy::GreedyMesh, segment::generate_mesh_base_vol_particle}, @@ -400,6 +400,7 @@ impl ParticleMgr { renderer: &mut Renderer, scene_data: &SceneData, terrain: &Terrain, + figure_mgr: &FigureMgr, lights: &mut Vec, ) { span!(_guard, "maintain", "ParticleMgr::maintain"); @@ -415,7 +416,7 @@ impl ParticleMgr { self.maintain_body_particles(scene_data); self.maintain_char_state_particles(scene_data); self.maintain_beam_particles(scene_data, lights); - self.maintain_block_particles(scene_data, terrain); + self.maintain_block_particles(scene_data, terrain, figure_mgr); self.maintain_shockwave_particles(scene_data); self.maintain_aura_particles(scene_data); self.maintain_buff_particles(scene_data); @@ -1434,6 +1435,7 @@ impl ParticleMgr { &mut self, scene_data: &SceneData, terrain: &Terrain, + figure_mgr: &FigureMgr, ) { span!( _guard, @@ -1533,6 +1535,7 @@ impl ParticleMgr { }, ]; + let ecs = scene_data.state.ecs(); let mut rng = thread_rng(); for particles in particles.iter() { if !(particles.cond)(scene_data) { @@ -1564,6 +1567,45 @@ impl ParticleMgr { }) }); } + + for (entity, body, pos, ori, collider) in ( + &ecs.entities(), + &ecs.read_storage::(), + &ecs.read_storage::(), + &ecs.read_storage::(), + ecs.read_storage::().maybe(), + ) + .join() + { + if let Some((blocks_of_interest, offset)) = + figure_mgr.get_blocks_of_interest(entity, body, collider) + { + let blocks = (particles.blocks)(blocks_of_interest); + + let avg_particles = dt * blocks.len() as f32 * particles.rate; + let particle_count = avg_particles.trunc() as usize + + (rng.gen::() < avg_particles.fract()) as usize; + + self.particles + .resize_with(self.particles.len() + particle_count, || { + let rel_pos = blocks + .choose(&mut rng) + .copied() + .unwrap() + .map(|e: i32| e as f32 + rng.gen::()); // Can't fail + let wpos = (Mat4::from(ori.to_quat()).translated_3d(pos.0) + * (rel_pos + offset).with_w(1.0)) + .xyz(); + + Particle::new( + Duration::from_secs_f32(particles.lifetime), + time, + particles.mode, + wpos, + ) + }) + } + } } // smoke is more complex as it comes with varying rate and color {