...
 
Commits (2)
......@@ -108,4 +108,4 @@ Entities!(
[10, ARMOUR, armour, parts::Armour],
[11, INVENTORY, inventory, parts::Inventory],
[12, CIRCLE, circle, parts::Circle]
);
);
\ No newline at end of file
......@@ -4,6 +4,7 @@
// resolve contacts
use components::*;
use components::parts::*;
use components::systems::has_mask;
use std::f32;
use vec2d::Vec2d;
......@@ -18,15 +19,23 @@ use vec2d::Vec2d;
const DRAGCOEFICIENT: f32 = 0.05; // 0.05 feels balanced
const REAL_MAX: f32 = 1.0;
pub struct EntityRefs<'a> {
pub position: &'a mut Vec2d<f32>,
pub velocity: &'a mut Vec2d<f32>,
pub physics: &'a mut Physics,
}
/// The physics `Contact` resolver.
///
/// Works with an `Entities` object to get:
/// * Position
/// * Velocity
/// * Physics object
pub struct Contact {
pub first: u32,
pub second: Option<u32>,
pub struct Contact<'a> {
/// ID of the primary entity contact
pub first: EntityRefs<'a>,
/// ID of the secondary entity contact
pub second: Option<EntityRefs<'a>>,
pub first_move: Vec2d<f32>,
pub second_move: Vec2d<f32>,
pub restitution: f32,
......@@ -34,30 +43,25 @@ pub struct Contact {
pub penetration: f32,
}
impl Contact {
impl<'a> Contact<'a> {
/// The main contact resolution method
pub fn resolve(&mut self, entities: &mut Entities, dt: f32) {
self.resolve_velocity(entities, dt);
self.resolve_interpen(entities, dt);
pub fn resolve(&mut self, dt: f32) {
self.resolve_velocity(dt);
self.resolve_interpen(dt);
}
/// Called by `resolve_velocity`
pub fn calc_separating_v(&self, entities: &Entities, dt: f32) -> f32 {
if let Some(velocity) = entities.parts.velocity.get(&self.first) {
let mut relative_velocity = velocity.vel.clone();
if let Some(id) = self.second {
if let Some(other_v) = entities.parts.velocity.get(&id) {
relative_velocity -= other_v.vel
};
};
return (relative_velocity * self.contact_norm).magnitude();
}
0.0
pub fn calc_separating_v(&self, dt: f32) -> f32 {
let mut relative_velocity = self.first.velocity.clone();
if let Some(ref other) = self.second {
relative_velocity -= *other.velocity;
};
return (relative_velocity * self.contact_norm).magnitude();
}
pub fn resolve_velocity(&self, entities: &mut Entities, dt: f32) {
pub fn resolve_velocity(&mut self, dt: f32) {
// velocity in direction of contact
let separating_v = self.calc_separating_v(entities, dt);
let separating_v = self.calc_separating_v(dt);
if separating_v > 0.0 {
return;
}
......@@ -66,10 +70,7 @@ impl Contact {
let delta_velocity = new_separating_v - separating_v;
// apply velocity to each entity in proportion to their mass
let total_inverse_mass = match self.get_total_inverse_mass(entities) {
Some(inverse) => inverse,
None => return,
};
let total_inverse_mass = self.get_total_inverse_mass();
if total_inverse_mass <= 0.0 {
return;
}
......@@ -79,73 +80,47 @@ impl Contact {
let impulse_per_imass = self.contact_norm * impulse;
// Now, apply impulses
if let Some(velocity) = entities.parts.velocity.get_mut(&self.first) {
let tmp = velocity.vel.clone(); // need to clone because of mut rules
// getting imass this way is safe since earlier block would have returned if missing
let imass = entities.parts.physics[&self.first].inv_mass;
velocity.vel = tmp + impulse_per_imass * imass;
}
if let Some(id) = self.second {
if let Some(velocity) = entities.parts.velocity.get_mut(&id) {
let tmp = velocity.vel.clone();
let imass = entities.parts.physics[&self.first].inv_mass;
velocity.vel = tmp + impulse_per_imass * imass;
}
let tmp = self.first.velocity.clone(); // need to clone because of mut rules
let imass = self.first.physics.inv_mass; // copy type
*self.first.velocity = tmp + impulse_per_imass * imass;
if let Some(ref mut other) = self.second {
let tmp = other.velocity.clone();
let imass = self.first.physics.inv_mass;
*other.velocity = tmp + impulse_per_imass * imass;
}
}
pub fn get_total_inverse_mass(&self, entities: &Entities) -> Option<f32> {
match entities.parts.physics.get(&self.first) {
Some(first_physics) => {
match self.second {
Some(id) => {
match entities.parts.physics.get(&id) {
Some(sec_physics) => {
Some(first_physics.inv_mass + sec_physics.inv_mass)
}
None => Some(first_physics.inv_mass),
}
}
None => Some(first_physics.inv_mass),
}
}
None => None,
pub fn get_total_inverse_mass(&mut self) -> f32 {
if let Some(ref mut other) = self.second {
return self.first.physics.inv_mass + other.physics.inv_mass;
}
return self.first.physics.inv_mass;
}
pub fn resolve_interpen(&mut self, entities: &mut Entities, dt: f32) {
pub fn resolve_interpen(&mut self, dt: f32) {
if self.penetration < 0.0 {
return;
}
let total_inverse_mass = match self.get_total_inverse_mass(entities) {
Some(inverse) => inverse,
None => return,
};
let total_inverse_mass = self.get_total_inverse_mass();
if total_inverse_mass <= 0.0 {
return;
}
let move_per_imass = self.contact_norm * (self.penetration / total_inverse_mass);
// Calc movement amounts
self.first_move = move_per_imass * entities.parts.physics[&self.first].inv_mass;
if let Some(id) = self.second {
self.second_move = move_per_imass * -entities.parts.physics[&id].inv_mass;
} else {
self.second_move.x = 0.0;
self.second_move.y = 0.0;
self.first_move = move_per_imass * self.first.physics.inv_mass;
if let Some(ref other) = self.second {
self.second_move = move_per_imass * -other.physics.inv_mass;
};
// Apply penetration resolution to positions
if let Some(position) = entities.parts.position.get_mut(&self.first) {
position.pos += self.first_move;
};
if let Some(id) = self.second {
if let Some(position) = entities.parts.position.get_mut(&id) {
position.pos += self.second_move;
}
};
*self.first.position += self.first_move;
if let Some(ref mut other) = self.second {
*other.position += self.second_move;
}
}
}
......@@ -215,7 +190,7 @@ pub fn integrate(entity: u32, entities: &mut Entities, dt: f32) {
// damping required to avoid float errors
*velocity *= physics.damping.powf(dt);
// TODO: temporary, actual entitiy bounding update should be done elsewhere to not clutter this up
// TODO: temporary, actual entity bounding update should be done elsewhere to not clutter this up
if has_mask(mask, AABB) {
let aabb = &mut entities.parts.aabb.get_mut(&entity).unwrap().rect;
aabb.set_x(position.x as i32);
......@@ -224,7 +199,7 @@ pub fn integrate(entity: u32, entities: &mut Entities, dt: f32) {
physics.force_accum.x = 0.0;
physics.force_accum.y = 0.0;
// TODO: temporary
// TODO: temporary - acceleration should be controlled elsewhere and depend on the entity type
physics.acceleration.x = 0.0;
physics.acceleration.y = 0.0;
}
......@@ -255,7 +230,7 @@ pub fn resolve_contacts(contacts: &mut Vec<Contact>,
let mut max_index = num_contacts as usize;
let mut sep_vel;
for i in 0..num_contacts as usize {
sep_vel = contacts[i as usize].calc_separating_v(entities, dt);
sep_vel = contacts[i as usize].calc_separating_v(dt);
if sep_vel < max && (sep_vel < 0.0 || contacts[i].penetration > 0.0) {
max = sep_vel;
max_index = i;
......@@ -265,8 +240,8 @@ pub fn resolve_contacts(contacts: &mut Vec<Contact>,
break;
}
// resolve this contact
contacts[max_index as usize].resolve(&mut entities, dt);
contacts[max_index as usize].resolve(dt);
iterations_used += 1;
}
contacts.clear();
}