use crate::{
core::{
algebra::{Vector2, Vector3},
color::Color,
color_gradient::ColorGradient,
math::TriangleDefinition,
numeric_range::NumericRange,
pool::Handle,
visitor::{Visit, VisitResult, Visitor},
},
resource::texture::Texture,
scene::{
base::{Base, BaseBuilder},
graph::Graph,
node::Node,
},
};
use std::{
cell::Cell,
cmp::Ordering,
fmt::Debug,
ops::{Deref, DerefMut},
};
#[repr(C)]
#[derive(Debug)]
pub struct Vertex {
position: Vector3<f32>,
tex_coord: Vector2<f32>,
size: f32,
rotation: f32,
color: Color,
}
pub struct DrawData {
vertices: Vec<Vertex>,
triangles: Vec<TriangleDefinition>,
}
impl Default for DrawData {
fn default() -> Self {
Self {
vertices: Vec::new(),
triangles: Vec::new(),
}
}
}
impl DrawData {
fn clear(&mut self) {
self.vertices.clear();
self.triangles.clear();
}
pub fn vertices(&self) -> &[Vertex] {
&self.vertices
}
pub fn triangles(&self) -> &[TriangleDefinition] {
&self.triangles
}
}
#[derive(Clone, Debug)]
pub struct Particle {
pub position: Vector3<f32>,
pub velocity: Vector3<f32>,
pub size: f32,
alive: bool,
pub size_modifier: f32,
lifetime: f32,
pub initial_lifetime: f32,
pub rotation_speed: f32,
pub rotation: f32,
pub color: Color,
emitter_index: u32,
sqr_distance_to_camera: Cell<f32>,
}
impl Default for Particle {
fn default() -> Self {
Self {
position: Default::default(),
velocity: Default::default(),
size: 1.0,
alive: true,
size_modifier: 0.0,
lifetime: 0.0,
initial_lifetime: 2.0,
rotation_speed: 0.0,
rotation: 0.0,
emitter_index: 0,
color: Color::WHITE,
sqr_distance_to_camera: Cell::new(0.0),
}
}
}
impl Visit for Particle {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
self.position.visit("Pos", visitor)?;
self.velocity.visit("Vel", visitor)?;
self.size.visit("Size", visitor)?;
self.alive.visit("Alive", visitor)?;
self.size_modifier.visit("SizeMod", visitor)?;
self.lifetime.visit("LifeTime", visitor)?;
self.initial_lifetime.visit("InitLifeTime", visitor)?;
self.rotation_speed.visit("RotSpeed", visitor)?;
self.rotation.visit("Rotation", visitor)?;
self.color.visit("Color", visitor)?;
self.emitter_index.visit("EmitterIndex", visitor)?;
visitor.leave_region()
}
}
pub trait Emit {
fn emit(&self, particle_system: &ParticleSystem, particle: &mut Particle);
}
#[derive(Debug, Clone)]
pub struct BoxEmitter {
emitter: BaseEmitter,
half_width: f32,
half_height: f32,
half_depth: f32,
}
impl Deref for BoxEmitter {
type Target = BaseEmitter;
fn deref(&self) -> &Self::Target {
&self.emitter
}
}
impl DerefMut for BoxEmitter {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.emitter
}
}
impl BoxEmitter {
pub fn new(emitter: BaseEmitter, width: f32, height: f32, depth: f32) -> Self {
Self {
emitter,
half_width: width * 0.5,
half_height: height * 0.5,
half_depth: depth * 0.5,
}
}
pub fn half_width(&self) -> f32 {
self.half_width
}
pub fn set_half_width(&mut self, half_width: f32) {
self.half_width = half_width.max(0.0);
}
pub fn half_height(&self) -> f32 {
self.half_height
}
pub fn set_half_height(&mut self, half_height: f32) {
self.half_height = half_height.max(0.0);
}
pub fn half_depth(&self) -> f32 {
self.half_depth
}
pub fn set_half_depth(&mut self, half_depth: f32) {
self.half_depth = half_depth.max(0.0);
}
}
impl Default for BoxEmitter {
fn default() -> Self {
Self {
emitter: Default::default(),
half_width: 0.5,
half_height: 0.5,
half_depth: 0.5,
}
}
}
impl Emit for BoxEmitter {
fn emit(&self, _particle_system: &ParticleSystem, particle: &mut Particle) {
self.emitter.emit(particle);
particle.position = Vector3::new(
self.position.x + NumericRange::new(-self.half_width, self.half_width).random(),
self.position.y + NumericRange::new(-self.half_height, self.half_height).random(),
self.position.z + NumericRange::new(-self.half_depth, self.half_depth).random(),
)
}
}
impl Visit for BoxEmitter {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
self.emitter.visit("Emitter", visitor)?;
self.half_width.visit("HalfWidth", visitor)?;
self.half_height.visit("HalfHeight", visitor)?;
self.half_depth.visit("HalfDepth", visitor)?;
visitor.leave_region()
}
}
#[derive(Clone, Debug)]
pub struct CylinderEmitter {
emitter: BaseEmitter,
height: f32,
radius: f32,
}
impl Default for CylinderEmitter {
fn default() -> Self {
Self {
emitter: Default::default(),
height: 1.0,
radius: 0.5,
}
}
}
impl Deref for CylinderEmitter {
type Target = BaseEmitter;
fn deref(&self) -> &Self::Target {
&self.emitter
}
}
impl DerefMut for CylinderEmitter {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.emitter
}
}
impl Visit for CylinderEmitter {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
self.emitter.visit("Emitter", visitor)?;
self.radius.visit("Radius", visitor)?;
self.height.visit("Height", visitor)?;
visitor.leave_region()
}
}
impl Emit for CylinderEmitter {
fn emit(&self, _particle_system: &ParticleSystem, particle: &mut Particle) {
let scale: f32 = NumericRange::new(0.0, 1.0).random();
let theta = NumericRange::new(0.0, 2.0 * std::f32::consts::PI).random();
let z = NumericRange::new(0.0, self.height).random();
let radius = scale.sqrt() * self.radius;
let x = radius * theta.cos();
let y = radius * theta.sin();
particle.position = self.position + Vector3::new(x, y, z);
}
}
impl CylinderEmitter {
pub fn radius(&self) -> f32 {
self.radius
}
pub fn set_radius(&mut self, radius: f32) {
self.radius = radius.max(0.0);
}
pub fn height(&self) -> f32 {
self.height
}
pub fn set_height(&mut self, height: f32) {
self.height = height.max(0.0);
}
}
pub struct CylinderEmitterBuilder {
base: BaseEmitterBuilder,
height: f32,
radius: f32,
}
impl CylinderEmitterBuilder {
pub fn new(base: BaseEmitterBuilder) -> Self {
Self {
base,
height: 1.0,
radius: 0.5,
}
}
pub fn with_height(mut self, height: f32) -> Self {
self.height = height;
self
}
pub fn with_radius(mut self, radius: f32) -> Self {
self.radius = radius;
self
}
pub fn build(self) -> Emitter {
Emitter::Cylinder(CylinderEmitter {
emitter: self.base.build(),
height: self.height,
radius: self.radius,
})
}
}
pub struct BoxEmitterBuilder {
base: BaseEmitterBuilder,
width: f32,
height: f32,
depth: f32,
}
impl BoxEmitterBuilder {
pub fn new(base: BaseEmitterBuilder) -> Self {
Self {
base,
width: 1.0,
height: 1.0,
depth: 1.0,
}
}
pub fn with_width(mut self, width: f32) -> Self {
self.width = width;
self
}
pub fn with_height(mut self, height: f32) -> Self {
self.height = height;
self
}
pub fn with_depth(mut self, depth: f32) -> Self {
self.depth = depth;
self
}
pub fn build(self) -> Emitter {
Emitter::Box(BoxEmitter {
emitter: self.base.build(),
half_width: self.width * 0.5,
half_height: self.height * 0.5,
half_depth: self.depth * 0.5,
})
}
}
#[derive(Debug, Clone)]
pub struct SphereEmitter {
emitter: BaseEmitter,
radius: f32,
}
impl Deref for SphereEmitter {
type Target = BaseEmitter;
fn deref(&self) -> &Self::Target {
&self.emitter
}
}
impl DerefMut for SphereEmitter {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.emitter
}
}
impl Default for SphereEmitter {
fn default() -> Self {
Self {
emitter: BaseEmitter::default(),
radius: 0.5,
}
}
}
impl SphereEmitter {
pub fn new(emitter: BaseEmitter, radius: f32) -> Self {
Self { emitter, radius }
}
pub fn radius(&self) -> f32 {
self.radius
}
pub fn set_radius(&mut self, radius: f32) {
self.radius = radius.max(0.0);
}
}
impl Visit for SphereEmitter {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
self.emitter.visit("Emitter", visitor)?;
self.radius.visit("Radius", visitor)?;
visitor.leave_region()
}
}
impl Emit for SphereEmitter {
fn emit(&self, _particle_system: &ParticleSystem, particle: &mut Particle) {
self.emitter.emit(particle);
let phi = NumericRange::new(0.0, std::f32::consts::PI).random();
let theta = NumericRange::new(0.0, 2.0 * std::f32::consts::PI).random();
let radius = NumericRange::new(0.0, self.radius).random();
let cos_theta = theta.cos();
let sin_theta = theta.sin();
let cos_phi = phi.cos();
let sin_phi = phi.sin();
particle.position = self.position
+ Vector3::new(
radius * sin_theta * cos_phi,
radius * sin_theta * sin_phi,
radius * cos_theta,
);
}
}
pub struct SphereEmitterBuilder {
base: BaseEmitterBuilder,
radius: f32,
}
impl SphereEmitterBuilder {
pub fn new(base: BaseEmitterBuilder) -> Self {
Self { base, radius: 0.5 }
}
pub fn with_radius(mut self, radius: f32) -> Self {
self.radius = radius;
self
}
pub fn build(self) -> Emitter {
Emitter::Sphere(SphereEmitter {
emitter: self.base.build(),
radius: self.radius,
})
}
}
#[derive(Debug)]
pub enum Emitter {
Unknown,
Box(BoxEmitter),
Sphere(SphereEmitter),
Cylinder(CylinderEmitter),
}
impl Emitter {
pub fn new(id: i32) -> Result<Self, String> {
match id {
0 => Ok(Self::Unknown),
1 => Ok(Self::Box(Default::default())),
2 => Ok(Self::Sphere(Default::default())),
3 => Ok(Self::Cylinder(Default::default())),
_ => Err(format!("Invalid emitter id {}!", id)),
}
}
pub fn id(&self) -> i32 {
match self {
Self::Unknown => 0,
Self::Box(_) => 1,
Self::Sphere(_) => 2,
Self::Cylinder(_) => 3,
}
}
}
macro_rules! static_dispatch {
($self:ident, $func:ident, $($args:expr),*) => {
match $self {
Emitter::Unknown => panic!("Unknown emitter must not be used!"),
Emitter::Box(v) => v.$func($($args),*),
Emitter::Sphere(v) => v.$func($($args),*),
Emitter::Cylinder(v) => v.$func($($args),*),
}
};
}
impl Emit for Emitter {
fn emit(&self, particle_system: &ParticleSystem, particle: &mut Particle) {
static_dispatch!(self, emit, particle_system, particle)
}
}
impl Clone for Emitter {
fn clone(&self) -> Self {
match self {
Self::Unknown => panic!("Unknown emitter kind is not supported"),
Self::Box(box_emitter) => Self::Box(box_emitter.clone()),
Self::Sphere(sphere_emitter) => Self::Sphere(sphere_emitter.clone()),
Self::Cylinder(cylinder) => Self::Cylinder(cylinder.clone()),
}
}
}
impl Visit for Emitter {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
let mut kind_id: i32 = self.id();
kind_id.visit("KindId", visitor)?;
if visitor.is_reading() {
*self = Emitter::new(kind_id)?;
}
static_dispatch!(self, visit, name, visitor)
}
}
impl Deref for Emitter {
type Target = BaseEmitter;
fn deref(&self) -> &Self::Target {
static_dispatch!(self, deref,)
}
}
impl DerefMut for Emitter {
fn deref_mut(&mut self) -> &mut Self::Target {
static_dispatch!(self, deref_mut,)
}
}
impl Default for Emitter {
fn default() -> Self {
Self::Unknown
}
}
#[derive(Copy, Clone, Debug)]
pub enum ParticleLimit {
Unlimited,
Strict(u32),
}
impl Visit for ParticleLimit {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
let mut amount = match self {
Self::Unlimited => -1,
Self::Strict(value) => *value as i32,
};
amount.visit("Amount", visitor)?;
if visitor.is_reading() {
*self = if amount < 0 {
Self::Unlimited
} else {
Self::Strict(amount as u32)
};
}
visitor.leave_region()
}
}
#[derive(Debug)]
pub struct BaseEmitter {
position: Vector3<f32>,
particle_spawn_rate: u32,
max_particles: ParticleLimit,
lifetime: NumericRange,
size: NumericRange,
size_modifier: NumericRange,
x_velocity: NumericRange,
y_velocity: NumericRange,
z_velocity: NumericRange,
rotation_speed: NumericRange,
rotation: NumericRange,
alive_particles: Cell<u32>,
time: f32,
particles_to_spawn: usize,
resurrect_particles: bool,
spawned_particles: u64,
}
pub struct BaseEmitterBuilder {
position: Option<Vector3<f32>>,
particle_spawn_rate: Option<u32>,
max_particles: Option<u32>,
lifetime: Option<NumericRange>,
size: Option<NumericRange>,
size_modifier: Option<NumericRange>,
x_velocity: Option<NumericRange>,
y_velocity: Option<NumericRange>,
z_velocity: Option<NumericRange>,
rotation_speed: Option<NumericRange>,
rotation: Option<NumericRange>,
resurrect_particles: bool,
}
impl Default for BaseEmitterBuilder {
fn default() -> Self {
Self::new()
}
}
impl BaseEmitterBuilder {
pub fn new() -> Self {
Self {
position: None,
particle_spawn_rate: None,
max_particles: None,
lifetime: None,
size: None,
size_modifier: None,
x_velocity: None,
y_velocity: None,
z_velocity: None,
rotation_speed: None,
rotation: None,
resurrect_particles: true,
}
}
pub fn with_position(mut self, position: Vector3<f32>) -> Self {
self.position = Some(position);
self
}
pub fn with_spawn_rate(mut self, rate: u32) -> Self {
self.particle_spawn_rate = Some(rate);
self
}
pub fn with_max_particles(mut self, value: u32) -> Self {
self.max_particles = Some(value);
self
}
pub fn with_lifetime_range(mut self, time_range: NumericRange) -> Self {
self.lifetime = Some(time_range);
self
}
pub fn with_size_range(mut self, size_range: NumericRange) -> Self {
self.size = Some(size_range);
self
}
pub fn with_size_modifier_range(mut self, mod_range: NumericRange) -> Self {
self.size_modifier = Some(mod_range);
self
}
pub fn with_x_velocity_range(mut self, x_vel_range: NumericRange) -> Self {
self.x_velocity = Some(x_vel_range);
self
}
pub fn with_y_velocity_range(mut self, y_vel_range: NumericRange) -> Self {
self.y_velocity = Some(y_vel_range);
self
}
pub fn with_z_velocity_range(mut self, z_vel_range: NumericRange) -> Self {
self.z_velocity = Some(z_vel_range);
self
}
pub fn with_rotation_speed_range(mut self, speed_range: NumericRange) -> Self {
self.rotation_speed = Some(speed_range);
self
}
pub fn with_rotation_range(mut self, angle_range: NumericRange) -> Self {
self.rotation = Some(angle_range);
self
}
pub fn resurrect_particles(mut self, value: bool) -> Self {
self.resurrect_particles = value;
self
}
pub fn build(self) -> BaseEmitter {
BaseEmitter {
position: self.position.unwrap_or_default(),
particle_spawn_rate: self.particle_spawn_rate.unwrap_or(25),
max_particles: self
.max_particles
.map_or(ParticleLimit::Unlimited, ParticleLimit::Strict),
lifetime: self
.lifetime
.unwrap_or_else(|| NumericRange::new(5.0, 10.0)),
size: self.size.unwrap_or_else(|| NumericRange::new(0.125, 0.250)),
size_modifier: self
.size_modifier
.unwrap_or_else(|| NumericRange::new(0.0005, 0.0010)),
x_velocity: self
.x_velocity
.unwrap_or_else(|| NumericRange::new(-0.001, 0.001)),
y_velocity: self
.y_velocity
.unwrap_or_else(|| NumericRange::new(-0.001, 0.001)),
z_velocity: self
.z_velocity
.unwrap_or_else(|| NumericRange::new(-0.001, 0.001)),
rotation_speed: self
.rotation_speed
.unwrap_or_else(|| NumericRange::new(-0.02, 0.02)),
rotation: self
.rotation
.unwrap_or_else(|| NumericRange::new(-std::f32::consts::PI, std::f32::consts::PI)),
alive_particles: Cell::new(0),
time: 0.0,
particles_to_spawn: 0,
resurrect_particles: self.resurrect_particles,
spawned_particles: 0,
}
}
}
impl BaseEmitter {
pub fn tick(&mut self, dt: f32) {
self.time += dt;
let time_amount_per_particle = 1.0 / self.particle_spawn_rate as f32;
let mut particle_count = (self.time / time_amount_per_particle) as u32;
self.time -= time_amount_per_particle * particle_count as f32;
if let ParticleLimit::Strict(max_particles) = self.max_particles {
let alive_particles = self.alive_particles.get();
if alive_particles < max_particles && alive_particles + particle_count > max_particles {
particle_count = max_particles - particle_count;
}
if !self.resurrect_particles && self.spawned_particles > u64::from(max_particles) {
self.particles_to_spawn = 0;
return;
}
}
self.particles_to_spawn = particle_count as usize;
self.spawned_particles += self.particles_to_spawn as u64;
}
pub fn emit(&self, particle: &mut Particle) {
particle.lifetime = 0.0;
particle.initial_lifetime = self.lifetime.random();
particle.color = Color::WHITE;
particle.size = self.size.random();
particle.size_modifier = self.size_modifier.random();
particle.velocity = Vector3::new(
self.x_velocity.random(),
self.y_velocity.random(),
self.z_velocity.random(),
);
particle.rotation = self.rotation.random();
particle.rotation_speed = self.rotation_speed.random();
}
pub fn set_position(&mut self, position: Vector3<f32>) -> &mut Self {
self.position = position;
self
}
pub fn position(&self) -> Vector3<f32> {
self.position
}
pub fn set_spawn_rate(&mut self, rate: u32) -> &mut Self {
self.particle_spawn_rate = rate;
self
}
pub fn spawn_rate(&self) -> u32 {
self.particle_spawn_rate
}
pub fn set_max_particles(&mut self, max: ParticleLimit) -> &mut Self {
self.max_particles = max;
self
}
pub fn max_particles(&self) -> ParticleLimit {
self.max_particles
}
pub fn set_life_time_range(&mut self, range: NumericRange) -> &mut Self {
self.lifetime = range;
self
}
pub fn life_time_range(&self) -> NumericRange {
self.lifetime
}
pub fn set_size_range(&mut self, range: NumericRange) -> &mut Self {
self.size = range;
self
}
pub fn size_range(&self) -> NumericRange {
self.size
}
pub fn set_size_modifier_range(&mut self, range: NumericRange) -> &mut Self {
self.size_modifier = range;
self
}
pub fn size_modifier_range(&self) -> NumericRange {
self.size_modifier
}
pub fn set_x_velocity_range(&mut self, range: NumericRange) -> &mut Self {
self.x_velocity = range;
self
}
pub fn x_velocity_range(&self) -> NumericRange {
self.x_velocity
}
pub fn set_y_velocity_range(&mut self, range: NumericRange) -> &mut Self {
self.y_velocity = range;
self
}
pub fn y_velocity_range(&self) -> NumericRange {
self.y_velocity
}
pub fn set_z_velocity_range(&mut self, range: NumericRange) -> &mut Self {
self.z_velocity = range;
self
}
pub fn z_velocity_range(&self) -> NumericRange {
self.z_velocity
}
pub fn set_rotation_speed_range(&mut self, range: NumericRange) -> &mut Self {
self.rotation_speed = range;
self
}
pub fn rotation_speed_range(&self) -> NumericRange {
self.rotation_speed
}
pub fn set_rotation_range(&mut self, range: NumericRange) -> &mut Self {
self.rotation = range;
self
}
pub fn rotation_range(&self) -> NumericRange {
self.rotation
}
pub fn enable_particle_resurrection(&mut self, state: bool) -> &mut Self {
self.resurrect_particles = state;
self
}
pub fn is_particles_resurrects(&self) -> bool {
self.resurrect_particles
}
pub fn spawned_particles(&self) -> u64 {
self.spawned_particles
}
}
impl Visit for BaseEmitter {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
self.position.visit("Position", visitor)?;
self.particle_spawn_rate.visit("SpawnRate", visitor)?;
self.max_particles.visit("MaxParticles", visitor)?;
self.lifetime.visit("LifeTime", visitor)?;
self.size.visit("Size", visitor)?;
self.size_modifier.visit("SizeModifier", visitor)?;
self.x_velocity.visit("XVelocity", visitor)?;
self.y_velocity.visit("YVelocity", visitor)?;
self.z_velocity.visit("ZVelocity", visitor)?;
self.rotation_speed.visit("RotationSpeed", visitor)?;
self.rotation.visit("Rotation", visitor)?;
self.alive_particles.visit("AliveParticles", visitor)?;
self.time.visit("Time", visitor)?;
self.resurrect_particles
.visit("ResurrectParticles", visitor)?;
self.spawned_particles.visit("SpawnedParticles", visitor)?;
visitor.leave_region()
}
}
impl Clone for BaseEmitter {
fn clone(&self) -> Self {
Self {
position: self.position,
particle_spawn_rate: self.particle_spawn_rate,
max_particles: self.max_particles,
lifetime: self.lifetime,
size: self.size,
size_modifier: self.size_modifier,
x_velocity: self.x_velocity,
y_velocity: self.y_velocity,
z_velocity: self.z_velocity,
rotation_speed: self.rotation_speed,
rotation: self.rotation,
alive_particles: self.alive_particles.clone(),
time: self.time,
particles_to_spawn: 0,
resurrect_particles: self.resurrect_particles,
spawned_particles: self.spawned_particles,
}
}
}
impl Default for BaseEmitter {
fn default() -> Self {
Self {
position: Vector3::default(),
particle_spawn_rate: 100,
max_particles: ParticleLimit::Unlimited,
lifetime: NumericRange::new(5.0, 10.0),
size: NumericRange::new(0.125, 0.250),
size_modifier: NumericRange::new(0.0005, 0.0010),
x_velocity: NumericRange::new(-0.001, 0.001),
y_velocity: NumericRange::new(-0.001, 0.001),
z_velocity: NumericRange::new(-0.001, 0.001),
rotation_speed: NumericRange::new(-0.02, 0.02),
rotation: NumericRange::new(-std::f32::consts::PI, std::f32::consts::PI),
alive_particles: Cell::new(0),
time: 0.0,
particles_to_spawn: 0,
resurrect_particles: true,
spawned_particles: 0,
}
}
}
#[derive(Debug)]
pub struct ParticleSystem {
base: Base,
particles: Vec<Particle>,
free_particles: Vec<u32>,
pub emitters: Vec<Emitter>,
texture: Option<Texture>,
acceleration: Vector3<f32>,
color_over_lifetime: Option<ColorGradient>,
}
impl Deref for ParticleSystem {
type Target = Base;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for ParticleSystem {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
impl ParticleSystem {
pub fn raw_copy(&self) -> Self {
Self {
base: self.base.raw_copy(),
particles: self.particles.clone(),
free_particles: self.free_particles.clone(),
emitters: self.emitters.clone(),
texture: self.texture.clone(),
acceleration: self.acceleration,
color_over_lifetime: self.color_over_lifetime.clone(),
}
}
pub fn acceleration(&self) -> Vector3<f32> {
self.acceleration
}
pub fn set_acceleration(&mut self, accel: Vector3<f32>) {
self.acceleration = accel;
}
pub fn set_color_over_lifetime_gradient(&mut self, gradient: ColorGradient) {
self.color_over_lifetime = Some(gradient)
}
pub fn clear_particles(&mut self) {
self.particles.clear();
self.free_particles.clear();
for emitter in self.emitters.iter_mut() {
emitter.alive_particles.set(0);
}
}
pub fn update(&mut self, dt: f32) {
for emitter in self.emitters.iter_mut() {
emitter.tick(dt);
}
for (i, emitter) in self.emitters.iter().enumerate() {
for _ in 0..emitter.particles_to_spawn {
let mut particle = Particle {
emitter_index: i as u32,
..Particle::default()
};
emitter
.alive_particles
.set(emitter.alive_particles.get() + 1);
emitter.emit(self, &mut particle);
if let Some(free_index) = self.free_particles.pop() {
self.particles[free_index as usize] = particle;
} else {
self.particles.push(particle);
}
}
}
let acceleration_offset = self.acceleration.scale(dt * dt);
for (i, particle) in self.particles.iter_mut().enumerate() {
if particle.alive {
particle.lifetime += dt;
if particle.lifetime >= particle.initial_lifetime {
self.free_particles.push(i as u32);
if let Some(emitter) = self.emitters.get(particle.emitter_index as usize) {
emitter
.alive_particles
.set(emitter.alive_particles.get() - 1);
}
particle.alive = false;
particle.lifetime = particle.initial_lifetime;
} else {
particle.velocity += acceleration_offset;
particle.position += particle.velocity;
particle.size += particle.size_modifier * dt;
if particle.size < 0.0 {
particle.size = 0.0;
}
particle.rotation += particle.rotation_speed * dt;
if let Some(color_over_lifetime) = &self.color_over_lifetime {
let k = particle.lifetime / particle.initial_lifetime;
particle.color = color_over_lifetime.get_color(k);
} else {
particle.color = Color::WHITE;
}
}
}
}
}
pub fn generate_draw_data(
&self,
sorted_particles: &mut Vec<u32>,
draw_data: &mut DrawData,
camera_pos: &Vector3<f32>,
) {
sorted_particles.clear();
for (i, particle) in self.particles.iter().enumerate() {
if particle.alive {
let actual_position = particle.position + self.base.global_position();
particle
.sqr_distance_to_camera
.set((camera_pos - actual_position).norm_squared());
sorted_particles.push(i as u32);
}
}
let particles = &self.particles;
sorted_particles.sort_by(|a, b| {
let particle_a = particles.get(*a as usize).unwrap();
let particle_b = particles.get(*b as usize).unwrap();
if particle_a.sqr_distance_to_camera < particle_b.sqr_distance_to_camera {
Ordering::Greater
} else if particle_a.sqr_distance_to_camera > particle_b.sqr_distance_to_camera {
Ordering::Less
} else {
Ordering::Equal
}
});
draw_data.clear();
for (i, particle_index) in sorted_particles.iter().enumerate() {
let particle = self.particles.get(*particle_index as usize).unwrap();
draw_data.vertices.push(Vertex {
position: particle.position,
tex_coord: Vector2::default(),
size: particle.size,
rotation: particle.rotation,
color: particle.color,
});
draw_data.vertices.push(Vertex {
position: particle.position,
tex_coord: Vector2::new(1.0, 0.0),
size: particle.size,
rotation: particle.rotation,
color: particle.color,
});
draw_data.vertices.push(Vertex {
position: particle.position,
tex_coord: Vector2::new(1.0, 1.0),
size: particle.size,
rotation: particle.rotation,
color: particle.color,
});
draw_data.vertices.push(Vertex {
position: particle.position,
tex_coord: Vector2::new(0.0, 1.0),
size: particle.size,
rotation: particle.rotation,
color: particle.color,
});
let base_index = (i * 4) as u32;
draw_data.triangles.push(TriangleDefinition([
base_index,
base_index + 1,
base_index + 2,
]));
draw_data.triangles.push(TriangleDefinition([
base_index,
base_index + 2,
base_index + 3,
]));
}
}
pub fn set_texture(&mut self, texture: Option<Texture>) {
self.texture = texture
}
pub fn texture(&self) -> Option<Texture> {
self.texture.clone()
}
pub fn texture_ref(&self) -> Option<&Texture> {
self.texture.as_ref()
}
}
impl Visit for ParticleSystem {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
self.particles.visit("Particles", visitor)?;
self.free_particles.visit("FreeParticles", visitor)?;
self.texture.visit("Texture", visitor)?;
self.emitters.visit("Emitters", visitor)?;
self.acceleration.visit("Acceleration", visitor)?;
self.color_over_lifetime.visit("ColorGradient", visitor)?;
self.base.visit("Base", visitor)?;
visitor.leave_region()
}
}
impl Default for ParticleSystem {
fn default() -> Self {
ParticleSystemBuilder::new(BaseBuilder::new()).build_particle_system()
}
}
pub struct ParticleSystemBuilder {
base_builder: BaseBuilder,
emitters: Vec<Emitter>,
texture: Option<Texture>,
acceleration: Vector3<f32>,
color_over_lifetime: Option<ColorGradient>,
}
impl ParticleSystemBuilder {
pub fn new(base_builder: BaseBuilder) -> Self {
Self {
base_builder,
emitters: Default::default(),
texture: None,
acceleration: Vector3::new(0.0, -9.81, 0.0),
color_over_lifetime: None,
}
}
pub fn with_emitters(mut self, emitters: Vec<Emitter>) -> Self {
self.emitters = emitters;
self
}
pub fn with_texture(mut self, texture: Texture) -> Self {
self.texture = Some(texture);
self
}
pub fn with_opt_texture(mut self, texture: Option<Texture>) -> Self {
self.texture = texture;
self
}
pub fn with_acceleration(mut self, acceleration: Vector3<f32>) -> Self {
self.acceleration = acceleration;
self
}
pub fn with_color_over_lifetime_gradient(mut self, color_over_lifetime: ColorGradient) -> Self {
self.color_over_lifetime = Some(color_over_lifetime);
self
}
fn build_particle_system(self) -> ParticleSystem {
ParticleSystem {
base: self.base_builder.build_base(),
particles: Vec::new(),
free_particles: Vec::new(),
emitters: self.emitters,
texture: self.texture.clone(),
acceleration: self.acceleration,
color_over_lifetime: self.color_over_lifetime,
}
}
pub fn build_node(self) -> Node {
Node::ParticleSystem(self.build_particle_system())
}
pub fn build(self, graph: &mut Graph) -> Handle<Node> {
graph.add_node(self.build_node())
}
}