add test for ball resting on a horizontal surface

also improved numerical stability in this case by factoring face collisions
so a divide happens later.
main
_ 2021-12-18 20:03:01 +00:00
parent 96436d6c36
commit dec16c4079
1 changed files with 73 additions and 11 deletions

View File

@ -121,11 +121,12 @@ pub fn get_candidate (world: &[Triangle], p0: Vec3, p1: Vec3, radius: f32)
let passed_plane = distance_to_face0 > 0.0 && distance_to_face1 < 0.0; let passed_plane = distance_to_face0 > 0.0 && distance_to_face1 < 0.0;
if passed_plane { if passed_plane {
let t = distance_to_face0 / (distance_to_face0 - distance_to_face1); let denom = distance_to_face0 - distance_to_face1;
let t_times_denom = distance_to_face0;
// Because of previous early returns we know that 0.0 < t < 1.0 // Because of previous early returns we know that 0.0 < t < 1.0
let p_impact = p0 * (1.0 - t) + p1 * (t); let p_impact_times_denom = p0 * (denom - t_times_denom) + p1 * (t_times_denom);
let impact_inside_tri = (|| { let impact_inside_tri = (|| {
for j in 0..3 { for j in 0..3 {
@ -133,7 +134,7 @@ pub fn get_candidate (world: &[Triangle], p0: Vec3, p1: Vec3, radius: f32)
let b = tri.verts [(j + 1) % 3]; let b = tri.verts [(j + 1) % 3];
let tangent = Vec3::cross (b - a, normal); let tangent = Vec3::cross (b - a, normal);
if Vec3::dot (tangent, p_impact - a) < 0.0 { if Vec3::dot (tangent, p_impact_times_denom - a * denom) < 0.0 {
return false; return false;
} }
} }
@ -144,8 +145,8 @@ pub fn get_candidate (world: &[Triangle], p0: Vec3, p1: Vec3, radius: f32)
// Stop it // Stop it
let c = Collision { let c = Collision {
t, t: t_times_denom / denom,
p_impact, p_impact: p_impact_times_denom / denom,
normal, normal,
i, i,
}; };
@ -343,12 +344,6 @@ mod test {
fn test_physics () { fn test_physics () {
// Remember, Z is up // Remember, Z is up
let params = Params {
dt: 1.0,
gravity: (0.0, 0.0, 0.0).into (),
margin: 0.00125,
};
let world: Vec <_> = vec! [ let world: Vec <_> = vec! [
( (
(0.0, 0.0, 0.0), (0.0, 0.0, 0.0),
@ -367,6 +362,12 @@ mod test {
}) })
.collect (); .collect ();
let params = Params {
dt: 1.0,
gravity: (0.0, 0.0, 0.0).into (),
margin: 0.00125,
};
let magic_0 = f32::sqrt (f32::powf (0.5 + params.margin, 2.0) / 2.0); let magic_0 = f32::sqrt (f32::powf (0.5 + params.margin, 2.0) / 2.0);
for ((radius, body_before), e) in [ for ((radius, body_before), e) in [
@ -450,5 +451,66 @@ mod test {
assert_eq! (a.triangles_hit, e.triangles_hit); assert_eq! (a.triangles_hit, e.triangles_hit);
assert_eq! (a.kill, e.kill); assert_eq! (a.kill, e.kill);
} }
// With no bounce, a ball should settle on a flat triangle in one
// frame and reach a steady state
let params = Params {
dt: 1.0,
gravity: (0.0, 0.0, -1.0).into (),
margin: 0.00125,
};
let radius = 0.5;
let body_before = PhysicsBody {
pos: (0.5, 0.5, 2.0).into (),
vel: (0.0, 0.0, -4.0).into (),
};
assert_eq! (
get_candidate (&world, (0.5, 0.5, 2.0).into (), (0.5, 0.5, -3.0).into (), radius),
Collision {
t: 0.3,
p_impact: (0.5, 0.5, 0.5).into (),
normal: (0.0, 0.0, 1.0).into (),
i: 0,
},
);
let a = physics_step (&params, &world, radius, &body_before);
let e = PhysicsResult {
body: PhysicsBody {
pos: (0.5, 0.5, 0.5 + params.margin).into (),
vel: (0.0, 0.0, 0.0).into (),
},
triangles_hit: vec! [0],
kill: false,
};
// Fixed point should be here
// If this test passes, at least a ball can rest on a single horizontal
// triangle. If it fails, that means the ball is not resting
// (maybe the velocity is non-zero even if the position is stable)
// or the ball is going to gain energy and bounce away, or it's going
// to slide through the triangle.
let body_before = PhysicsBody {
pos: (0.5, 0.5, 0.5 + params.margin).into (),
vel: (0.0, 0.0, 0.0).into (),
};
let a = physics_step (&params, &world, radius, &body_before);
let e = PhysicsResult {
body: PhysicsBody {
pos: (0.5, 0.5, 0.5 + params.margin).into (),
vel: (0.0, 0.0, 0.0).into (),
},
triangles_hit: vec! [0],
kill: false,
};
assert_eq! (a, e);
} }
} }