diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index fcd4616023..c51431b5d5 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -10,10 +10,9 @@ in vec4 inst_mat0; in vec4 inst_mat1; in vec4 inst_mat2; in vec4 inst_mat3; -in vec3 inst_col; -in vec3 inst_vel; -in vec4 inst_time; -in float inst_wind_sway; +in float inst_time; +in float inst_entropy; +in int inst_mode; out vec3 f_pos; flat out vec3 f_norm; @@ -23,6 +22,17 @@ out float f_light; const float SCALE = 1.0 / 11.0; +float PHI = 1.61803398874989484820459; // Φ = Golden Ratio + +float gold_noise(in vec2 xy, in float seed){ + return fract(tan(distance(xy * PHI, xy) * seed) * xy.x); +} + +// Modes +const int SMOKE = 0; +const int FIRE = 1; +const int FLAMETHROWER = 2; + void main() { mat4 inst_mat; inst_mat[0] = inst_mat0; @@ -30,10 +40,39 @@ void main() { inst_mat[2] = inst_mat2; inst_mat[3] = inst_mat3; - f_pos = (inst_mat * vec4(v_pos * SCALE, 1)).xyz; - //f_pos.z -= 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0); + float rand1 = gold_noise(vec2(0.0, 0.0), inst_entropy); + float rand2 = gold_noise(vec2(1.0, 1.0), inst_entropy); + float rand3 = gold_noise(vec2(2.0, 2.0), inst_entropy); + float rand4 = gold_noise(vec2(3.0, 3.0), inst_entropy); + float rand5 = gold_noise(vec2(4.0, 4.0), inst_entropy); + float rand6 = gold_noise(vec2(5.0, 5.0), inst_entropy); - f_pos += inst_vel * (tick.x - inst_time.x); + vec3 inst_vel = vec3(0.0, 0.0, 0.0); + vec3 inst_pos = vec3(0.0, 0.0, 0.0); + vec3 inst_col = vec3(1.0, 1.0, 1.0); + + if (inst_mode == SMOKE) { + inst_col = vec3(1.0, 1.0, 1.0); + inst_vel = vec3(rand1 * 0.2 - 0.1, rand2 * 0.2 - 0.1, 1.0 + rand3); + inst_pos = vec3(rand4 * 5.0 - 2.5, rand5 * 5.0 - 2.5, 0.0); + } else if (inst_mode == FIRE) { + inst_col = vec3(1.0, 1.0 * inst_entropy, 0.0); + inst_vel = vec3(rand1 * 0.2 - 0.1, rand2 * 0.2 - 0.1, 4.0 + rand3); + inst_pos = vec3(rand4 * 5.0 - 2.5, rand5 * 5.0 - 2.5, 0.0); + } else if (inst_mode == FLAMETHROWER) { + // TODO: velocity based on attack range, angle and parent orientation. + inst_col = vec3(1.0, 1.0 * inst_entropy, 0.0); + inst_vel = vec3(rand1 * 0.1, rand2 * 0.1, 3.0 + rand3); + inst_pos = vec3(rand4 * 5.0 - 2.5, rand5 * 5.0 - 2.5, 0.0); + } else { + inst_col = vec3(rand1, rand2, rand3); + inst_vel = vec3(rand4, rand5, rand6); + inst_pos = vec3(rand1, rand2, rand3); + } + + f_pos = (inst_mat * vec4((v_pos + inst_pos) * SCALE, 1)).xyz; + + f_pos += inst_vel * (tick.x - inst_time); // 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)); diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index d656e8d4e6..79292b797e 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -2,8 +2,7 @@ use crate::{ comp::{ ability::Stage, item::{armor::Protection, Item, ItemKind}, - Body, CharacterState, EnergySource, Gravity, LightEmitter, ParticleEmitter, Projectile, - StateUpdate, + Body, CharacterState, EnergySource, Gravity, LightEmitter, Projectile, StateUpdate, }, states::{triple_strike::*, *}, sys::character_behavior::JoinData, @@ -62,7 +61,6 @@ pub enum CharacterAbility { projectile: Projectile, projectile_body: Body, projectile_light: Option, - projectile_particles: Vec, projectile_gravity: Option, }, Boost { @@ -249,7 +247,6 @@ impl From<&CharacterAbility> for CharacterState { projectile, projectile_body, projectile_light, - projectile_particles, projectile_gravity, energy_cost: _, } => CharacterState::BasicRanged(basic_ranged::Data { @@ -261,7 +258,6 @@ impl From<&CharacterAbility> for CharacterState { projectile: projectile.clone(), projectile_body: *projectile_body, projectile_light: *projectile_light, - projectile_particles: projectile_particles.clone().to_vec(), projectile_gravity: *projectile_gravity, }), CharacterAbility::Boost { duration, only_up } => CharacterState::Boost(boost::Data { diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 86746daf2f..8b09cc7324 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -2,8 +2,8 @@ // version in voxygen\src\meta.rs in order to reset save files to being empty use crate::comp::{ - body::object, projectile, visual::ParticleEmitterMode, Body, CharacterAbility, Gravity, - HealthChange, HealthSource, LightEmitter, ParticleEmitter, Projectile, + body::object, projectile, Body, CharacterAbility, Gravity, HealthChange, HealthSource, + LightEmitter, Projectile, }; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -180,7 +180,6 @@ impl Tool { }, projectile_body: Body::Object(object::Body::Arrow), projectile_light: None, - projectile_particles: vec![], projectile_gravity: Some(Gravity(0.2)), }, ChargedRanged { @@ -195,7 +194,6 @@ impl Tool { recover_duration: Duration::from_millis(500), projectile_body: Body::Object(object::Body::Arrow), projectile_light: None, - projectile_particles: vec![], projectile_gravity: Some(Gravity(0.05)), }, ], @@ -276,21 +274,6 @@ impl Tool { col: (0.85, 0.5, 0.11).into(), ..Default::default() }), - projectile_particles: vec![ParticleEmitter { - mode: ParticleEmitterMode::Sprinkler, - // model_key: "voxygen.voxel.not_found", - count: (2, 3), - frequency: Duration::from_millis(50), - initial_lifespan: Duration::from_millis(500), - initial_offset: (vek::Vec3::broadcast(-1.0), vek::Vec3::broadcast(1.0)), - initial_orientation: ( - vek::Vec3::broadcast(-1.0), - vek::Vec3::broadcast(1.0), - ), - initial_scale: (0.1, 0.3), - initial_velocity: (vek::Vec3::zero(), vek::Vec3::one()), - initial_col: (vek::Rgb::zero(), vek::Rgb::one()), - }], projectile_gravity: None, }, BasicRanged { @@ -315,7 +298,6 @@ impl Tool { col: (1.0, 0.75, 0.11).into(), ..Default::default() }), - projectile_particles: vec![ParticleEmitter::default()], projectile_gravity: None, }, ], @@ -360,7 +342,6 @@ impl Tool { col: (0.0, 1.0, 0.33).into(), ..Default::default() }), - projectile_particles: vec![], projectile_gravity: None, }, ], diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 3326cf2288..963f3c95a6 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -53,4 +53,4 @@ pub use player::{Player, MAX_MOUNT_RANGE_SQR}; pub use projectile::Projectile; pub use skills::{Skill, SkillGroup, SkillGroupType, SkillSet}; pub use stats::{Exp, HealthChange, HealthSource, Level, Stats}; -pub use visual::{LightAnimation, LightEmitter, ParticleEmitter, ParticleEmitters}; +pub use visual::{LightAnimation, LightEmitter}; diff --git a/common/src/comp/phys.rs b/common/src/comp/phys.rs index e9496f047c..b6fdb18d73 100644 --- a/common/src/comp/phys.rs +++ b/common/src/comp/phys.rs @@ -24,6 +24,10 @@ impl Component for Vel { #[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct Ori(pub Dir); +impl Ori { + pub fn vec(&self) -> &Vec3 { &self.0.vec() } +} + impl Component for Ori { type Storage = IdvStorage; } diff --git a/common/src/comp/visual.rs b/common/src/comp/visual.rs index 0159c2945e..b8f463d593 100644 --- a/common/src/comp/visual.rs +++ b/common/src/comp/visual.rs @@ -1,7 +1,6 @@ use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; -use std::time::Duration; use vek::*; #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -47,67 +46,67 @@ impl Default for LightAnimation { impl Component for LightAnimation { type Storage = FlaggedStorage>; } -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] -pub enum ParticleEmitterMode { - Sprinkler, -} +// #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +// pub enum ParticleEmitterMode { +// Sprinkler, +// } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] // Copy -pub struct ParticleEmitters(pub Vec); +// #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] // Copy +// pub struct ParticleEmitters(pub Vec); -impl Default for ParticleEmitters { - fn default() -> Self { - Self(vec![ParticleEmitter::default(), ParticleEmitter { - mode: ParticleEmitterMode::Sprinkler, - // model_key: "voxygen.voxel.not_found", - count: (7, 10), - frequency: Duration::from_millis(100), - initial_lifespan: Duration::from_secs(500), - initial_offset: (Vec3::broadcast(-0.2), Vec3::broadcast(0.2)), - initial_orientation: (Vec3::broadcast(0.0), Vec3::broadcast(1.0)), - initial_scale: (1.0, 2.0), - initial_velocity: (Vec3::new(0.0, 0.0, 1.0), Vec3::new(0.01, 0.01, 3.0)), - initial_col: (Rgb::new(0.999, 0.0, 0.0), Rgb::new(1.0, 1.0, 0.001)), - }]) - } -} +// impl Default for ParticleEmitters { +// fn default() -> Self { +// Self(vec![ParticleEmitter::default(), ParticleEmitter { +// mode: ParticleEmitterMode::Sprinkler, +// // model_key: "voxygen.voxel.not_found", +// count: (7, 10), +// frequency: Duration::from_millis(100), +// initial_lifespan: Duration::from_millis(500), +// initial_offset: (Vec3::broadcast(-0.2), Vec3::broadcast(0.2)), +// initial_orientation: (Vec3::broadcast(0.0), +// Vec3::broadcast(1.0)), initial_scale: (1.0, 2.5), +// initial_velocity: (Vec3::new(0.0, 0.0, 1.0), Vec3::new(0.01, +// 0.01, 3.0)), initial_col: (Rgb::new(0.999, 0.0, 0.0), +// Rgb::new(1.0, 1.0, 0.001)), }]) +// } +// } -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct ParticleEmitter { - pub mode: ParticleEmitterMode, +// #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +// pub struct ParticleEmitter { +// pub mode: ParticleEmitterMode, - // spawn X particles per Y, that live for Z - // pub model_ref: &str, // can we have some kind of stack based key like a u8? - pub count: (u8, u8), - pub frequency: Duration, +// // spawn X particles per Y, that live for Z +// // pub model_ref: &str, // can we have some kind of stack based key like +// a u8? pub count: (u8, u8), +// pub frequency: Duration, - // relative to Pos, Ori components? - // can these be functions that returns a Vec3? - pub initial_lifespan: Duration, - pub initial_offset: (Vec3, Vec3), // fn() -> Vec3, - pub initial_scale: (f32, f32), // fn() -> Vec3, - pub initial_orientation: (Vec3, Vec3), // fn() -> Vec3, - pub initial_velocity: (Vec3, Vec3), // fn() -> Vec3, - pub initial_col: (Rgb, Rgb), // fn() -> Vec3, -} +// // relative to Pos, Ori components? +// // can these be functions that returns a Vec3? +// pub initial_lifespan: Duration, +// pub initial_offset: (Vec3, Vec3), // fn() -> Vec3, +// pub initial_scale: (f32, f32), // fn() -> Vec3, +// pub initial_orientation: (Vec3, Vec3), // fn() -> Vec3, +// pub initial_velocity: (Vec3, Vec3), // fn() -> Vec3, +// pub initial_col: (Rgb, Rgb), // fn() -> Vec3, +// } -impl Default for ParticleEmitter { - fn default() -> Self { - Self { - mode: ParticleEmitterMode::Sprinkler, - // model_key: "voxygen.voxel.not_found", - count: (2, 5), - frequency: Duration::from_millis(100), - initial_lifespan: Duration::from_secs(20), - initial_offset: (Vec3::broadcast(-0.1), Vec3::broadcast(0.1)), - initial_orientation: (Vec3::broadcast(0.0), Vec3::broadcast(1.0)), - initial_scale: (0.1, 2.0), - initial_velocity: (Vec3::new(0.0, 0.0, 0.2), Vec3::new(0.01, 0.01, 1.0)), - initial_col: (Rgb::new(0.999, 0.999, 0.999), Rgb::new(1.0, 1.0, 1.0)), - } - } -} +// impl Default for ParticleEmitter { +// fn default() -> Self { +// Self { +// mode: ParticleEmitterMode::Sprinkler, +// // model_key: "voxygen.voxel.not_found", +// count: (2, 5), +// frequency: Duration::from_millis(100), +// initial_lifespan: Duration::from_secs(20), +// initial_offset: (Vec3::broadcast(-0.1), Vec3::broadcast(0.1)), +// initial_orientation: (Vec3::broadcast(0.0), +// Vec3::broadcast(1.0)), initial_scale: (0.1, 2.0), +// initial_velocity: (Vec3::new(0.0, 0.0, 0.2), Vec3::new(0.01, +// 0.01, 1.0)), initial_col: (Rgb::new(0.999, 0.999, 0.999), +// Rgb::new(1.0, 1.0, 1.0)), } +// } +// } -impl Component for ParticleEmitter { - type Storage = FlaggedStorage>; -} +// impl Component for ParticleEmitter { +// type Storage = FlaggedStorage>; +// } diff --git a/common/src/event.rs b/common/src/event.rs index 36e595154e..943757a2a7 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -43,7 +43,6 @@ pub enum ServerEvent { dir: Dir, body: comp::Body, light: Option, - particles: Vec, projectile: comp::Projectile, gravity: Option, }, diff --git a/common/src/msg/ecs_packet.rs b/common/src/msg/ecs_packet.rs index 2b7512e234..726d67321a 100644 --- a/common/src/msg/ecs_packet.rs +++ b/common/src/msg/ecs_packet.rs @@ -15,7 +15,6 @@ sum_type! { Stats(comp::Stats), Energy(comp::Energy), LightEmitter(comp::LightEmitter), - ParticleEmitter(comp::ParticleEmitters), Item(comp::Item), Scale(comp::Scale), Group(comp::Group), @@ -43,7 +42,6 @@ sum_type! { Stats(PhantomData), Energy(PhantomData), LightEmitter(PhantomData), - ParticleEmitter(PhantomData), Item(PhantomData), Scale(PhantomData), Group(PhantomData), @@ -71,7 +69,6 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::Stats(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Energy(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::LightEmitter(comp) => sync::handle_insert(comp, entity, world), - EcsCompPacket::ParticleEmitter(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Item(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Scale(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Group(comp) => sync::handle_insert(comp, entity, world), @@ -97,7 +94,6 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::Stats(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Energy(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::LightEmitter(comp) => sync::handle_modify(comp, entity, world), - EcsCompPacket::ParticleEmitter(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Item(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Scale(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Group(comp) => sync::handle_modify(comp, entity, world), @@ -125,9 +121,6 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPhantom::LightEmitter(_) => { sync::handle_remove::(entity, world) }, - EcsCompPhantom::ParticleEmitter(_) => { - sync::handle_remove::(entity, world) - }, EcsCompPhantom::Item(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Scale(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Group(_) => sync::handle_remove::(entity, world), diff --git a/common/src/state.rs b/common/src/state.rs index a06ee25dce..3d003b7a4f 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -113,7 +113,6 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); - ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index 8e5acaa967..b41bd6c208 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -1,5 +1,5 @@ use crate::{ - comp::{Body, CharacterState, Gravity, LightEmitter, ParticleEmitter, Projectile, StateUpdate}, + comp::{Body, CharacterState, Gravity, LightEmitter, Projectile, StateUpdate}, event::ServerEvent, states::utils::*, sys::character_behavior::*, @@ -20,7 +20,6 @@ pub struct Data { pub projectile: Projectile, pub projectile_body: Body, pub projectile_light: Option, - pub projectile_particles: Vec, pub projectile_gravity: Option, /// Whether the attack fired already pub exhausted: bool, @@ -49,7 +48,6 @@ impl CharacterBehavior for Data { projectile: self.projectile.clone(), projectile_body: self.projectile_body, projectile_light: self.projectile_light, - projectile_particles: self.projectile_particles.clone(), projectile_gravity: self.projectile_gravity, exhausted: false, }); @@ -63,7 +61,6 @@ impl CharacterBehavior for Data { body: self.projectile_body, projectile, light: self.projectile_light, - particles: self.projectile_particles.clone(), gravity: self.projectile_gravity, }); @@ -75,7 +72,6 @@ impl CharacterBehavior for Data { projectile: self.projectile.clone(), projectile_body: self.projectile_body, projectile_light: self.projectile_light, - projectile_particles: self.projectile_particles.clone(), projectile_gravity: self.projectile_gravity, exhausted: true, }); @@ -92,7 +88,6 @@ impl CharacterBehavior for Data { projectile: self.projectile.clone(), projectile_body: self.projectile_body, projectile_light: self.projectile_light, - projectile_particles: self.projectile_particles.clone(), projectile_gravity: self.projectile_gravity, exhausted: true, }); diff --git a/common/src/util/dir.rs b/common/src/util/dir.rs index 22aa1b0fe6..96c085479d 100644 --- a/common/src/util/dir.rs +++ b/common/src/util/dir.rs @@ -87,6 +87,8 @@ impl Dir { } pub fn is_valid(&self) -> bool { !self.0.map(f32::is_nan).reduce_or() && self.is_normalized() } + + pub fn vec(&self) -> &Vec3 { &self.0 } } impl std::ops::Deref for Dir { diff --git a/server/src/cmd.rs b/server/src/cmd.rs index c96265e33c..b580cfbd98 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -910,7 +910,6 @@ fn handle_light( .create_entity_synced() .with(pos) .with(comp::ForceUpdate) - .with(comp::ParticleEmitters::default()) .with(light_emitter); if let Some(light_offset) = light_offset_opt { builder.with(light_offset).build(); diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index d6725fc87f..a62c0b342c 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -1,8 +1,8 @@ use crate::{sys, Server, StateExt}; use common::{ comp::{ - self, Agent, Alignment, Body, Gravity, Item, ItemDrop, LightEmitter, Loadout, - ParticleEmitter, ParticleEmitters, Pos, Projectile, Scale, Stats, Vel, WaypointArea, + self, Agent, Alignment, Body, Gravity, Item, ItemDrop, LightEmitter, Loadout, Pos, + Projectile, Scale, Stats, Vel, WaypointArea, }, util::Dir, }; @@ -77,7 +77,6 @@ pub fn handle_shoot( dir: Dir, body: Body, light: Option, - particles: Vec, projectile: Projectile, gravity: Option, ) { @@ -97,7 +96,6 @@ pub fn handle_shoot( if let Some(light) = light { builder = builder.with(light) } - builder = builder.with(ParticleEmitters(particles)); if let Some(gravity) = gravity { builder = builder.with(gravity) } @@ -115,7 +113,6 @@ pub fn handle_create_waypoint(server: &mut Server, pos: Vec3) { flicker: 1.0, animated: true, }) - .with(ParticleEmitters::default()) .with(WaypointArea::default()) .build(); } diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 9761e1d869..412d9a536a 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -61,10 +61,9 @@ impl Server { dir, body, light, - particles, projectile, gravity, - } => handle_shoot(self, entity, dir, body, light, particles, projectile, gravity), + } => handle_shoot(self, entity, dir, body, light, projectile, gravity), ServerEvent::Damage { uid, change } => handle_damage(&self, uid, change), ServerEvent::Destroy { entity, cause } => handle_destroy(self, entity, cause), ServerEvent::InventoryManip(entity, manip) => handle_inventory(self, entity, manip), diff --git a/server/src/sys/sentinel.rs b/server/src/sys/sentinel.rs index 97e86c3480..6121dbb4a9 100644 --- a/server/src/sys/sentinel.rs +++ b/server/src/sys/sentinel.rs @@ -2,7 +2,7 @@ use super::SysTimer; use common::{ comp::{ Alignment, Body, CanBuild, CharacterState, Collider, Energy, Gravity, Item, LightEmitter, - Loadout, Mass, MountState, Mounting, Ori, ParticleEmitter, ParticleEmitters, Player, Pos, + Loadout, Mass, MountState, Mounting, Ori, Player, Pos, Scale, Stats, Sticky, Vel, }, msg::EcsCompPacket, @@ -45,7 +45,6 @@ pub struct TrackedComps<'a> { pub energy: ReadStorage<'a, Energy>, pub can_build: ReadStorage<'a, CanBuild>, pub light_emitter: ReadStorage<'a, LightEmitter>, - pub particle_emitter: ReadStorage<'a, ParticleEmitters>, pub item: ReadStorage<'a, Item>, pub scale: ReadStorage<'a, Scale>, pub mounting: ReadStorage<'a, Mounting>, @@ -94,10 +93,6 @@ impl<'a> TrackedComps<'a> { .get(entity) .copied() .map(|c| comps.push(c.into())); - self.particle_emitter - .get(entity) - .cloned() - .map(|c| comps.push(c.into())); self.item.get(entity).cloned().map(|c| comps.push(c.into())); self.scale .get(entity) @@ -153,7 +148,6 @@ pub struct ReadTrackers<'a> { pub energy: ReadExpect<'a, UpdateTracker>, pub can_build: ReadExpect<'a, UpdateTracker>, pub light_emitter: ReadExpect<'a, UpdateTracker>, - pub particle_emitter: ReadExpect<'a, UpdateTracker>, pub item: ReadExpect<'a, UpdateTracker>, pub scale: ReadExpect<'a, UpdateTracker>, pub mounting: ReadExpect<'a, UpdateTracker>, @@ -187,12 +181,6 @@ impl<'a> ReadTrackers<'a> { &comps.light_emitter, filter, ) - .with_component( - &comps.uid, - &*self.particle_emitter, - &comps.particle_emitter, - filter, - ) .with_component(&comps.uid, &*self.item, &comps.item, filter) .with_component(&comps.uid, &*self.scale, &comps.scale, filter) .with_component(&comps.uid, &*self.mounting, &comps.mounting, filter) @@ -223,7 +211,6 @@ pub struct WriteTrackers<'a> { energy: WriteExpect<'a, UpdateTracker>, can_build: WriteExpect<'a, UpdateTracker>, light_emitter: WriteExpect<'a, UpdateTracker>, - particle_emitter: WriteExpect<'a, UpdateTracker>, item: WriteExpect<'a, UpdateTracker>, scale: WriteExpect<'a, UpdateTracker>, mounting: WriteExpect<'a, UpdateTracker>, @@ -246,9 +233,6 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) { trackers.energy.record_changes(&comps.energy); trackers.can_build.record_changes(&comps.can_build); trackers.light_emitter.record_changes(&comps.light_emitter); - trackers - .particle_emitter - .record_changes(&comps.particle_emitter); trackers.item.record_changes(&comps.item); trackers.scale.record_changes(&comps.scale); trackers.mounting.record_changes(&comps.mounting); @@ -304,7 +288,6 @@ pub fn register_trackers(world: &mut World) { world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); - world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); diff --git a/voxygen/src/render/pipelines/particle.rs b/voxygen/src/render/pipelines/particle.rs index 37c15a4577..9b4a94606f 100644 --- a/voxygen/src/render/pipelines/particle.rs +++ b/voxygen/src/render/pipelines/particle.rs @@ -2,7 +2,6 @@ use super::{ super::{Pipeline, TgtColorFmt, TgtDepthStencilFmt}, Globals, Light, Shadow, }; -use common::comp::visual::ParticleEmitterMode; use gfx::{ self, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, gfx_pipeline_inner, gfx_vertex_struct_meta, @@ -22,15 +21,26 @@ gfx_defines! { } vertex Instance { + // created_at time, so we can calculate time relativity, needed for relative animation. + // can save 32 bits per instance, for particles that are not relatively animated. + inst_time: f32 = "inst_time", + + // a seed value for randomness + inst_entropy: f32 = "inst_entropy", + + // modes should probably be seperate shaders, as a part of scaling and optimisation efforts + inst_mode: i32 = "inst_mode", + + // a triangle is: f32 x 3 x 3 x 1 = 288 bits + // a quad is: f32 x 3 x 3 x 2 = 576 bits + // a cube is: f32 x 3 x 3 x 12 = 3456 bits + // this matrix is: f32 x 4 x 4 x 1 = 512 bits (per instance!) + // consider using vertex postion & entropy instead; + // to determine initial offset, scale, orientation etc. inst_mat0: [f32; 4] = "inst_mat0", inst_mat1: [f32; 4] = "inst_mat1", inst_mat2: [f32; 4] = "inst_mat2", inst_mat3: [f32; 4] = "inst_mat3", - inst_col: [f32; 3] = "inst_col", - inst_vel: [f32; 3] = "inst_vel", - inst_time: [f32; 4] = "inst_time", - inst_wind_sway: f32 = "inst_wind_sway", - mode: u8 = "mode", } pipeline pipe { @@ -69,43 +79,38 @@ impl Vertex { } } +pub enum ParticleMode { + CampfireSmoke, + CampfireFire, +} + +impl ParticleMode { + pub fn into_uint(self) -> u32 { self as u32 } +} + impl Instance { pub fn new( - mat: Mat4, - col: Rgb, - vel: Vec3, - time: f64, - wind_sway: f32, - mode: ParticleEmitterMode, + inst_time: f64, + inst_entropy: f32, + inst_mode: ParticleMode, + inst_mat: Mat4, ) -> Self { - let mat_arr = mat.into_col_arrays(); + let inst_mat_col = inst_mat.into_col_arrays(); Self { - inst_mat0: mat_arr[0], - inst_mat1: mat_arr[1], - inst_mat2: mat_arr[2], - inst_mat3: mat_arr[3], - inst_col: col.into_array(), - inst_vel: vel.into_array(), - inst_time: [time as f32; 4], + inst_time: inst_time as f32, + inst_entropy, + inst_mode: inst_mode as i32, - inst_wind_sway: wind_sway, - - mode: mode as u8, + inst_mat0: inst_mat_col[0], + inst_mat1: inst_mat_col[1], + inst_mat2: inst_mat_col[2], + inst_mat3: inst_mat_col[3], } } } impl Default for Instance { - fn default() -> Self { - Self::new( - Mat4::identity(), - Rgb::broadcast(1.0), - Vec3::zero(), - 0.0, - 0.0, - ParticleEmitterMode::Sprinkler, - ) - } + fn default() -> Self { Self::new(0.0, 0.0, ParticleMode::CampfireSmoke, Mat4::identity()) } } pub struct ParticlePipeline; diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 9b0f6c3fe5..8e7186cfb6 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -398,14 +398,7 @@ impl Scene { self.figure_mgr.clean(scene_data.tick); // Maintain the particles. - self.particle_mgr.maintain( - renderer, - &scene_data, - self.camera.get_focus_pos(), - self.loaded_distance, - view_mat, - proj_mat, - ); + self.particle_mgr.maintain(renderer, &scene_data); // Maintain audio self.sfx_mgr @@ -442,13 +435,8 @@ impl Scene { scene_data.figure_lod_render_distance, ); - self.particle_mgr.render( - renderer, - &self.globals, - &self.lights, - &self.shadows, - self.camera.get_focus_pos(), - ); + self.particle_mgr + .render(renderer, &self.globals, &self.lights, &self.shadows); // Render the skybox. renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals); diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 96af3493a7..3477f52d87 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -2,51 +2,31 @@ use super::SceneData; use crate::{ mesh::Meshable, render::{ - mesh::Quad, Consts, Globals, Instances, Light, Model, ParticleInstance, ParticlePipeline, - Renderer, Shadow, + pipelines::particle::ParticleMode, Consts, Globals, Instances, Light, Model, + ParticleInstance, ParticlePipeline, Renderer, Shadow, }, }; use common::{ assets, - comp::{ - visual::ParticleEmitterMode, CharacterState, Ori, ParticleEmitter, ParticleEmitters, Pos, - Vel, - }, + comp::{object, Body, CharacterState, Pos}, figure::Segment, }; use dot_vox::DotVoxData; use hashbrown::HashMap; use rand::Rng; -use specs::{Entity as EcsEntity, Join, WorldExt}; +use specs::{Join, WorldExt}; use std::time::{Duration, Instant}; -use vek::{Mat4, Rgb, Vec3}; +use vek::{Mat4, Vec3}; struct Particles { - // this is probably nieve, - // could cache and re-use between particles, - // should be a cache key? - // model: Model, - // created_at: Instant, - // lifespan: Duration, alive_until: Instant, // created_at + lifespan - instances: Instances, } -struct Emitter { - last_emit: Instant, -} - pub struct ParticleMgr { - // to keep track of spawn intervals - emitters: HashMap, - - // to keep track of lifespans + // keep track of lifespans particles: Vec, - model_cache: HashMap<&'static str, Model>, - - beginning_of_time: Instant, } const MODEL_KEY: &str = "voxygen.voxel.particle"; @@ -55,21 +35,18 @@ impl ParticleMgr { pub fn new(renderer: &mut Renderer) -> Self { let mut model_cache = HashMap::new(); - let model = model_cache.entry(MODEL_KEY).or_insert_with(|| { + model_cache.entry(MODEL_KEY).or_insert_with(|| { let offset = Vec3::zero(); let lod_scale = Vec3::one(); - // TODO: from cache let vox = assets::load_expect::(MODEL_KEY); - // TODO: from cache let mesh = &Meshable::::generate_mesh( &Segment::from(vox.as_ref()), (offset * lod_scale, Vec3::one() / lod_scale), ) .0; - // TODO: from cache let model = renderer .create_model(mesh) .expect("Failed to create particle model"); @@ -78,137 +55,103 @@ impl ParticleMgr { }); Self { - emitters: HashMap::new(), particles: Vec::new(), model_cache, - beginning_of_time: Instant::now(), } } - pub fn maintain( - &mut self, - renderer: &mut Renderer, - scene_data: &SceneData, - focus_pos: Vec3, - loaded_distance: f32, - view_mat: Mat4, - proj_mat: Mat4, - ) { + pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: &SceneData) { let now = Instant::now(); - let state = scene_data.state; - let ecs = state.ecs(); - - // remove dead emitters - self.emitters.retain(|k, _v| ecs.is_alive(*k)); // remove dead particles self.particles.retain(|p| p.alive_until > now); - // add ParticleEmitter particles - self.maintain_particle_emitter(renderer, scene_data); + self.maintain_waypoint_particles(renderer, scene_data); - self.maintain_ability_particles(renderer, scene_data); + self.maintain_boost_particles(renderer, scene_data); } - fn maintain_particle_emitter(&mut self, renderer: &mut Renderer, scene_data: &SceneData) { + fn maintain_waypoint_particles(&mut self, renderer: &mut Renderer, scene_data: &SceneData) { let state = scene_data.state; let ecs = state.ecs(); - let time = state.get_time(); - let now = Instant::now(); - let beginning_of_time1 = self.beginning_of_time.clone(); + let mut rng = rand::thread_rng(); - for (_i, (entity, particle_emitters, pos, ori, vel)) in ( + for (_i, (_entity, pos, body)) in ( &ecs.entities(), - &ecs.read_storage::(), &ecs.read_storage::(), - ecs.read_storage::().maybe(), - ecs.read_storage::().maybe(), + &ecs.read_storage::(), ) .join() .enumerate() { - for particle_emitter in &particle_emitters.0 { - // TODO: track multiple particle_emitter last_emit - let emitter = self.emitters.entry(entity).or_insert_with(|| Emitter { - last_emit: beginning_of_time1, // self.beginning_of_time.clone() - }); + match body { + Body::Object(object::Body::CampfireLit) => { + let fire_cpu_insts = vec![ParticleInstance::new( + time, + rng.gen(), + ParticleMode::CampfireFire, + Mat4::identity().translated_3d(pos.0), + )]; - if emitter.last_emit + particle_emitter.frequency < now { - emitter.last_emit = Instant::now(); + self.particles.push(Particles { + alive_until: now + Duration::from_millis(250), + instances: renderer + .create_instances(&fire_cpu_insts) + .expect("Failed to upload particle instances to the GPU!"), + }); - let cpu_insts = - into_particle_instances(&particle_emitter, renderer, time, pos, ori, vel); + let smoke_cpu_insts = vec![ParticleInstance::new( + time, + rng.gen(), + ParticleMode::CampfireSmoke, + Mat4::identity().translated_3d(pos.0), + )]; - let gpu_insts = renderer - .create_instances(&cpu_insts) + let smoke_cpu_insts = renderer + .create_instances(&smoke_cpu_insts) .expect("Failed to upload particle instances to the GPU!"); - let entry = self.particles.push(Particles { - alive_until: now + particle_emitter.initial_lifespan, - instances: gpu_insts, + self.particles.push(Particles { + alive_until: now + Duration::from_secs(10), + instances: smoke_cpu_insts, }); - } + }, + _ => {}, } } } - fn maintain_ability_particles(&mut self, renderer: &mut Renderer, scene_data: &SceneData) { + fn maintain_boost_particles(&mut self, renderer: &mut Renderer, scene_data: &SceneData) { let state = scene_data.state; let ecs = state.ecs(); - let time = state.get_time(); - let now = Instant::now(); - let beginning_of_time1 = self.beginning_of_time.clone(); + let mut rng = rand::thread_rng(); - for (_i, (entity, pos, character_state)) in ( + for (_i, (_entity, pos, character_state)) in ( &ecs.entities(), - //&ecs.read_storage::(), &ecs.read_storage::(), &ecs.read_storage::(), ) .join() .enumerate() { - // let emitter = self.emitters.entry(entity).or_insert_with(|| Emitter { - // last_emit: beginning_of_time1, // self.beginning_of_time.clone() - // }); - - // if emitter.last_emit + particle_emitter.frequency < now { - // emitter.last_emit = Instant::now(); - // } - - if let CharacterState::BasicMelee(melee_data) = character_state { - // TODO: configure the emitter on the ability instead. - let particle_emitter = ParticleEmitter { - count: (30, 50), - frequency: Duration::from_millis(1000), // doesn't matter - initial_lifespan: Duration::from_millis(1000), - initial_offset: ( - Vec3::new(1.0, -1.0, 0.0), - Vec3::new(1.01, 1.0, 2.0), /* TODO: cone // melee_data.max_angle */ - ), - initial_orientation: (Vec3::zero(), Vec3::one()), - initial_scale: (1.0, 3.0), - mode: ParticleEmitterMode::Sprinkler, - initial_velocity: ( - Vec3::new(1.0, 0.0, 0.0), - Vec3::new(10.0, 0.01, 0.01), /* TODO: cone // melee_data.max_angle */ - ), - initial_col: (Rgb::zero(), Rgb::one()), - }; - - let cpu_insts = - into_particle_instances(&particle_emitter, renderer, time, pos, None, None); + if let CharacterState::Boost(_) = character_state { + let cpu_insts = vec![ParticleInstance::new( + time, + rng.gen(), + ParticleMode::CampfireSmoke, + Mat4::identity().translated_3d(pos.0), + )]; let gpu_insts = renderer .create_instances(&cpu_insts) .expect("Failed to upload particle instances to the GPU!"); - let entry = self.particles.push(Particles { - alive_until: now + particle_emitter.initial_lifespan, + self.particles.push(Particles { + alive_until: now + Duration::from_secs(15), instances: gpu_insts, }); } @@ -221,7 +164,6 @@ impl ParticleMgr { globals: &Consts, lights: &Consts, shadows: &Consts, - focus_pos: Vec3, ) { for particle in &self.particles { renderer.render_particles( @@ -237,58 +179,3 @@ impl ParticleMgr { } } } - -fn into_particle_instances( - particle_emitter: &ParticleEmitter, - renderer: &mut Renderer, - time: f64, - pos: &Pos, - ori: Option<&Ori>, - vel: Option<&Vel>, -) -> Vec { - let mut rng = rand::thread_rng(); - let vel_default = Vel::default(); - let vel2 = vel.unwrap_or_else(|| &vel_default).0; - - let mut instances_vec = Vec::new(); - - for x in 0..rng.gen_range(particle_emitter.count.0, particle_emitter.count.1) { - // how does ParticleEmitterMode fit in here? - // can we have a ParticleInstance type per ParticleEmitterMode? - // can we mix and match instance types in the same instances_vec? - instances_vec.push(ParticleInstance::new( - Mat4::identity() - // initial rotation - .rotated_x(rng.gen_range(particle_emitter.initial_orientation.0.x * std::f32::consts::PI * 2.0, particle_emitter.initial_orientation.1.x * std::f32::consts::PI * 2.0)) - .rotated_y(rng.gen_range(particle_emitter.initial_orientation.0.y * std::f32::consts::PI * 2.0, particle_emitter.initial_orientation.1.y * std::f32::consts::PI * 2.0)) - .rotated_z(rng.gen_range(particle_emitter.initial_orientation.0.z * std::f32::consts::PI * 2.0, particle_emitter.initial_orientation.1.z * std::f32::consts::PI * 2.0)) - // initial scale - .scaled_3d(rng.gen_range(particle_emitter.initial_scale.0, particle_emitter.initial_scale.1)) - // inition position - .translated_3d( - pos.0 // relative - + Vec3::new( - rng.gen_range(particle_emitter.initial_offset.0.x, particle_emitter.initial_offset.1.x), - rng.gen_range(particle_emitter.initial_offset.0.y, particle_emitter.initial_offset.1.y), - rng.gen_range(particle_emitter.initial_offset.0.z, particle_emitter.initial_offset.1.z), - ), - ), - Rgb::new( - rng.gen_range(particle_emitter.initial_col.0.r, particle_emitter.initial_col.1.r), - rng.gen_range(particle_emitter.initial_col.0.g, particle_emitter.initial_col.1.g), - rng.gen_range(particle_emitter.initial_col.0.b, particle_emitter.initial_col.1.b), - ), // instance color - vel2 // relative - + Vec3::new( - rng.gen_range(particle_emitter.initial_velocity.0.x, particle_emitter.initial_velocity.1.x), - rng.gen_range(particle_emitter.initial_velocity.0.y, particle_emitter.initial_velocity.1.y), - rng.gen_range(particle_emitter.initial_velocity.0.z, particle_emitter.initial_velocity.1.z), - ), - time, - rng.gen_range(0.0, 20.0), // wind sway - ParticleEmitterMode::Sprinkler, // particle_emitter.mode */ - )); - } - - instances_vec -} diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index fb44b952a4..37d3a7e637 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -787,7 +787,7 @@ impl PlayState for SessionState { }, HudEvent::AdjustParticleRenderDistance(particle_render_distance) => { global_state.settings.graphics.particle_render_distance = - particle_render_distance; + particle_render_distance; global_state.settings.save_to_file_warn(); }, HudEvent::AdjustFigureLoDRenderDistance(figure_lod_render_distance) => { @@ -1002,9 +1002,12 @@ impl PlayState for SessionState { gamma: global_state.settings.graphics.gamma, mouse_smoothing: global_state.settings.gameplay.smooth_pan_enable, sprite_render_distance: global_state.settings.graphics.sprite_render_distance - as f32, - particle_render_distance: global_state.settings.graphics.particle_render_distance - as f32, + as f32, + particle_render_distance: global_state + .settings + .graphics + .particle_render_distance + as f32, figure_lod_render_distance: global_state .settings .graphics @@ -1060,6 +1063,7 @@ impl PlayState for SessionState { mouse_smoothing: settings.gameplay.smooth_pan_enable, sprite_render_distance: settings.graphics.sprite_render_distance as f32, figure_lod_render_distance: settings.graphics.figure_lod_render_distance as f32, + particle_render_distance: settings.graphics.particle_render_distance as f32, is_aiming: self.is_aiming, }; self.scene.render(