Add particle velocity and ability particle emitter

This commit is contained in:
scott-c 2020-07-13 21:44:28 +08:00
parent da5f4828a5
commit 803677f0fb
15 changed files with 160 additions and 84 deletions

View File

@ -12,7 +12,7 @@ in vec4 inst_mat2;
in vec4 inst_mat3;
in vec3 inst_col;
in vec3 inst_vel;
in vec4 inst_tick;
in vec4 inst_time;
in float inst_wind_sway;
out vec3 f_pos;
@ -30,20 +30,10 @@ void main() {
inst_mat[2] = inst_mat2;
inst_mat[3] = inst_mat3;
vec3 particle_pos = (inst_mat * vec4(0, 0, 0, 1)).xyz;
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);
//f_pos.z -= 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0);
// Wind waving
//f_pos += inst_wind_sway * vec3(
// sin(tick.x * 1.5 + f_pos.y * 0.1) * sin(tick.x * 0.35),
// sin(tick.x * 1.5 + f_pos.x * 0.1) * sin(tick.x * 0.25),
// 0.0
//) * pow(abs(v_pos.z) * SCALE, 1.3) * 0.2;
float elapsed = (tick.x - inst_tick.x) / 100000.0;
f_pos += (inst_vel * elapsed);
f_pos += inst_vel * (tick.x - inst_time.x);
// 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));
@ -53,11 +43,6 @@ void main() {
f_col = srgb_to_linear(col) * srgb_to_linear(inst_col);
f_ao = float((v_norm_ao >> 3) & 0x3u) / 4.0;
// Select glowing
if (select_pos.w > 0 && select_pos.xyz == floor(particle_pos)) {
f_col *= 4.0;
}
f_light = 1.0;
gl_Position =

BIN
assets/voxygen/voxel/particle.vox (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -62,7 +62,7 @@ pub enum CharacterAbility {
projectile: Projectile,
projectile_body: Body,
projectile_light: Option<LightEmitter>,
projectile_particles: Option<ParticleEmitter>,
projectile_particles: Vec<ParticleEmitter>,
projectile_gravity: Option<Gravity>,
},
Boost {
@ -261,7 +261,7 @@ impl From<&CharacterAbility> for CharacterState {
projectile: projectile.clone(),
projectile_body: *projectile_body,
projectile_light: *projectile_light,
projectile_particles: *projectile_particles,
projectile_particles: projectile_particles.clone().to_vec(),
projectile_gravity: *projectile_gravity,
}),
CharacterAbility::Boost { duration, only_up } => CharacterState::Boost(boost::Data {

View File

@ -180,7 +180,7 @@ impl Tool {
},
projectile_body: Body::Object(object::Body::Arrow),
projectile_light: None,
projectile_particles: None,
projectile_particles: vec![],
projectile_gravity: Some(Gravity(0.2)),
},
ChargedRanged {
@ -195,7 +195,7 @@ impl Tool {
recover_duration: Duration::from_millis(500),
projectile_body: Body::Object(object::Body::Arrow),
projectile_light: None,
projectile_particles: None,
projectile_particles: vec![],
projectile_gravity: Some(Gravity(0.05)),
},
],
@ -276,7 +276,7 @@ impl Tool {
col: (0.85, 0.5, 0.11).into(),
..Default::default()
}),
projectile_particles: Some(ParticleEmitter {
projectile_particles: vec![ParticleEmitter {
mode: ParticleEmitterMode::Sprinkler,
// model_key: "voxygen.voxel.not_found",
count: (2, 3),
@ -288,7 +288,8 @@ impl Tool {
vek::Vec3::broadcast(1.0),
),
initial_scale: (0.1, 0.3),
}),
initial_velocity: (vek::Vec3::zero(), vek::Vec3::one()),
}],
projectile_gravity: None,
},
BasicRanged {
@ -313,7 +314,7 @@ impl Tool {
col: (1.0, 0.75, 0.11).into(),
..Default::default()
}),
projectile_particles: Some(ParticleEmitter::default()),
projectile_particles: vec![ParticleEmitter::default()],
projectile_gravity: None,
},
],
@ -358,7 +359,7 @@ impl Tool {
col: (0.0, 1.0, 0.33).into(),
..Default::default()
}),
projectile_particles: None,
projectile_particles: vec![],
projectile_gravity: None,
},
],

