From d1bbfc99609062c5f1c137d0395db38b3ff4feeb Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 18 Aug 2020 16:51:43 +0100 Subject: [PATCH] Added BlocksOfInterest and block particle emission --- assets/voxygen/shaders/particle-frag.glsl | 6 +- assets/voxygen/shaders/particle-vert.glsl | 40 +++++++------ voxygen/src/render/pipelines/particle.rs | 2 + voxygen/src/scene/mod.rs | 3 +- voxygen/src/scene/particle.rs | 68 ++++++++++++++++++++++- voxygen/src/scene/terrain.rs | 27 ++++++++- voxygen/src/scene/terrain/watcher.rs | 37 ++++++++++++ 7 files changed, 158 insertions(+), 25 deletions(-) create mode 100644 voxygen/src/scene/terrain/watcher.rs diff --git a/assets/voxygen/shaders/particle-frag.glsl b/assets/voxygen/shaders/particle-frag.glsl index 4dfbd6dbca..12a4ad2c3b 100644 --- a/assets/voxygen/shaders/particle-frag.glsl +++ b/assets/voxygen/shaders/particle-frag.glsl @@ -18,7 +18,7 @@ in vec3 f_pos; flat in vec3 f_norm; -in vec3 f_col; +in vec4 f_col; out vec4 tgt_color; @@ -50,7 +50,7 @@ void main() { DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, f_pos); DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac); - vec3 surf_color = f_col; + vec3 surf_color = f_col.rgb; float alpha = 1.0; const float n2 = 1.5; const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); @@ -82,5 +82,5 @@ void main() { vec3 color = surf_color; #endif - tgt_color = vec4(color, 0.3); + tgt_color = vec4(color, f_col.a); } diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index 082fc4e453..fa589067d8 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -26,7 +26,7 @@ in int inst_mode; out vec3 f_pos; flat out vec3 f_norm; -out vec3 f_col; +out vec4 f_col; out float f_ao; out float f_light; @@ -43,6 +43,7 @@ const int FIREWORK_GREEN = 5; const int FIREWORK_PURPLE = 6; const int FIREWORK_RED = 7; const int FIREWORK_YELLOW = 8; +const int LEAF = 9; // meters per second squared (acceleration) const float earth_gravity = 9.807; @@ -50,7 +51,7 @@ const float earth_gravity = 9.807; struct Attr { vec3 offs; float scale; - vec3 col; + vec4 col; }; float lifetime = tick.x - inst_time; @@ -90,7 +91,7 @@ void main() { vec3(rand2 * 0.1, rand3 * 0.1, 1.0 + rand4 * 0.1)// + vec3(sin(lifetime), sin(lifetime + 1.5), sin(lifetime * 4) * 0.25) ), linear_scale(0.5), - vec3(1) + vec4(1, 1, 1, 0.3) ); } else if (inst_mode == FIRE) { attr = Attr( @@ -99,7 +100,7 @@ void main() { vec3(rand2 * 0.1, rand3 * 0.1, 2.0 + rand4 * 1.0) ), 1.0, - vec3(2, rand5 + 2, 0) + vec4(2, 0.8 + rand5 * 0.3, 0, 1) ); } else if (inst_mode == GUN_POWDER_SPARK) { attr = Attr( @@ -108,7 +109,7 @@ void main() { vec3(rand4, rand5, rand6) * 2.0 + grav_vel(earth_gravity) ), 1.0, - vec3(3.5, 3 + rand7, 0) + vec4(3.5, 3 + rand7, 0, 1) ); } else if (inst_mode == SHRAPNEL) { attr = Attr( @@ -117,7 +118,7 @@ void main() { vec3(rand4, rand5, rand6) * 40.0 + grav_vel(earth_gravity) ), 3.0 + rand0, - vec3(0.6 + rand7 * 0.4) + vec4(vec3(0.6 + rand7 * 0.4), 1) ); } else if (inst_mode == FIREWORK_BLUE) { attr = Attr( @@ -126,7 +127,7 @@ void main() { vec3(rand4, rand5, rand6) * 40.0 + grav_vel(earth_gravity) ), 3.0 + rand0, - vec3(0.6 + rand7 * 0.4) + vec4(vec3(0.6 + rand7 * 0.4), 0.3) ); } else if (inst_mode == FIREWORK_GREEN) { attr = Attr( @@ -135,7 +136,7 @@ void main() { vec3(rand4, rand5, rand6) * 40.0 + grav_vel(earth_gravity) ), 3.0 + rand0, - vec3(0.6 + rand7 * 0.4) + vec4(vec3(0.6 + rand7 * 0.4), 0.3) ); } else if (inst_mode == FIREWORK_PURPLE) { attr = Attr( @@ -144,7 +145,7 @@ void main() { vec3(rand4, rand5, rand6) * 40.0 + grav_vel(earth_gravity) ), 3.0 + rand0, - vec3(0.6 + rand7 * 0.4) + vec4(vec3(0.6 + rand7 * 0.4), 0.3) ); } else if (inst_mode == FIREWORK_RED) { attr = Attr( @@ -153,7 +154,7 @@ void main() { vec3(rand4, rand5, rand6) * 40.0 + grav_vel(earth_gravity) ), 3.0 + rand0, - vec3(0.6 + rand7 * 0.4) + vec4(vec3(0.6 + rand7 * 0.4), 0.3) ); } else if (inst_mode == FIREWORK_YELLOW) { attr = Attr( @@ -162,7 +163,16 @@ void main() { vec3(rand4, rand5, rand6) * 40.0 + grav_vel(earth_gravity) ), 3.0 + rand0, - vec3(0.6 + rand7 * 0.4) + vec4(vec3(0.6 + rand7 * 0.4), 0.3) + ); + } else if (inst_mode == LEAF) { + attr = Attr( + linear_motion( + vec3(1.0, 1.0, 0.0), + vec3(0, 0, -2.0) + ) + vec3(sin(lifetime), sin(lifetime + 0.7), sin(lifetime * 0.5)) * 2.0, + 4, + vec4(vec3(0.2 + rand7 * 0.2, 0.2 + (0.5 + rand6 * 0.5) * 0.6, 0), 1) ); } else { attr = Attr( @@ -171,7 +181,7 @@ void main() { vec3(rand2 * 0.1, rand3 * 0.1, 1.0 + rand4 * 0.5) ), exp_scale(-0.2), - vec3(1) + vec4(1) ); } @@ -179,14 +189,12 @@ void main() { // First 3 normals are negative, next 3 are positive vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1)); - f_norm = + f_norm = // inst_pos * normals[(v_norm_ao >> 0) & 0x7u]; //vec3 col = vec3((uvec3(v_col) >> uvec3(0, 8, 16)) & uvec3(0xFFu)) / 255.0; - f_col = - //srgb_to_linear(col) * - srgb_to_linear(attr.col); + f_col = vec4(srgb_to_linear(attr.col.rgb), attr.col.a); gl_Position = all_mat * diff --git a/voxygen/src/render/pipelines/particle.rs b/voxygen/src/render/pipelines/particle.rs index b2af4921f2..bb95acdfa1 100644 --- a/voxygen/src/render/pipelines/particle.rs +++ b/voxygen/src/render/pipelines/particle.rs @@ -88,6 +88,7 @@ impl Vertex { } } +#[derive(Copy, Clone)] pub enum ParticleMode { CampfireSmoke = 0, CampfireFire = 1, @@ -98,6 +99,7 @@ pub enum ParticleMode { FireworkPurple = 6, FireworkRed = 7, FireworkYellow = 8, + Leaf = 9, } impl ParticleMode { diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index d69cf98c2c..b51106de00 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -942,7 +942,8 @@ impl Scene { self.figure_mgr.clean(scene_data.tick); // Maintain the particles. - self.particle_mgr.maintain(renderer, &scene_data); + self.particle_mgr + .maintain(renderer, &scene_data, &self.terrain); // Maintain audio self.sfx_mgr.maintain( diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 32f6ce5c49..96b2077b24 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -1,4 +1,4 @@ -use super::SceneData; +use super::{terrain::BlocksOfInterest, SceneData, Terrain}; use crate::{ mesh::{greedy::GreedyMesh, Meshable}, render::{ @@ -11,10 +11,14 @@ use common::{ comp::{item::Reagent, object, Body, CharacterState, Pos}, figure::Segment, outcome::Outcome, + spiral::Spiral2d, + state::DeltaTime, + terrain::TerrainChunk, + vol::RectRasterableVol, }; use dot_vox::DotVoxData; use hashbrown::HashMap; -use rand::Rng; +use rand::prelude::*; use specs::{Join, WorldExt}; use std::time::Duration; use vek::*; @@ -83,7 +87,12 @@ impl ParticleMgr { } } - pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: &SceneData) { + pub fn maintain( + &mut self, + renderer: &mut Renderer, + scene_data: &SceneData, + terrain: &Terrain, + ) { if scene_data.particles_enabled { // update timings self.scheduler.maintain(scene_data.state.get_time()); @@ -95,6 +104,7 @@ impl ParticleMgr { // add new Particle self.maintain_body_particles(scene_data); self.maintain_boost_particles(scene_data); + self.maintain_block_particles(scene_data, terrain); } else { // remove all particle lifespans self.particles.clear(); @@ -245,6 +255,58 @@ impl ParticleMgr { } } + #[allow(clippy::same_item_push)] // TODO: Pending review in #587 + fn maintain_block_particles( + &mut self, + scene_data: &SceneData, + terrain: &Terrain, + ) { + let dt = scene_data.state.ecs().fetch::().0; + let time = scene_data.state.get_time(); + let player_pos = scene_data + .state + .read_component_cloned::(scene_data.player_entity) + .unwrap_or_default(); + let player_chunk = player_pos.0.xy().map2(TerrainChunk::RECT_SIZE, |e, sz| { + (e.floor() as i32).div_euclid(sz as i32) + }); + + type BoiFn<'a> = fn(&'a BlocksOfInterest) -> &'a [Vec3]; + let particles: &[(BoiFn, _, _, _)] = &[ + (|boi| &boi.leaves, 0.005, 30.0, ParticleMode::Leaf), + (|boi| &boi.embers, 20.0, 0.25, ParticleMode::CampfireFire), + (|boi| &boi.embers, 6.0, 30.0, ParticleMode::CampfireSmoke), + ]; + + const RANGE: usize = 4; + for offset in Spiral2d::new().take((RANGE * 2 + 1).pow(2)) { + let chunk_pos = player_chunk + offset; + + terrain.get(chunk_pos).map(|chunk_data| { + for (get_blocks, rate, dur, mode) in particles.iter() { + let blocks = get_blocks(&chunk_data.blocks_of_interest); + + let avg_particles = dt * blocks.len() as f32 * *rate; + let particle_count = avg_particles.trunc() as usize + + (thread_rng().gen::() < avg_particles.fract()) as usize; + + for _ in 0..particle_count { + let block_pos = + Vec3::from(chunk_pos * TerrainChunk::RECT_SIZE.map(|e| e as i32)) + + blocks.choose(&mut thread_rng()).copied().unwrap(); // Can't fail + + self.particles.push(Particle::new( + Duration::from_secs_f32(*dur), + time, + *mode, + block_pos.map(|e: i32| e as f32 + 0.5), + )); + } + } + }); + } + } + fn upload_particles(&mut self, renderer: &mut Renderer) { let all_cpu_instances = self .particles diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index 06ec276b93..47cde9862b 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -1,3 +1,7 @@ +mod watcher; + +pub use self::watcher::BlocksOfInterest; + use crate::{ mesh::{greedy::GreedyMesh, Meshable}, render::{ @@ -35,7 +39,7 @@ enum Visibility { Visible = 2, } -struct TerrainChunkData { +pub struct TerrainChunkData { // GPU data load_time: f32, opaque_model: Model, @@ -43,6 +47,7 @@ struct TerrainChunkData { col_lights: guillotiere::AllocId, sprite_instances: HashMap<(BlockKind, usize), Instances>, locals: Consts, + pub blocks_of_interest: BlocksOfInterest, visible: Visibility, can_shadow_point: bool, @@ -51,6 +56,7 @@ struct TerrainChunkData { frustum_last_plane_index: u8, } +#[derive(Copy, Clone)] struct ChunkMeshState { pos: Vec2, started_tick: u64, @@ -67,6 +73,7 @@ struct MeshWorkerResponse { col_lights_info: ColLightInfo, sprite_instances: HashMap<(BlockKind, usize), Vec>, started_tick: u64, + blocks_of_interest: BlocksOfInterest, } struct SpriteConfig { @@ -392,6 +399,7 @@ fn mesh_worker + RectRasterableVol + ReadVol + Debug>( started_tick: u64, volume: as SampleVol>>::Sample, max_texture_size: u16, + chunk: Arc, range: Aabb, sprite_data: &HashMap<(BlockKind, usize), Vec>, ) -> MeshWorkerResponse { @@ -446,6 +454,7 @@ fn mesh_worker + RectRasterableVol + ReadVol + Debug>( instances }, + blocks_of_interest: BlocksOfInterest::from_chunk(&chunk), started_tick, } } @@ -2575,7 +2584,7 @@ impl Terrain { // Limit ourselves to u16::MAX even if larger textures are supported. let max_texture_size = renderer.max_texture_size(); - for todo in self + for (todo, chunk) in self .mesh_todo .values_mut() .filter(|todo| { @@ -2584,6 +2593,14 @@ impl Terrain { .unwrap_or(true) }) .min_by_key(|todo| todo.active_worker.unwrap_or(todo.started_tick)) + // Find a reference to the actual `TerrainChunk` we're meshing + .and_then(|todo| { + let pos = todo.pos; + Some((todo, scene_data.state + .terrain() + .get_key_arc(pos) + .cloned()?)) + }) { // TODO: find a alternative! if scene_data.thread_pool.queued_jobs() > 0 { @@ -2644,6 +2661,7 @@ impl Terrain { started_tick, volume, max_texture_size, + chunk, aabb, &sprite_data, )); @@ -2737,6 +2755,7 @@ impl Terrain { visible: Visibility::OutOfRange, can_shadow_point: false, can_shadow_sun: false, + blocks_of_interest: response.blocks_of_interest, z_bounds: response.z_bounds, frustum_last_plane_index: 0, }); @@ -2921,6 +2940,10 @@ impl Terrain { ) } + pub fn get(&self, chunk_key: Vec2) -> Option<&TerrainChunkData> { + self.chunks.get(&chunk_key) + } + pub fn chunk_count(&self) -> usize { self.chunks.len() } pub fn visible_chunk_count(&self) -> usize { diff --git a/voxygen/src/scene/terrain/watcher.rs b/voxygen/src/scene/terrain/watcher.rs new file mode 100644 index 0000000000..439462f12a --- /dev/null +++ b/voxygen/src/scene/terrain/watcher.rs @@ -0,0 +1,37 @@ +use common::{ + terrain::{BlockKind, TerrainChunk}, + vol::{IntoVolIterator, RectRasterableVol}, +}; +use rand::prelude::*; +use vek::*; + +pub struct BlocksOfInterest { + pub leaves: Vec>, + pub embers: Vec>, +} + +impl BlocksOfInterest { + pub fn from_chunk(chunk: &TerrainChunk) -> Self { + let mut leaves = Vec::new(); + let mut embers = Vec::new(); + + chunk + .vol_iter( + Vec3::new(0, 0, chunk.get_min_z()), + Vec3::new( + TerrainChunk::RECT_SIZE.x as i32, + TerrainChunk::RECT_SIZE.y as i32, + chunk.get_max_z(), + ), + ) + .for_each(|(pos, block)| { + if block.kind() == BlockKind::Leaves && thread_rng().gen_range(0, 16) == 0 { + leaves.push(pos); + } else if block.kind() == BlockKind::Ember { + embers.push(pos); + } + }); + + Self { leaves, embers } + } +}