Sign in to follow this  

incremental persistent manifold - sliding box on inclined plane problem

Recommended Posts

Hi all, sorry if I start annoying you with my posts :P

 

So I recently added a persistent manifold in my simple physics engine. It worked nice and fine, until I try to slide a box down an inclined plane. Basically the box slide with a bit "jerky" movement (along the normal), because the cached contact gets broken now and then (it causes contact to come and go). But first, here's how I store my contact.

A contact contains these information:

  • world position on body A
  • world position on body B
  • local position on body A
  • local position on body B
  • normal
  • depth

each frame, the manifold gets refreshed. then the contacts get removed if they become invalid. Here's how I check if a contact becomes invalid:

  1. based on current bodies transformation, compute world position on body A and B
  2. vAB = world position A - world position B;
  3. calculate new depth:
    depth = vAB.normal; (normal pointing to body A, so if it's positive, it means it's separated)
  4. "flatten" vAB on the collision plane:
    vAB -= normal * (vAB . normal);
  5. now we can calculate 2D distance along the collision plane
    dist = vAB.vAB;

     

  6. valid if:
    valid = depth < tolerance && dist < CACHE_DISTANCE_TOLERANCE;

and I simply remove them if they become invalid. For most of the time, the bodies tumble and come to rest just fine. I also add debug rendering of the manifold, they show just fine. But on the sliding case though (especially when sliding fast), there goes the jerky problem. It's not too visible if the box is sliding along the ground plane, because the friction will slow it down quickly, resulting in cache hit and faster convergence which will cause the body to come to rest. But on inclined plane, however, they're gonna keep moving (cause of gravity), and sometimes the friction wouldn't be strong enough to stop the bodies from moving along the inclined plane. Here's the problem shown in pictures (pardon me of the picture).

