use crate::math::curve::Curve;
use crate::{Vect, Sprite, Batch, Texture};
use crate::render::batch::{Target, VertexData};
use crate::math::rgba::{RGBA, Graph};
use rand::Rng;
use rand::rngs::ThreadRng;
#[inline]
pub fn in_range(random: &mut ThreadRng , min: f32, max:f32) -> f32 {
random.gen::<f32>() * (max - min) + min
}
pub trait Particle {
fn draw<T: Target>(&mut self, target: &mut T, position: Vect, rotation: f32, scale: f32, color: &RGBA);
}
pub trait Generator {
fn gen(&mut self) -> Vect;
}
#[derive(Copy, Clone)]
pub struct Property {
pub curve: Curve,
pub value: f32,
}
impl Property {
#[inline]
fn get(&self, t: f32) -> f32 {
self.curve.get_point(t).y * self.value
}
}
#[derive(Copy, Clone)]
pub struct RandomizedProperty {
value: f32,
offset: f32,
}
impl RandomizedProperty {
#[inline]
pub fn new(value: f32, offset: f32) -> Self {
Self { value, offset }
}
#[inline]
pub fn no_random(value: f32) -> Self {
Self{ value, offset: 0.0 }
}
#[inline]
pub fn get(&self, randomizer: &mut ThreadRng) -> f32 {
self.value + randomizer.gen::<f32>() * 2.0 * self.offset - self.offset
}
}
struct Object {
pos: Vect,
vel: Vect,
origin: Vect,
twerk: f32,
rot: f32,
live_time: f32,
progress: f32,
}
#[derive(Copy, Clone)]
pub struct Initial {
pub velocity: RandomizedProperty,
pub rotation: RandomizedProperty,
pub twerk: RandomizedProperty,
pub live_time: RandomizedProperty,
}
#[derive(Copy, Clone)]
pub struct Dynamic {
pub acceleration: Property,
pub twerk_acceleration: Property,
pub scale: Property,
}
#[derive(Clone)]
pub struct ParticleConfig {
pub rotation_relative_to_spawn_direction: bool,
pub inverted: bool,
pub gravity: Vect,
pub color: Graph,
pub spread: f32,
pub friction: f32,
pub origin_attraction: f32,
pub initial: Initial,
pub dynamic: Dynamic,
}
pub struct Gen {}
impl Generator for Gen {
fn gen(&mut self) -> Vect {
Vect::ZERO
}
}
struct Inner<G: Generator> {
random: ThreadRng,
config: ParticleConfig,
objects: Vec<Object>,
generator: G,
pos: Vect,
dir: Vect,
}
impl<G: Generator> Inner<G> {
pub fn new_obj(&mut self) -> Object {
let mut obj = Object{
pos: self.pos + self.generator.gen(),
vel: Vect::rad(self.dir.ang() + in_range(&mut self.random,-self.config.spread, self.config.spread),
self.config.initial.velocity.get(&mut self.random)),
origin: self.pos,
twerk: self.config.initial.twerk.get(&mut self.random),
rot: self.config.initial.rotation.get(&mut self.random),
live_time: self.config.initial.live_time.get(&mut self.random),
progress: 0.0
};
if self.config.rotation_relative_to_spawn_direction {
obj.rot += obj.vel.ang();
}
obj
}
}
pub struct ParticleSystem<P: Particle, G: Generator> {
shape: P,
progress: f32,
batch: Batch,
objects: Vec<Object>,
inner: Inner<G>,
}
pub fn new(config: ParticleConfig, texture: Texture, sprite: Sprite) -> ParticleSystem<Sprite, Gen> {
ParticleSystem::customized(Batch::new(texture), config, sprite, Gen {})
}
pub fn no_texture<P: Particle>(config: ParticleConfig, shape: P) -> ParticleSystem<P, Gen> {
ParticleSystem::customized(Batch::no_texture(), config, shape, Gen {})
}
impl<P: Particle, G: Generator> ParticleSystem<P, G> {
pub fn customized(batch: Batch, config: ParticleConfig, shape: P, generator: G) -> Self {
Self {
inner: Inner {
random: rand::thread_rng(),
config,
generator,
pos: Vect::ZERO,
objects: vec![],
dir: Vect::ZERO,
},
progress: 0.0,
batch,
shape,
objects: vec![],
}
}
pub fn spawn(&mut self, count: usize, pos: Vect, dir: Vect) {
self.objects.reserve(count);
self.inner.pos = pos;
self.inner.dir = dir;
if self.inner.config.inverted {
for _ in 0..count {
self.objects.insert(0, self.inner.new_obj())
}
} else {
for _ in 0..count {
self.objects.push(self.inner.new_obj())
}
}
}
pub fn update(&mut self, delta: f32) {
self.batch.clear();
for mut obj in self.objects.drain(..) {
obj.progress += delta;
self.progress = obj.progress / obj.live_time;
if self.progress >= 1.0 {
continue;
}
self.shape.draw(&mut self.batch, obj.pos, obj.rot, self.inner.config.dynamic.scale.get(self.progress), &self.inner.config.color.get_color(self.progress));
obj.vel += (obj.vel.norm() * (self.inner.config.dynamic.acceleration.get(self.progress)) + self.inner.config.gravity - obj.vel * self.inner.config.friction + (obj.origin - obj.pos).norm() * self.inner.config.origin_attraction) * delta;
obj.twerk += (self.inner.config.dynamic.twerk_acceleration.get(self.progress) - obj.twerk * self.inner.config.friction) * delta;
obj.pos += obj.vel * delta;
obj.rot += obj.twerk * delta;
self.inner.objects.push(obj);
}
std::mem::swap(&mut self.inner.objects, &mut self.objects);
}
#[inline]
pub fn draw<T: Target>(&self, target: &mut T) {
self.batch.draw(target);
}
}