View File

@ -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};
pub use visual::{LightAnimation, LightEmitter, ParticleEmitter, ParticleEmitters};

View File

@ -52,6 +52,13 @@ pub enum ParticleEmitterMode {
Sprinkler,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] // Copy
pub struct ParticleEmitters(pub Vec<ParticleEmitter>);
impl Default for ParticleEmitters {
fn default() -> Self { Self(vec![ParticleEmitter::default()]) }
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ParticleEmitter {
pub mode: ParticleEmitterMode,
@ -67,6 +74,7 @@ pub struct ParticleEmitter {
pub initial_offset: (Vec3<f32>, Vec3<f32>), // fn() -> Vec3<f32>,
pub initial_scale: (f32, f32), // fn() -> Vec3<f32>,
pub initial_orientation: (Vec3<f32>, Vec3<f32>), // fn() -> Vec3<f32>,
pub initial_velocity: (Vec3<f32>, Vec3<f32>), // fn() -> Vec3<f32>,
}
impl Default for ParticleEmitter {
@ -75,11 +83,15 @@ impl Default for ParticleEmitter {
mode: ParticleEmitterMode::Sprinkler,
// model_key: "voxygen.voxel.not_found",
count: (2, 5),
frequency: Duration::from_millis(500),
initial_lifespan: Duration::from_secs(2),
initial_offset: (vek::Vec3::broadcast(-5.0), vek::Vec3::broadcast(5.0)),
initial_orientation: (vek::Vec3::broadcast(-5.0), vek::Vec3::broadcast(5.0)),
frequency: Duration::from_millis(100),
initial_lifespan: Duration::from_secs(20),
initial_offset: (vek::Vec3::broadcast(-0.1), vek::Vec3::broadcast(0.1)),
initial_orientation: (vek::Vec3::broadcast(0.0), vek::Vec3::broadcast(1.0)),
initial_scale: (0.1, 2.0),
initial_velocity: (
vek::Vec3::new(0.0, 0.0, 0.2),
vek::Vec3::new(0.01, 0.01, 1.0),
),
}
}
}

View File

@ -43,7 +43,7 @@ pub enum ServerEvent {
dir: Dir,
body: comp::Body,
light: Option<comp::LightEmitter>,
particles: Option<comp::ParticleEmitter>,
particles: Vec<comp::ParticleEmitter>,
projectile: comp::Projectile,
gravity: Option<comp::Gravity>,
},

View File

@ -15,7 +15,7 @@ sum_type! {
Stats(comp::Stats),
Energy(comp::Energy),
LightEmitter(comp::LightEmitter),
ParticleEmitter(comp::ParticleEmitter),
ParticleEmitter(comp::ParticleEmitters),
Item(comp::Item),
Scale(comp::Scale),
Group(comp::Group),
@ -43,7 +43,7 @@ sum_type! {
Stats(PhantomData<comp::Stats>),
Energy(PhantomData<comp::Energy>),
LightEmitter(PhantomData<comp::LightEmitter>),
ParticleEmitter(PhantomData<comp::ParticleEmitter>),
ParticleEmitter(PhantomData<comp::ParticleEmitters>),
Item(PhantomData<comp::Item>),
Scale(PhantomData<comp::Scale>),
Group(PhantomData<comp::Group>),
@ -126,7 +126,7 @@ impl sync::CompPacket for EcsCompPacket {
sync::handle_remove::<comp::LightEmitter>(entity, world)
},
EcsCompPhantom::ParticleEmitter(_) => {
sync::handle_remove::<comp::ParticleEmitter>(entity, world)
sync::handle_remove::<comp::ParticleEmitters>(entity, world)
},
EcsCompPhantom::Item(_) => sync::handle_remove::<comp::Item>(entity, world),
EcsCompPhantom::Scale(_) => sync::handle_remove::<comp::Scale>(entity, world),

View File

@ -113,7 +113,7 @@ impl State {
ecs.register::<comp::Energy>();
ecs.register::<comp::CanBuild>();
ecs.register::<comp::LightEmitter>();
ecs.register::<comp::ParticleEmitter>();
ecs.register::<comp::ParticleEmitters>();
ecs.register::<comp::Item>();
ecs.register::<comp::Scale>();
ecs.register::<comp::Mounting>();

View File

@ -20,7 +20,7 @@ pub struct Data {
pub projectile: Projectile,
pub projectile_body: Body,
pub projectile_light: Option<LightEmitter>,
pub projectile_particles: Option<ParticleEmitter>,
pub projectile_particles: Vec<ParticleEmitter>,
pub projectile_gravity: Option<Gravity>,
/// Whether the attack fired already
pub exhausted: bool,
@ -49,7 +49,7 @@ impl CharacterBehavior for Data {
projectile: self.projectile.clone(),
projectile_body: self.projectile_body,
projectile_light: self.projectile_light,
projectile_particles: self.projectile_particles,
projectile_particles: self.projectile_particles.clone(),
projectile_gravity: self.projectile_gravity,
exhausted: false,
});
@ -63,7 +63,7 @@ impl CharacterBehavior for Data {
body: self.projectile_body,
projectile,
light: self.projectile_light,
particles: self.projectile_particles,
particles: self.projectile_particles.clone(),
gravity: self.projectile_gravity,
});
@ -75,7 +75,7 @@ impl CharacterBehavior for Data {
projectile: self.projectile.clone(),
projectile_body: self.projectile_body,
projectile_light: self.projectile_light,
projectile_particles: self.projectile_particles,
projectile_particles: self.projectile_particles.clone(),
projectile_gravity: self.projectile_gravity,
exhausted: true,
});
@ -92,7 +92,7 @@ impl CharacterBehavior for Data {
projectile: self.projectile.clone(),
projectile_body: self.projectile_body,
projectile_light: self.projectile_light,
projectile_particles: self.projectile_particles,
projectile_particles: self.projectile_particles.clone(),
projectile_gravity: self.projectile_gravity,
exhausted: true,
});

View File

@ -23,7 +23,6 @@ use specs::{Builder, Entity as EcsEntity, Join, WorldExt};
use vek::*;
use world::util::Sampler;
use comp::visual::ParticleEmitterMode;
use scan_fmt::{scan_fmt, scan_fmt_some};
use tracing::error;
@ -911,7 +910,7 @@ fn handle_light(
.create_entity_synced()
.with(pos)
.with(comp::ForceUpdate)
.with(comp::ParticleEmitter::default())
.with(comp::ParticleEmitters::default())
.with(light_emitter);
if let Some(light_offset) = light_offset_opt {
builder.with(light_offset).build();

View File

@ -2,11 +2,10 @@ use crate::{sys, Server, StateExt};
use common::{
comp::{
self, Agent, Alignment, Body, Gravity, Item, ItemDrop, LightEmitter, Loadout,
ParticleEmitter, Pos, Projectile, Scale, Stats, Vel, WaypointArea,
ParticleEmitter, ParticleEmitters, Pos, Projectile, Scale, Stats, Vel, WaypointArea,
},
util::Dir,
};
use comp::visual::ParticleEmitterMode;
use specs::{Builder, Entity as EcsEntity, WorldExt};
use vek::{Rgb, Vec3};
@ -78,7 +77,7 @@ pub fn handle_shoot(
dir: Dir,
body: Body,
light: Option<LightEmitter>,
particles: Option<ParticleEmitter>,
particles: Vec<ParticleEmitter>,
projectile: Projectile,
gravity: Option<Gravity>,
) {
@ -98,9 +97,7 @@ pub fn handle_shoot(
if let Some(light) = light {
builder = builder.with(light)
}
if let Some(particles) = particles {
builder = builder.with(particles)
}
builder = builder.with(ParticleEmitters(particles));
if let Some(gravity) = gravity {
builder = builder.with(gravity)
}
@ -118,7 +115,7 @@ pub fn handle_create_waypoint(server: &mut Server, pos: Vec3<f32>) {
flicker: 1.0,
animated: true,
})
.with(ParticleEmitter::default())
.with(ParticleEmitters::default())
.with(WaypointArea::default())
.build();
}

View File

@ -2,8 +2,8 @@ use super::SysTimer;
use common::{
comp::{
Alignment, Body, CanBuild, CharacterState, Collider, Energy, Gravity, Item, LightEmitter,
Loadout, Mass, MountState, Mounting, Ori, ParticleEmitter, Player, Pos, Scale, Stats,
Sticky, Vel,
Loadout, Mass, MountState, Mounting, Ori, ParticleEmitter, ParticleEmitters, Player, Pos,
Scale, Stats, Sticky, Vel,
},
msg::EcsCompPacket,
sync::{CompSyncPackage, EntityPackage, EntitySyncPackage, Uid, UpdateTracker, WorldSyncExt},
@ -45,7 +45,7 @@ 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, ParticleEmitter>,
pub particle_emitter: ReadStorage<'a, ParticleEmitters>,
pub item: ReadStorage<'a, Item>,
pub scale: ReadStorage<'a, Scale>,
pub mounting: ReadStorage<'a, Mounting>,
@ -96,7 +96,7 @@ impl<'a> TrackedComps<'a> {
.map(|c| comps.push(c.into()));
self.particle_emitter
.get(entity)
.copied()
.cloned()
.map(|c| comps.push(c.into()));
self.item.get(entity).cloned().map(|c| comps.push(c.into()));
self.scale
@ -153,7 +153,7 @@ pub struct ReadTrackers<'a> {
pub energy: ReadExpect<'a, UpdateTracker<Energy>>,
pub can_build: ReadExpect<'a, UpdateTracker<CanBuild>>,
pub light_emitter: ReadExpect<'a, UpdateTracker<LightEmitter>>,
pub particle_emitter: ReadExpect<'a, UpdateTracker<ParticleEmitter>>,
pub particle_emitter: ReadExpect<'a, UpdateTracker<ParticleEmitters>>,
pub item: ReadExpect<'a, UpdateTracker<Item>>,
pub scale: ReadExpect<'a, UpdateTracker<Scale>>,
pub mounting: ReadExpect<'a, UpdateTracker<Mounting>>,
@ -223,7 +223,7 @@ pub struct WriteTrackers<'a> {
energy: WriteExpect<'a, UpdateTracker<Energy>>,
can_build: WriteExpect<'a, UpdateTracker<CanBuild>>,
light_emitter: WriteExpect<'a, UpdateTracker<LightEmitter>>,
particle_emitter: WriteExpect<'a, UpdateTracker<ParticleEmitter>>,
particle_emitter: WriteExpect<'a, UpdateTracker<ParticleEmitters>>,
item: WriteExpect<'a, UpdateTracker<Item>>,
scale: WriteExpect<'a, UpdateTracker<Scale>>,
mounting: WriteExpect<'a, UpdateTracker<Mounting>>,
@ -304,7 +304,7 @@ pub fn register_trackers(world: &mut World) {
world.register_tracker::<Energy>();
world.register_tracker::<CanBuild>();
world.register_tracker::<LightEmitter>();
world.register_tracker::<ParticleEmitter>();
world.register_tracker::<ParticleEmitters>();
world.register_tracker::<Item>();
world.register_tracker::<Scale>();
world.register_tracker::<Mounting>();

View File

@ -28,7 +28,7 @@ gfx_defines! {
inst_mat3: [f32; 4] = "inst_mat3",
inst_col: [f32; 3] = "inst_col",
inst_vel: [f32; 3] = "inst_vel",
inst_tick: [f32; 4] = "inst_tick",
inst_time: [f32; 4] = "inst_time",
inst_wind_sway: f32 = "inst_wind_sway",
mode: u8 = "mode",
}
@ -74,7 +74,7 @@ impl Instance {
mat: Mat4<f32>,
col: Rgb<f32>,
vel: Vec3<f32>,
tick: u64,
time: f64,
wind_sway: f32,
mode: ParticleEmitterMode,
) -> Self {
@ -86,7 +86,7 @@ impl Instance {
inst_mat3: mat_arr[3],
inst_col: col.into_array(),
inst_vel: vel.into_array(),
inst_tick: [tick as f32; 4],
inst_time: [time as f32; 4],
inst_wind_sway: wind_sway,
@ -101,7 +101,7 @@ impl Default for Instance {
Mat4::identity(),
Rgb::broadcast(1.0),
Vec3::zero(),
0,
0.0,
0.0,
ParticleEmitterMode::Sprinkler,
)

View File

@ -2,22 +2,23 @@ use super::SceneData;
use crate::{
mesh::Meshable,
render::{
mesh::Quad, Consts, Globals, Instances, Light, Mesh, Model, ParticleInstance,
ParticlePipeline, Renderer, Shadow,
mesh::Quad, Consts, Globals, Instances, Light, Model, ParticleInstance, ParticlePipeline,
Renderer, Shadow,
},
};
use common::{
assets,
comp::{visual::ParticleEmitterMode, Ori, ParticleEmitter, Pos, Vel},
comp::{
visual::ParticleEmitterMode, CharacterState, Ori, ParticleEmitter, ParticleEmitters, Pos,
Vel,
},
figure::Segment,
vol::BaseVol,
};
use dot_vox::DotVoxData;
use hashbrown::HashMap;
use rand::Rng;
use specs::{Entity as EcsEntity, Join, WorldExt};
use std::time::{Duration, Instant};
use tracing::{debug, error, warn};
use vek::{Mat4, Rgb, Vec3};
struct Particles {
@ -48,7 +49,7 @@ pub struct ParticleMgr {
beginning_of_time: Instant,
}
const MODEL_KEY: &str = "voxygen.voxel.not_found";
const MODEL_KEY: &str = "voxygen.voxel.particle";
impl ParticleMgr {
pub fn new(renderer: &mut Renderer) -> Self {
@ -93,24 +94,34 @@ impl ParticleMgr {
view_mat: Mat4<f32>,
proj_mat: Mat4<f32>,
) {
let now = Instant::now();
let state = scene_data.state;
let ecs = state.ecs();
let tick = scene_data.tick;
let now = Instant::now();
let beginning_of_time1 = self.beginning_of_time.clone();
// remove dead emitters
self.emitters.retain(|k, _v| ecs.is_alive(*k));
// remove dead particles
self.particles.retain(|p| p.alive_until > now);
// add living entities particles
for (_i, (entity, particle_emitter, pos, ori, vel)) in (
// add ParticleEmitter particles
self.maintain_particle_emitter(renderer, scene_data);
self.maintain_ability_particles(renderer, scene_data);
}
fn maintain_particle_emitter(&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();
for (_i, (entity, particle_emitters, pos, ori, vel)) in (
&ecs.entities(),
&ecs.read_storage::<ParticleEmitter>(),
&ecs.read_storage::<ParticleEmitters>(),
&ecs.read_storage::<Pos>(),
ecs.read_storage::<Ori>().maybe(),
ecs.read_storage::<Vel>().maybe(),
@ -118,15 +129,78 @@ impl ParticleMgr {
.join()
.enumerate()
{
let emitter = self.emitters.entry(entity).or_insert_with(|| Emitter {
last_emit: beginning_of_time1, // self.beginning_of_time.clone()
});
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()
});
if emitter.last_emit + particle_emitter.frequency < now {
emitter.last_emit = Instant::now();
if emitter.last_emit + particle_emitter.frequency < now {
emitter.last_emit = Instant::now();
let cpu_insts =
into_particle_instances(&particle_emitter, renderer, time, pos, ori, vel);
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,
instances: gpu_insts,
});
}
}
}
}
fn maintain_ability_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();
for (_i, (entity, pos, character_state)) in (
&ecs.entities(),
//&ecs.read_storage::<ParticleEmitter>(),
&ecs.read_storage::<Pos>(),
&ecs.read_storage::<CharacterState>(),
)
.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 */
),
};
let cpu_insts =
into_particle_instances(particle_emitter, renderer, tick, pos, ori, vel);
into_particle_instances(&particle_emitter, renderer, time, pos, None, None);
let gpu_insts = renderer
.create_instances(&cpu_insts)
@ -166,7 +240,7 @@ impl ParticleMgr {
fn into_particle_instances(
particle_emitter: &ParticleEmitter,
renderer: &mut Renderer,
tick: u64,
time: f64,
pos: &Pos,
ori: Option<&Ori>,
vel: Option<&Vel>,
@ -199,8 +273,13 @@ fn into_particle_instances(
),
),
Rgb::broadcast(1.0), // instance color
vel2 + Vec3::broadcast(rng.gen_range(-5.0, 5.0)),
tick,
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 */
));