2020-07-05 12:10:58 +00:00
|
|
|
use super::SceneData;
|
|
|
|
use crate::{
|
|
|
|
mesh::Meshable,
|
|
|
|
render::{
|
2020-07-15 15:45:47 +00:00
|
|
|
pipelines::particle::ParticleMode, Consts, Globals, Instances, Light, Model,
|
|
|
|
ParticleInstance, ParticlePipeline, Renderer, Shadow,
|
2020-07-05 12:10:58 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
use common::{
|
|
|
|
assets,
|
2020-07-15 15:45:47 +00:00
|
|
|
comp::{object, Body, CharacterState, Pos},
|
2020-07-05 12:10:58 +00:00
|
|
|
figure::Segment,
|
|
|
|
};
|
|
|
|
use dot_vox::DotVoxData;
|
|
|
|
use hashbrown::HashMap;
|
|
|
|
use rand::Rng;
|
2020-07-15 15:45:47 +00:00
|
|
|
use specs::{Join, WorldExt};
|
2020-07-11 10:37:19 +00:00
|
|
|
use std::time::{Duration, Instant};
|
2020-07-19 07:16:06 +00:00
|
|
|
use vek::Vec3;
|
2020-07-11 10:37:19 +00:00
|
|
|
|
2020-07-05 12:10:58 +00:00
|
|
|
struct Particles {
|
2020-07-11 10:37:19 +00:00
|
|
|
alive_until: Instant, // created_at + lifespan
|
2020-07-21 15:48:20 +00:00
|
|
|
instance: ParticleInstance,
|
2020-07-05 12:10:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ParticleMgr {
|
2020-07-15 15:45:47 +00:00
|
|
|
// keep track of lifespans
|
2020-07-11 10:37:19 +00:00
|
|
|
particles: Vec<Particles>,
|
2020-07-21 15:48:20 +00:00
|
|
|
instances: Instances<ParticleInstance>,
|
2020-07-11 10:37:19 +00:00
|
|
|
model_cache: HashMap<&'static str, Model<ParticlePipeline>>,
|
2020-07-05 12:10:58 +00:00
|
|
|
}
|
|
|
|
|
2020-07-13 13:44:28 +00:00
|
|
|
const MODEL_KEY: &str = "voxygen.voxel.particle";
|
2020-07-11 10:37:19 +00:00
|
|
|
|
2020-07-05 12:10:58 +00:00
|
|
|
impl ParticleMgr {
|
|
|
|
pub fn new(renderer: &mut Renderer) -> Self {
|
2020-07-11 10:37:19 +00:00
|
|
|
let mut model_cache = HashMap::new();
|
2020-07-05 12:10:58 +00:00
|
|
|
|
2020-07-15 15:45:47 +00:00
|
|
|
model_cache.entry(MODEL_KEY).or_insert_with(|| {
|
2020-07-11 10:37:19 +00:00
|
|
|
let offset = Vec3::zero();
|
|
|
|
let lod_scale = Vec3::one();
|
2020-07-05 12:10:58 +00:00
|
|
|
|
2020-07-11 10:37:19 +00:00
|
|
|
let vox = assets::load_expect::<DotVoxData>(MODEL_KEY);
|
|
|
|
|
|
|
|
let mesh = &Meshable::<ParticlePipeline, ParticlePipeline>::generate_mesh(
|
|
|
|
&Segment::from(vox.as_ref()),
|
|
|
|
(offset * lod_scale, Vec3::one() / lod_scale),
|
|
|
|
)
|
|
|
|
.0;
|
|
|
|
|
|
|
|
let model = renderer
|
|
|
|
.create_model(mesh)
|
|
|
|
.expect("Failed to create particle model");
|
2020-07-05 12:10:58 +00:00
|
|
|
|
2020-07-11 10:37:19 +00:00
|
|
|
model
|
|
|
|
});
|
2020-07-05 12:10:58 +00:00
|
|
|
|
2020-07-21 15:48:20 +00:00
|
|
|
let insts = Vec::new();
|
|
|
|
|
|
|
|
let instances = renderer
|
|
|
|
.create_instances(&insts)
|
|
|
|
.expect("Failed to upload particle instances to the GPU!");
|
|
|
|
|
2020-07-05 12:10:58 +00:00
|
|
|
Self {
|
2020-07-11 10:37:19 +00:00
|
|
|
particles: Vec::new(),
|
2020-07-21 15:48:20 +00:00
|
|
|
instances,
|
2020-07-11 10:37:19 +00:00
|
|
|
model_cache,
|
2020-07-05 12:10:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-21 15:48:20 +00:00
|
|
|
pub fn particle_count(&self) -> usize { self.instances.count() }
|
|
|
|
|
|
|
|
pub fn particle_count_visible(&self) -> usize { self.instances.count() }
|
|
|
|
|
2020-07-15 15:45:47 +00:00
|
|
|
pub fn maintain(&mut self, renderer: &mut Renderer, scene_data: &SceneData) {
|
2020-07-13 13:44:28 +00:00
|
|
|
let now = Instant::now();
|
2020-07-05 12:10:58 +00:00
|
|
|
|
2020-07-11 10:37:19 +00:00
|
|
|
// remove dead particles
|
|
|
|
self.particles.retain(|p| p.alive_until > now);
|
2020-07-05 12:10:58 +00:00
|
|
|
|
2020-07-21 15:48:20 +00:00
|
|
|
// let zxc = scene_data.particle_render_distance;
|
|
|
|
|
2020-07-19 07:16:06 +00:00
|
|
|
self.maintain_body_particles(renderer, scene_data);
|
2020-07-13 13:44:28 +00:00
|
|
|
|
2020-07-15 15:45:47 +00:00
|
|
|
self.maintain_boost_particles(renderer, scene_data);
|
2020-07-21 15:48:20 +00:00
|
|
|
|
|
|
|
let all_cpu_instances = self
|
|
|
|
.particles
|
|
|
|
.iter()
|
|
|
|
.map(|p| p.instance)
|
|
|
|
.collect::<Vec<ParticleInstance>>();
|
|
|
|
|
|
|
|
// TODO: upload just the ones that were created and added to queue, not all of
|
|
|
|
// them.
|
|
|
|
self.instances = renderer
|
|
|
|
.create_instances(&all_cpu_instances)
|
|
|
|
.expect("Failed to upload particle instances to the GPU!");
|
2020-07-13 13:44:28 +00:00
|
|
|
}
|
|
|
|
|
2020-07-19 07:16:06 +00:00
|
|
|
fn maintain_body_particles(&mut self, renderer: &mut Renderer, scene_data: &SceneData) {
|
|
|
|
let ecs = scene_data.state.ecs();
|
|
|
|
for (_i, (_entity, body, pos)) in (
|
2020-07-05 12:10:58 +00:00
|
|
|
&ecs.entities(),
|
2020-07-15 15:45:47 +00:00
|
|
|
&ecs.read_storage::<Body>(),
|
2020-07-19 07:16:06 +00:00
|
|
|
&ecs.read_storage::<Pos>(),
|
2020-07-05 12:10:58 +00:00
|
|
|
)
|
|
|
|
.join()
|
|
|
|
.enumerate()
|
|
|
|
{
|
2020-07-15 15:45:47 +00:00
|
|
|
match body {
|
|
|
|
Body::Object(object::Body::CampfireLit) => {
|
2020-07-19 07:16:06 +00:00
|
|
|
self.maintain_campfirelit_particles(renderer, scene_data, pos)
|
|
|
|
},
|
|
|
|
Body::Object(object::Body::BoltFire) => {
|
|
|
|
self.maintain_boltfire_particles(renderer, scene_data, pos)
|
2020-07-15 15:45:47 +00:00
|
|
|
},
|
2020-07-19 07:16:06 +00:00
|
|
|
Body::Object(object::Body::BoltFireBig) => {
|
|
|
|
self.maintain_boltfirebig_particles(renderer, scene_data, pos)
|
|
|
|
},
|
|
|
|
Body::Object(object::Body::Bomb) => {
|
|
|
|
self.maintain_bomb_particles(renderer, scene_data, pos)
|
|
|
|
},
|
|
|
|
// Body::Object(object::Body::Pouch) => {
|
|
|
|
// self.maintain_pouch_particles(renderer, scene_data, pos)
|
|
|
|
// },
|
2020-07-15 15:45:47 +00:00
|
|
|
_ => {},
|
2020-07-13 13:44:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-19 07:16:06 +00:00
|
|
|
fn maintain_campfirelit_particles(
|
|
|
|
&mut self,
|
|
|
|
renderer: &mut Renderer,
|
|
|
|
scene_data: &SceneData,
|
|
|
|
pos: &Pos,
|
|
|
|
) {
|
|
|
|
let time = scene_data.state.get_time();
|
|
|
|
let now = Instant::now();
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
|
|
|
|
self.particles.push(Particles {
|
|
|
|
alive_until: now + Duration::from_millis(250),
|
2020-07-21 15:48:20 +00:00
|
|
|
instance: ParticleInstance::new(time, rng.gen(), ParticleMode::CampfireFire, pos.0),
|
2020-07-19 07:16:06 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
self.particles.push(Particles {
|
|
|
|
alive_until: now + Duration::from_secs(10),
|
2020-07-21 15:48:20 +00:00
|
|
|
instance: ParticleInstance::new(time, rng.gen(), ParticleMode::CampfireSmoke, pos.0),
|
2020-07-19 07:16:06 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn maintain_boltfire_particles(
|
|
|
|
&mut self,
|
|
|
|
renderer: &mut Renderer,
|
|
|
|
scene_data: &SceneData,
|
|
|
|
pos: &Pos,
|
|
|
|
) {
|
|
|
|
let time = scene_data.state.get_time();
|
|
|
|
let now = Instant::now();
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
|
|
|
|
self.particles.push(Particles {
|
|
|
|
alive_until: now + Duration::from_millis(250),
|
2020-07-21 15:48:20 +00:00
|
|
|
instance: ParticleInstance::new(time, rng.gen(), ParticleMode::CampfireFire, pos.0),
|
2020-07-19 07:16:06 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
self.particles.push(Particles {
|
|
|
|
alive_until: now + Duration::from_secs(1),
|
2020-07-21 15:48:20 +00:00
|
|
|
instance: ParticleInstance::new(time, rng.gen(), ParticleMode::CampfireSmoke, pos.0),
|
2020-07-19 07:16:06 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn maintain_boltfirebig_particles(
|
|
|
|
&mut self,
|
|
|
|
renderer: &mut Renderer,
|
|
|
|
scene_data: &SceneData,
|
|
|
|
pos: &Pos,
|
|
|
|
) {
|
|
|
|
let time = scene_data.state.get_time();
|
|
|
|
let now = Instant::now();
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
|
2020-07-21 15:48:20 +00:00
|
|
|
// fire
|
2020-07-19 07:16:06 +00:00
|
|
|
self.particles.push(Particles {
|
|
|
|
alive_until: now + Duration::from_millis(250),
|
2020-07-21 15:48:20 +00:00
|
|
|
instance: ParticleInstance::new(time, rng.gen(), ParticleMode::CampfireFire, pos.0),
|
|
|
|
});
|
|
|
|
self.particles.push(Particles {
|
|
|
|
alive_until: now + Duration::from_millis(250),
|
|
|
|
instance: ParticleInstance::new(time, rng.gen(), ParticleMode::CampfireFire, pos.0),
|
2020-07-19 07:16:06 +00:00
|
|
|
});
|
|
|
|
|
2020-07-21 15:48:20 +00:00
|
|
|
// smoke
|
2020-07-19 07:16:06 +00:00
|
|
|
self.particles.push(Particles {
|
|
|
|
alive_until: now + Duration::from_secs(2),
|
2020-07-21 15:48:20 +00:00
|
|
|
instance: ParticleInstance::new(time, rng.gen(), ParticleMode::CampfireSmoke, pos.0),
|
|
|
|
});
|
|
|
|
self.particles.push(Particles {
|
|
|
|
alive_until: now + Duration::from_secs(2),
|
|
|
|
instance: ParticleInstance::new(time, rng.gen(), ParticleMode::CampfireSmoke, pos.0),
|
|
|
|
});
|
|
|
|
self.particles.push(Particles {
|
|
|
|
alive_until: now + Duration::from_secs(2),
|
|
|
|
instance: ParticleInstance::new(time, rng.gen(), ParticleMode::CampfireSmoke, pos.0),
|
2020-07-19 07:16:06 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn maintain_bomb_particles(
|
|
|
|
&mut self,
|
|
|
|
renderer: &mut Renderer,
|
|
|
|
scene_data: &SceneData,
|
|
|
|
pos: &Pos,
|
|
|
|
) {
|
|
|
|
let time = scene_data.state.get_time();
|
|
|
|
let now = Instant::now();
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
|
2020-07-21 15:48:20 +00:00
|
|
|
// sparks
|
2020-07-19 07:16:06 +00:00
|
|
|
self.particles.push(Particles {
|
|
|
|
alive_until: now + Duration::from_millis(1500),
|
2020-07-21 15:48:20 +00:00
|
|
|
instance: ParticleInstance::new(time, rng.gen(), ParticleMode::GunPowderSpark, pos.0),
|
|
|
|
});
|
|
|
|
self.particles.push(Particles {
|
|
|
|
alive_until: now + Duration::from_millis(1500),
|
|
|
|
instance: ParticleInstance::new(time, rng.gen(), ParticleMode::GunPowderSpark, pos.0),
|
|
|
|
});
|
|
|
|
self.particles.push(Particles {
|
|
|
|
alive_until: now + Duration::from_millis(1500),
|
|
|
|
instance: ParticleInstance::new(time, rng.gen(), ParticleMode::GunPowderSpark, pos.0),
|
|
|
|
});
|
|
|
|
self.particles.push(Particles {
|
|
|
|
alive_until: now + Duration::from_millis(1500),
|
|
|
|
instance: ParticleInstance::new(time, rng.gen(), ParticleMode::GunPowderSpark, pos.0),
|
|
|
|
});
|
|
|
|
self.particles.push(Particles {
|
|
|
|
alive_until: now + Duration::from_millis(1500),
|
|
|
|
instance: ParticleInstance::new(time, rng.gen(), ParticleMode::GunPowderSpark, pos.0),
|
2020-07-19 07:16:06 +00:00
|
|
|
});
|
|
|
|
|
2020-07-21 15:48:20 +00:00
|
|
|
// smoke
|
2020-07-19 07:16:06 +00:00
|
|
|
self.particles.push(Particles {
|
|
|
|
alive_until: now + Duration::from_secs(2),
|
2020-07-21 15:48:20 +00:00
|
|
|
instance: ParticleInstance::new(time, rng.gen(), ParticleMode::CampfireSmoke, pos.0),
|
2020-07-19 07:16:06 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// fn maintain_pouch_particles(
|
|
|
|
// &mut self,
|
|
|
|
// renderer: &mut Renderer,
|
|
|
|
// scene_data: &SceneData,
|
|
|
|
// pos: &Pos,
|
|
|
|
// ) {
|
|
|
|
// let time = scene_data.state.get_time();
|
|
|
|
// let now = Instant::now();
|
|
|
|
// let mut rng = rand::thread_rng();
|
|
|
|
|
|
|
|
// let smoke_cpu_insts = vec![ParticleInstance::new(
|
|
|
|
// time,
|
|
|
|
// rng.gen(),
|
|
|
|
// ParticleMode::CampfireSmoke,
|
|
|
|
// pos.0,
|
|
|
|
// )];
|
|
|
|
|
|
|
|
// let smoke_cpu_insts = renderer
|
|
|
|
// .create_instances(&smoke_cpu_insts)
|
|
|
|
// .expect("Failed to upload particle instances to the GPU!");
|
|
|
|
|
|
|
|
// self.particles.push(Particles {
|
|
|
|
// alive_until: now + Duration::from_secs(1),
|
|
|
|
// instances: smoke_cpu_insts,
|
|
|
|
// });
|
|
|
|
// }
|
|
|
|
|
2020-07-15 15:45:47 +00:00
|
|
|
fn maintain_boost_particles(&mut self, renderer: &mut Renderer, scene_data: &SceneData) {
|
2020-07-13 13:44:28 +00:00
|
|
|
let state = scene_data.state;
|
|
|
|
let ecs = state.ecs();
|
|
|
|
let time = state.get_time();
|
|
|
|
let now = Instant::now();
|
2020-07-15 15:45:47 +00:00
|
|
|
let mut rng = rand::thread_rng();
|
2020-07-13 13:44:28 +00:00
|
|
|
|
2020-07-15 15:45:47 +00:00
|
|
|
for (_i, (_entity, pos, character_state)) in (
|
2020-07-13 13:44:28 +00:00
|
|
|
&ecs.entities(),
|
|
|
|
&ecs.read_storage::<Pos>(),
|
|
|
|
&ecs.read_storage::<CharacterState>(),
|
|
|
|
)
|
|
|
|
.join()
|
|
|
|
.enumerate()
|
|
|
|
{
|
2020-07-15 15:45:47 +00:00
|
|
|
if let CharacterState::Boost(_) = character_state {
|
|
|
|
self.particles.push(Particles {
|
|
|
|
alive_until: now + Duration::from_secs(15),
|
2020-07-21 15:48:20 +00:00
|
|
|
instance: ParticleInstance::new(
|
|
|
|
time,
|
|
|
|
rng.gen(),
|
|
|
|
ParticleMode::CampfireSmoke,
|
|
|
|
pos.0,
|
|
|
|
),
|
2020-07-11 10:37:19 +00:00
|
|
|
});
|
|
|
|
}
|
2020-07-05 12:10:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn render(
|
|
|
|
&self,
|
|
|
|
renderer: &mut Renderer,
|
|
|
|
globals: &Consts<Globals>,
|
|
|
|
lights: &Consts<Light>,
|
|
|
|
shadows: &Consts<Shadow>,
|
|
|
|
) {
|
2020-07-19 07:16:06 +00:00
|
|
|
let model = &self
|
|
|
|
.model_cache
|
|
|
|
.get(MODEL_KEY)
|
|
|
|
.expect("Expected particle model in cache");
|
|
|
|
|
2020-07-21 15:48:20 +00:00
|
|
|
renderer.render_particles(model, globals, &self.instances, lights, shadows);
|
2020-07-05 12:10:58 +00:00
|
|
|
}
|
|
|
|
}
|