Hello,
I have been trying to make a 3D physics engine for about 2 weeks now and I've got something fairly primitive working. It uses Minkowski Portal Refinement to generate two contact points on the surfaces of each object, then I use a simple caching system with a hash table to generate multiple contact points over a few iterations, invalidating them when the distance between the two surfaces is too great. However, the collision resolution looks off a lot of the time, especially with rotation and friction.
I have a test scene with a static floor object and a dynamic cube object that's moving forwards at a velocity of 10 units per second, and falling due to gravity. I'm running it with the simulation speed set quite low so that I can see what's happening.
This video shows what it looks like with rotation, but no friction: https://cloud.disroot.org/s/DJrPSQka4we6pSp
As can be observed, the object has a little wobble (from the way my contact points move around, I think), but has mostly acceptable stability, at least for my purposes.
The red crosses are the contact points, and the lines drawn from their centres are the impulse vectors for the resolution.
However, when I add friction, this happens: https://cloud.disroot.org/s/RXR4bJcrr9NXi46
The object starts out looking alright when it first hits the floor, it starts to tip over because of its forward movement, which looks correct enough to me. However, when it fully tips over causing the other edge to touch the ground, it spins incorrectly and generally looks really bad. Again, the red crosses are the contact points, but the lines drawn from them are the friction impulse vectors.
I think what's happening is sometimes it generates huge friction impulses in the wrong direction which throw it out of whack and cause it to go spinning off into Narnia. I'm not entirely sure why, though, which is where I'd like a bit of help, maybe some pointers in the right direction at least.
This is how I'm generating and applying my friction impulse:
rv =
b.velocity + b.angular_velocity.cross(rb) -
a.velocity - a.angular_velocity.cross(ra);
t = -(rv - (n * rv.dot(n))).normalise();
jt = (-rv.dot(t)) / ms / divisor;
if (kindof(jt, 0.0f)) {
return;
}
if (fabsf(jt) < j * sf) {
impulse = t * jt;
} else {
impulse = t * -j * df;
}
a.velocity -= a.inv_mass * impulse;
b.velocity += b.inv_mass * impulse;
a.angular_velocity -= a.inv_moment * ra.cross(impulse);
b.angular_velocity += b.inv_moment * rb.cross(impulse);
To explain some of my (admittedly bad) variable names: ms
is the sum of both objects' inverse masses, divisor
is the contact count as a real number, n
is the normal, and ra
& rb
are the contact points relative to objects a and b respectively, kindof
is a function that checks if a float is “close enough” to a value by a very small margin (1.192092896e-07). sf
and df
are the averages of the static and dynamic friction coefficients of each object.
My full collision resolution code is here: https://bin.disroot.org/?ee0f95956a2468b1#8S4Kuim614rySGwaZQ17UxonKYxAB3FNh5d5WDqFCnZ1
To explain it a bit, Contact_List
is an array container of contacts. Each contact is stored as a pair of points, on the surface of and in the local space of each object. inv_moment
is a 3x3 matrix containing the inverse moment of inertia for each object. In this simple case of cubes, I generate it with this formula and then take the inverse (1/x) of each element:
My implementation is based on Randy Gaul's 2D physics tutorial - maybe there are more things that I need to do in 3D? I am having a little trouble finding resources for 3D physics. These guides were very helpful for me in understanding the maths, but they don't cover friction unfortunately: https://research.ncl.ac.uk/game/mastersdegree/gametechnologies/physicstutorials/
Does anything look obviously wrong with what I'm doing? Is there something that I'm not taking into account or am I using an incorrect technique?