persistent_Manifold.png

 

 

  • At t0, the box has just fall down on the inclined plane. GJK+EPA calculate a collision point (the red is the collision point on body A, the blue one is on body B).
  • At t1, the box has moved, so the cached contact has to be refreshed. We recalculate the world position based on the local points stored in the contact. the blue point is still there (cause the box is not moving and has infinite mass), while the red has moved. Now the distance has become too far, and we remove the cached conact.
  • At t2, GJK+EPA get new collision point (it's on there because the box has rotated a bit, of course). And we add it to the manifold. Now repeat to t0. Basically, on an inclined plane, the manifold will have their contacts appear and dissapear quickly, resulting in jitter.

So how would you solve it? I tried to look for similar cases on youtube, but there's no videos on the subject. Also, this happens only for incremental manifold. For one shot manifold (clipping features), it doesn't happen of course, because you would always have full manifold that is stable. If you have any ideas on how to solve it, or have experienced such thing, please share your knowledge. Thank you, fellow gamedev.

 

EDIT: rusty english, mixed tense. sorry.

 

EDIT2: the problem is quite visible at the original simulation rate, which is 30 Hz. When I crank it up to 60 Hz, it's basically.....kinda disappear (no visible jitter). My conclusion is perhaps because of much higher refresh rate, whilst moving slower each frame (due to smaller timestep), the cache hit rate increases, so the convergence gets better. But still, c'mon, I intend to do it @30 Hz for my android game :(

Edited by Bow_vernon

Share this post


Link to post
Share on other sites

Well, obviously a distance based heuristic fails quickly in the case of moving objects. You described the problem very well. Alternatively you can use some kind of feature id. Or better you use a clipping algorithm and build the full manifold each frame. This solves this and all similar problems. I gave a talk on contact creation here. I also talk about stability of contact manifolds in the beginning. Maybe this is helpful:

 

http://box2d.org/files/GDC2015/DirkGregorius_Contacts.pdf

Edited by Dirk Gregorius

Share this post


Link to post
Share on other sites

Well, obviously a distance based heuristic fails quickly in the case of moving objects. You described the problem very well. Alternatively you can use some kind of feature id. Or better you use a clipping algorithm and build the full manifold each frame. This solves this and all similar problems. I gave a talk on contact creation here. I also talk about stability of contact manifolds in the beginning. Maybe this is helpful:

 

http://box2d.org/files/GDC2015/DirkGregorius_Contacts.pdf

Hi Dirk!

Thanks for the material. Yes, in fact I was inspired by your slides, although I didn't use "core" shapes margin+GJK closest points. I simply let bodies overlap and use EPA to compute contact points on both bodies. I already implemented the one full shot manifold, and indeed, there would be no such problem. Thing is, I love incremental manifold, it looks elegant, fast, also works with common convex shapes (I need to support cylinder + cone) with no hassle (only need support point function, no clipping stuffs). To my surprise, it worked well enough for stacking things. I didn't implement feature id though (I used to do that, but dang, that adds a lot of complexity). One thing that I note though, it seems to happen at low simulation rate, perhaps because I used simple baumgarte stabilization to correct position error. With huge timesteps, it cause huge overlap,  resulting in huge added energy, which might explain the jitter back and forth on inclined plane. I recently implement split impulse and sort contacts from bottom to top, so lowest manifold gets processed first, albeit it was still a "hack" basically, but it worked great!! no more visible jittery movement, also stacks got more stable, even @30 hz, 7 solver iteration, 3 position solver iteration (I could stack up to 7 boxes). I have not implemented joint yet, the computing Jacobian for joint is confusing for me. Do you have any source that explain it in a more "geometrical" way? all tutorials do it in pure mathematical way, hard to follow.

Share this post


Link to post
Share on other sites

With any kind of stabilization you have to find a careful balance between resolving penetration and not loosing contact points (e.g. because of over-shooting). Jitter is more noticeable than a 'reasonable' penetration to the player from my experience. So I always resolve penetration very carefully. Saying this, things are different in VR these days and small glitches are very noticeable there. Oh, well...

 

Joints are not too bad. There are three or four constraints (building blocks) from which you can build most joints. I recommend starting with a spherical joint (ball-socket) followed by the revolute joint (hinge) and prismatic joint (slider). Finally add limits and motors. I recommend looking at Richard Tongues and Erin's slides. This is what I found:

 

http://box2d.org/files/GDC2013/Tonge_Richard_PhysicsForGame.pdf

http://box2d.org/files/GDC2014/GDC2014_ErinCatto.zip

http://box2d.org/files/GDC2009/GDC2009_Catto_Erin_Solver.ppt

http://box2d.org/files/GDC2007/GDC2007_Catto_Erin_Physics1.ppt

 

Finally Irlan and Randy released their physics engine and some useful information on their websites:

 

https://irlan.net/

http://www.randygaul.net/

 

If you get stuck or want your results to be verified I can surely have a look here. 

 

HTH,

-Dirk

Share this post


Link to post
Share on other sites

Thank you Mr. Gregorious!! that material really helped (although my lack of mathematical knowledge got in the way fast). I got Ball/Sphere joint and distance joint implemented. Question: Why can't I warmstart the distance joint though? With the sphere joint, I could accumulate impulse, and apply them at the next frame (before computing the new Jacobian).

 

Here's how I calculate the constraint mass of my distance joint

@Override
	public void preCalculate(float dt, float baumgarte) {
		// update position
		worldA = bodyA.toWorld(localA);
		worldB = bodyB.toWorld(localB);
		
		Vector3.sub(worldA, worldB, n);
		// grab bias. no slop needed
		bias = (length - n.length()) * baumgarte / dt;	// totally stiff
		
		// normalize direction
		n.normalize();
		
		// calculate mass?
		kMass = bodyA.getInvMass() + bodyB.getInvMass();
		
		Vector3 r = new Vector3();
		Vector3 rn = new Vector3();
		
		bodyA.getRot().transformVector(localA, r);
		Vector3.cross(r, n, rn);
		Vector3.cross(rn, r, rn);
		bodyA.getWorldInvInertia().transformVector3(rn, rn);
		kMass += Vector3.dot(n, rn);
		
		bodyB.getRot().transformVector(localB, r);
		Vector3.cross(r, n, rn);
		Vector3.cross(rn, r, rn);
		bodyB.getWorldInvInertia().transformVector3(rn, rn);
		kMass += Vector3.dot(n, rn);
		
		kMass = kMass > 0 ? 1.0f/kMass : 0;
		
		// should do warmstart here, applying old impulse,
		// but the joint would blow away!!
		// bodyA.applyImpulse(accumP, worldA);
		// bodyB.applyImpulse(accumP.inverse(), worldB);
	}

basically, It's quite similar with contact constraint mass calculation. Nothing special I guess. The bias term is the difference between the target length and the actual distance, am I right :unsure: ?

 

and here's the solver part:

@Override
	public void solve() {
		Vector3 j = new Vector3();
		Vector3.sub(bodyA.getVelWS(worldA), bodyB.getVelWS(worldB), j);
		
		float jMag = (-Vector3.dot(j, n) + bias) * kMass;
		
		j.setTo(n);
		j.scale(jMag);
		
		bodyA.applyImpulse(j, worldA);
		bodyB.applyImpulse(j.inverse(), worldB);
		
		// accumulate impulse
		Vector3.add(accumP, j, accumP);
	}

basically,

jMag = (-deltaV.n + bias) * kMass;

 

then I apply the impulse to body A, the same with body B, albeit with different direction (inverted).

 

which is pretty similar to contact impulse calculation. it worked just fine, the difference is, I dont clamp it against accumulated impulse, since it is an equality constraint. Applying old impulse (when calculating the jacobian) would cause the bodies to go crazy. perhaps warmstarting is not needed at all for this type of joint?

 

EDIT: I seem to forgot to mention that the distance joint works just fine without warmstarting, but it kinda itches me though, why can't I warmstart the distance joint the same way I do ball joint?

Edited by Bow_vernon

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this