use std::marker; use std::time::Instant; use amethyst::assets::Handle; use amethyst::core::cgmath::{ Array, EuclideanSpace, InnerSpace, Quaternion, Rotation, Vector3, Zero, }; use amethyst::core::{GlobalTransform, Transform}; use amethyst::ecs::prelude::{Entities, Entity, Join, ReadExpect, System, WriteStorage}; use amethyst::renderer::{Material, Mesh}; use amethyst_rhusics::{AsTransform, Convert}; use collision::{Bound, ComputeBound, Primitive, Union}; use rand::Rand; use rhusics_core::{ BodyPose, CollisionMode, CollisionShape, CollisionStrategy, Inertia, Mass, PhysicalEntity, Pose, Velocity, }; use rhusics_ecs::PhysicalEntityParts; use super::{Emitter, Graphics, ObjectType}; /// Primitive emission system. /// /// Will spawn new primitives regularly, based on the `Emitter`s in the `World`. /// /// Showcase how to use `RigidBodyParts` to setup new collidable entities /// /// ### Type parameters: /// /// - `P`: Collision primitive (see `collision::primitive` for more information) /// - `B`: Bounding volume (usually `Aabb2`, `Aabb3` or `Sphere`) /// - `R`: Rotational quantity (`Basis2` or `Quaternion`) /// - `A`: Angular velocity quantity (`Scalar` or `Vector3`) /// - `I`: Inertia tensor (`Scalar` or `Matrix3`) pub struct EmissionSystem { primitive: P, m: marker::PhantomData<(B, R, A, I)>, } impl EmissionSystem { pub fn new(primitive: P) -> Self { Self { primitive, m: marker::PhantomData, } } } impl<'a, P, B, R, A, I> System<'a> for EmissionSystem where B: Bound + Union + Clone + Send + Sync + 'static, P: Primitive + ComputeBound + Clone + Send + Sync + 'static, P::Point: EuclideanSpace + Convert> + Send + Sync + 'static, ::Diff: Rand + InnerSpace + Array + Send + Sync + 'static, R: Rotation + Convert> + Send + Sync + 'static, A: Clone + Copy + Zero + Send + Sync + 'static, I: Inertia + Send + Sync + 'static, { type SystemData = ( Entities<'a>, EmitterParts<'a, P, B, R, A, I>, ReadExpect<'a, Graphics>, WriteStorage<'a, Emitter>, ); fn run(&mut self, (entities, mut parts, graphics, mut emitters): Self::SystemData) { let now = Instant::now(); for emitter in (&mut emitters).join() { if (now - emitter.last_emit) > emitter.emission_interval { emit_box::( entities.create(), &mut parts, graphics.mesh.clone(), &emitter, &self.primitive, ); emitter.last_emit = now.clone(); emitter.emitted += 1; } } } } fn emit_box( entity: Entity, parts: &mut EmitterParts, mesh: Handle, emitter: &Emitter, primitive: &P, ) where B: Bound + Union + Clone + Send + Sync + 'static, P: Primitive + ComputeBound + Clone + Send + Sync + 'static, P::Point: EuclideanSpace + Convert> + Send + Sync + 'static, ::Diff: Rand + InnerSpace + Array + Send + Sync + 'static, R: Rotation + Convert> + Send + Sync + 'static, A: Clone + Copy + Zero + Send + Sync + 'static, I: Inertia + Send + Sync + 'static, { use rand; use rand::Rng; let offset = ::Diff::rand(&mut rand::thread_rng()).normalize_to(0.1); let speed: ::Scalar = rand::thread_rng().gen_range(-10.0, 10.0); let position = emitter.location + offset; let pose = BodyPose::new(position, R::one()); let mut transform = pose.as_transform(); transform.scale = Vector3::from_value(0.05); parts.object_type.insert(entity, ObjectType::Box).unwrap(); parts.mesh.insert(entity, mesh).unwrap(); parts .material .insert(entity, emitter.material.clone()) .unwrap(); parts .global .insert(entity, GlobalTransform::default()) .unwrap(); parts.local.insert(entity, transform).unwrap(); parts .physical_entity .dynamic_entity( entity, CollisionShape::, B, ObjectType>::new_simple( CollisionStrategy::FullResolution, CollisionMode::Discrete, primitive.clone(), ), pose, Velocity::<::Diff, A>::from_linear(offset * speed), PhysicalEntity::default(), Mass::::new(1.), ).unwrap(); } #[derive(SystemData)] pub struct EmitterParts<'a, P, B, R, A, I> where B: Bound + Union + Clone + Send + Sync + 'static, P: Primitive + ComputeBound + Clone + Send + Sync + 'static, P::Point: EuclideanSpace + Convert> + Send + Sync + 'static, ::Diff: Rand + InnerSpace + Array + Send + Sync + 'static, R: Rotation + Convert> + Send + Sync + 'static, A: Clone + Copy + Zero + Send + Sync + 'static, I: Inertia + Send + Sync + 'static, { pub object_type: WriteStorage<'a, ObjectType>, pub mesh: WriteStorage<'a, Handle>, pub material: WriteStorage<'a, Material>, pub global: WriteStorage<'a, GlobalTransform>, pub local: WriteStorage<'a, Transform>, pub physical_entity: PhysicalEntityParts< 'a, P, ObjectType, R, ::Diff, A, I, B, BodyPose, >, }