use std::fmt::Debug;
use std::marker;
use std::ops::{Add, Mul, Sub};
use cgmath::{BaseFloat, EuclideanSpace, InnerSpace, Rotation, VectorSpace, Zero};
use core::{
resolve_contact, ApplyAngular, ContactEvent, Inertia, Mass, PartialCrossProduct,
PhysicalEntity, ResolveData, Velocity,
};
use core::{NextFrame, Pose};
use shrev::{EventChannel, ReaderId};
use specs::prelude::{Component, Entity, Read, ReadStorage, Resources, System, WriteStorage};
pub struct ContactResolutionSystem<P, R, I, A, O, T>
where
P: EuclideanSpace + 'static,
P::Diff: Debug,
{
contact_reader: Option<ReaderId<ContactEvent<Entity, P>>>,
m: marker::PhantomData<(R, I, A, O, T)>,
}
impl<P, R, I, A, O, T> ContactResolutionSystem<P, R, I, A, O, T>
where
T: Pose<P, R>,
P: EuclideanSpace,
P::Scalar: BaseFloat,
P::Diff: VectorSpace + InnerSpace + Debug + PartialCrossProduct<P::Diff, Output = O>,
R: Rotation<P> + ApplyAngular<P::Scalar, A>,
O: PartialCrossProduct<P::Diff, Output = P::Diff>,
A: PartialCrossProduct<P::Diff, Output = P::Diff> + Clone + Zero,
for<'b> &'b A: Sub<O, Output = A> + Add<O, Output = A>,
I: Inertia<Orientation = R> + Mul<O, Output = O>,
{
pub fn new() -> Self {
Self {
contact_reader: None,
m: marker::PhantomData,
}
}
}
impl<'a, P, R, I, A, O, T> System<'a> for ContactResolutionSystem<P, R, I, A, O, T>
where
T: Pose<P, R> + Component + Send + Sync + 'static,
P: EuclideanSpace + Send + Sync + 'static,
P::Scalar: BaseFloat + Send + Sync + 'static,
P::Diff: VectorSpace
+ InnerSpace
+ Debug
+ Send
+ Sync
+ 'static
+ PartialCrossProduct<P::Diff, Output = O>,
R: Rotation<P> + ApplyAngular<P::Scalar, A> + Send + Sync + 'static,
O: PartialCrossProduct<P::Diff, Output = P::Diff>,
A: PartialCrossProduct<P::Diff, Output = P::Diff> + Clone + Zero + Send + Sync + 'static,
for<'b> &'b A: Sub<O, Output = A> + Add<O, Output = A>,
I: Inertia<Orientation = R> + Mul<O, Output = O> + Send + Sync + 'static,
{
type SystemData = (
Read<'a, EventChannel<ContactEvent<Entity, P>>>,
ReadStorage<'a, Mass<P::Scalar, I>>,
ReadStorage<'a, PhysicalEntity<P::Scalar>>,
WriteStorage<'a, NextFrame<Velocity<P::Diff, A>>>,
ReadStorage<'a, T>,
WriteStorage<'a, NextFrame<T>>,
);
fn run(&mut self, data: Self::SystemData) {
let (contacts, masses, entities, mut next_velocities, poses, mut next_poses) = data;
for contact in contacts.read(&mut self.contact_reader.as_mut().unwrap()) {
let change_set = match (
from_storage(
contact.bodies.0,
&next_velocities,
&next_poses,
&poses,
&masses,
&entities,
),
from_storage(
contact.bodies.1,
&next_velocities,
&next_poses,
&poses,
&masses,
&entities,
),
) {
(Some(resolve_0), Some(resolve_1)) => {
Some(resolve_contact(&contact.contact, &resolve_0, &resolve_1))
}
_ => None,
};
if let Some(cs) = change_set {
cs.0.apply(
next_poses.get_mut(contact.bodies.0),
next_velocities.get_mut(contact.bodies.0),
);
cs.1.apply(
next_poses.get_mut(contact.bodies.1),
next_velocities.get_mut(contact.bodies.1),
);
}
}
}
fn setup(&mut self, res: &mut Resources) {
use specs::prelude::SystemData;
Self::SystemData::setup(res);
self.contact_reader = Some(
res.fetch_mut::<EventChannel<ContactEvent<Entity, P>>>()
.register_reader(),
);
}
}
fn from_storage<'a, P, T, R, A, I>(
entity: Entity,
next_velocities: &'a WriteStorage<NextFrame<Velocity<P::Diff, A>>>,
next_poses: &'a WriteStorage<NextFrame<T>>,
poses: &'a ReadStorage<T>,
masses: &'a ReadStorage<Mass<P::Scalar, I>>,
entities: &'a ReadStorage<PhysicalEntity<P::Scalar>>,
) -> Option<ResolveData<'a, T, P, R, I, A>>
where
P: EuclideanSpace + Send + Sync + 'static,
P::Scalar: BaseFloat + Send + Sync + 'static,
P::Diff: Send + Sync + 'static,
T: Pose<P, R> + Component + Send + Sync + 'static,
R: Rotation<P> + Send + Sync + 'static,
A: Clone + Send + Sync + 'static,
I: Clone + Send + Sync + 'static,
{
match (entities.get(entity), masses.get(entity), poses.get(entity)) {
(Some(e), Some(mass), Some(pose)) if e.active() => Some(ResolveData::new(
next_velocities.get(entity),
next_poses.get(entity).map(|p| &p.value).unwrap_or(pose),
mass,
e.material(),
)),
_ => None,
}
}