✅ 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
parent
96436d6c36
commit
dec16c4079
|
@ -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 (¶ms, &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 (¶ms, &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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue