Sign in to follow this  
VanKurt

Friction... (2D physics)

Recommended Posts

I just implemented linear friction into my 2D physics engine like this: I take the velocity-components of two colliding bodies, which are parallel to the collision tangent, scale them down a bit and subtract them from the original velocities. The result is quite satisfying...except for a nasty problem that occurs now! :-( There's a strange "sliding" problem. Here's the two demos (seeing things in motion is better than a thousand words) ;-) Click me! See what I mean? Since the box stops sliding to the right, but still spins it just falls down where it is... Now my question: Did I do something wrong with linear friction? Or is only the angular stuff missing? ??? Thanks a lot for your help! again and again ;-)

Share this post


Link to post
Share on other sites
I think there is a big problem with how your executables work on other people's PC since you switched to DirectX (if I remember correctly you first posted OGL demos). It used to work with GL but your DX demos would output very tiny lines and I put it on Wine failing to emulate some DX features but I get the same result in VMWare so it might be with your code after all.

Here's what I get both with Wine and VMWare (can't reboot right now for confirmation on the real thing sorry):

Free Image Hosting at www.ImageShack.us

Share this post


Link to post
Share on other sites
Quote:
Original post by VanKurt
Huh? Looks all pretty good on my machine :-(
I'm new to DX, so I'm not quite sure how to fix this...can ANYONE see something? ;-)


But what I see is not what's supposed to be on screen right? :)

Share this post


Link to post
Share on other sites
Quote:
Original post by VanKurt
Nope, those lines are actually boxes ;-)


Works fine under native Windows :) so no problem with your code.
It's looking good! You finally sorted out the impulse problem.

I'd say the friction is not working, and when it seems to be it's just luck. I suppose this is impulse based so what happens if you completely cancel the tangential component of the impulse in your no friction code? This should give you full friction, even too much friction but that's a start... something like J = normal * J.Dot(normal) should do it.

edit: Seeing how well your no friction code works the problem is more likely to come from your friction code. The rigid body part looks fine.

Share this post


Link to post
Share on other sites
"...is more likely to come from your friction code"
Yes, that's what I'm thinking, too ;-)

But how should friction be implemented? Is my method not correct? Or is correct (->there's a bug in my code)?

BTW: You're able to run DirectX stuff under Linux? Wow, I'm impressed! :-D

Share this post


Link to post
Share on other sites
Quote:
Original post by VanKurt
"...is more likely to come from your friction code"
Yes, that's what I'm thinking, too ;-)

But how should friction be implemented? Is my method not correct? Or is correct (->there's a bug in my code)?


I made a mistake in my previous post... You actually do not want to remove the tangential component of the impulse as it will remove all chance of getting any friction :). So it's difficult to tell with no maths or code to check at. The impulse code should be really small, you could maybe post it?

Quote:

BTW: You're able to run DirectX stuff under Linux? Wow, I'm impressed! :-D


Yes, there's many ways to run it, VMWare5 now emulate DirectX through OpenGL, very nice. Wine works fine too, but strangely enough not with your demo :(.

Share this post


Link to post
Share on other sites
Here's my ApplyImpulse method (featuring this buggy friction part) ;-)


void CSimulator::ApplyImpulse( CRigidBody *b1, CRigidBody *b2, CVector2 colNormal )
{
// Calculate rel. velocity
CVector2 vRelativeVelocity;
CVector2 v1, v2;
v1 = b1->getVelocity() + CVector2( -b1->getVelocityAng()*b1->getColPoint().y, b1->getVelocityAng()*b1->getColPoint().x );
v2 = b2->getVelocity() + CVector2( -b2->getVelocityAng()*b2->getColPoint().y, b2->getVelocityAng()*b2->getColPoint().x );
vRelativeVelocity = v1-v2;


float fCr = 0.5f; //Temporary


// Find impulse
float impulse; // Thank's to Oliii :-)
float denom_m;
float denom_1;
float denom_2;
impulse = (-(1+fCr) * (vRelativeVelocity*colNormal));
denom_m = (b1->getInvMass() + b2->getInvMass());
denom_1 = colNormal.DotProduct(globalCorePtr->mMath.FloatCrossProduct((b1->getColPoint().CrossProduct(colNormal))*b1->getInvInertia(), b1->getColPoint()));
denom_2 = colNormal.DotProduct(globalCorePtr->mMath.FloatCrossProduct((b2->getColPoint().CrossProduct(colNormal))*b2->getInvInertia(), b2->getColPoint()));
impulse /= (denom_m + denom_1 + denom_2);


// Calculate changes in velocity for body1 and 2
CVector2 deltaVel1 = (impulse * colNormal) * b1->getInvMass();
CVector2 deltaVel2 = (impulse * colNormal) * b2->getInvMass();


// Friction!?!?!?! Crappy, cause it doesn't work as it should
CVector2 colTan = colNormal;
colTan.Ortogonalize();
CVector2 projVel1 = globalCorePtr->mMath.ProjectAOnB( &b1->getVelocity(), &colTan );
CVector2 projVel2 = globalCorePtr->mMath.ProjectAOnB( &b2->getVelocity(), &colTan );
CVector2 frictionForce = projVel1 - projVel2;


// Now update body 1
if( b1->getMass() != 0 )
{
// Change linear velocity
b1->changeVelocity( deltaVel1 );

// Change angular velocity
float deltaVelAng1 = (b1->getColPoint().CrossProduct(impulse*colNormal))*b1->getInvInertia();
b1->changeVelocityAng(deltaVelAng1);

// Apply stupid friction
b1->changeVelocity( -frictionForce*0.1f );
}//end if


// Now update body 2
if( b2->getMass() != 0 )
{
// Change linear velocity
b2->changeVelocity( -deltaVel2 );

// Change angular velocity
float deltaVelAng2 = (b2->getColPoint().CrossProduct(impulse*colNormal))*b2->getInvInertia();
b2->changeVelocityAng(-deltaVelAng2);

// Apply stupid friction
b2->changeVelocity( frictionForce*0.1f );
}//end if
}//end ApplyImpulse

Share this post


Link to post
Share on other sites
Can you clarify a few things for me please? :)

In: impulse = (-(1+fCr) * (vRelativeVelocity*colNormal));
I suppose that (vRelativeVelocity*colNormal) is a dot product operator, right?

If I'm not mistaken, the problem is that you only compute the normal impulse (ie. the one that modify the precollision velocity only along the collision normal). If you compute the complete impulse (with its tangential component) you will have an impulse sufficient to completely stop (or reverse the incoming velocity but anyway) not only along the normal of collision but also in the collision plane.

To compute the full impulse you will have to change a few types.
Something like that, should give full dead-on friction.


CVector2 impulse;
float denom_m;
float denom_1;
float denom_2;
impulse = (-(1+fCr) * vRelativeVelocity); // This is a vector now.
denom_m = (b1->getInvMass() + b2->getInvMass());
denom_1 = colNormal.DotProduct(globalCorePtr->mMath.FloatCrossProduct((b1->getColPoint().CrossProduct(colNormal))*b1->getInvInertia(), b1->getColPoint()));
denom_2 = colNormal.DotProduct(globalCorePtr->mMath.FloatCrossProduct((b2->getColPoint().CrossProduct(colNormal))*b2->getInvInertia(), b2->getColPoint()));
impulse /= (denom_m + denom_1 + denom_2);

// Calculate changes in velocity for body1 and 2
CVector2 deltaVel1 = impulse * b1->getInvMass();
CVector2 deltaVel2 = impulse * b2->getInvMass();





Note: do not apply any of your current friction code.
From there you can extract the normal and the tangential components from your impulse. The tangential component being responsible for stopping motion on the collision plane (ie. friction).

Share this post


Link to post
Share on other sites
Thank's a lot for your help so far!
I just tried what you said (changed impulse to a vector)...
The result is more than funny :-)

Take a look: Click me, please!


Here's the code:

void CSimulator::ApplyImpulse( CRigidBody *b1, CRigidBody *b2, CVector2 colNormal )
{
// Calculate rel. velocity
CVector2 vRelativeVelocity;
CVector2 v1, v2;
v1 = b1->getVelocity() + CVector2( -b1->getVelocityAng()*b1->getColPoint().y, b1->getVelocityAng()*b1->getColPoint().x );
v2 = b2->getVelocity() + CVector2( -b2->getVelocityAng()*b2->getColPoint().y, b2->getVelocityAng()*b2->getColPoint().x );
vRelativeVelocity = v1-v2;


float fCr = 0.5f; //Temporary


// Find impulse OLD CODE
/*float impulse;
float denom_m;
float denom_1;
float denom_2;
impulse = (-(1+fCr) * (vRelativeVelocity.DotProduct(colNormal)));
denom_m = (b1->getInvMass() + b2->getInvMass());
denom_1 = colNormal.DotProduct(globalCorePtr->mMath.FloatCrossProduct((b1->getColPoint().CrossProduct(colNormal))*b1->getInvInertia(), b1->getColPoint()));
denom_2 = colNormal.DotProduct(globalCorePtr->mMath.FloatCrossProduct((b2->getColPoint().CrossProduct(colNormal))*b2->getInvInertia(), b2->getColPoint()));
impulse /= (denom_m + denom_1 + denom_2);*/


CVector2 impulse;
float denom_m;
float denom_1;
float denom_2;
impulse = (-(1+fCr) * vRelativeVelocity);
denom_m = (b1->getInvMass() + b2->getInvMass());
denom_1 = colNormal.DotProduct(globalCorePtr->mMath.FloatCrossProduct((b1->getColPoint().CrossProduct(colNormal))*b1->getInvInertia(), b1->getColPoint()));
denom_2 = colNormal.DotProduct(globalCorePtr->mMath.FloatCrossProduct((b2->getColPoint().CrossProduct(colNormal))*b2->getInvInertia(), b2->getColPoint()));
impulse /= (denom_m + denom_1 + denom_2);

// Calculate changes in velocity for body1 and 2
CVector2 deltaVel1 = impulse * b1->getInvMass();
CVector2 deltaVel2 = impulse * b2->getInvMass();


// Friction!?!?!?! Crappy, cause it doesn't work as it should
CVector2 colTan = colNormal;
colTan.Ortogonalize();
CVector2 projVel1 = globalCorePtr->mMath.ProjectAOnB( &b1->getVelocity(), &colTan );
CVector2 projVel2 = globalCorePtr->mMath.ProjectAOnB( &b2->getVelocity(), &colTan );
CVector2 frictionForce = projVel1 - projVel2;


// Now update body 1
if( b1->getMass() != 0 )
{
// Change linear velocity
b1->changeVelocity( deltaVel1 );

// Change angular velocity
float deltaVelAng1 = (b1->getColPoint().CrossProduct(impulse*colNormal))*b1->getInvInertia();
b1->changeVelocityAng(deltaVelAng1);

// Apply stupid friction
//b1->changeVelocity( -frictionForce*0.1f );
}//end if


// Now update body 2
if( b2->getMass() != 0 )
{
// Change linear velocity
b2->changeVelocity( -deltaVel2 );

// Change angular velocity
float deltaVelAng2 = (b2->getColPoint().CrossProduct(impulse*colNormal))*b2->getInvInertia();
b2->changeVelocityAng(-deltaVelAng2);

// Apply stupid friction
//b2->changeVelocity( frictionForce*0.1f );
}//end if
}//end ApplyImpulse



[Edited by - VanKurt on July 11, 2005 9:17:51 AM]

Share this post


Link to post
Share on other sites
I just checked some literature (not that I understood that much)...but it really seems as if impulse is a vector, not a scalar :-)

That leads me to two questions:

1) Why did everything work fine until now (without friction)?????
2) How do I have to change the code so that impulse becomes a vector? The suggestion from above didn't work (as already mentioned) ;-)

Share this post


Link to post
Share on other sites
Quote:
Original post by VanKurt
I just checked some literature (not that I understood that much)...but it really seems as if impulse is a vector, not a scalar :-)

That leads me to two questions:

1) Why did everything work fine until now (without friction)?????
2) How do I have to change the code so that impulse becomes a vector? The suggestion from above didn't work (as already mentioned) ;-)


1) Because you were working on the magnitude of vectors. Wich was equivalent in this case. normal * velrel gives you the length of the normal component of velrel and once you had the length of the impulse (normal only) you used to multiply it by the normal vector again so you were back with a vector impulse as expected.

2) It should. The only reason I can see is that your relative velocity is reversed. Can you try vRelativeVelocity = v2-v1; instead of v1-v2?

Note: You can still work with a scalar when calculating the full impulse but it really doesn't make sense :).

Share this post


Link to post
Share on other sites
"The only reason I can see is that your relative velocity is reversed. Can you try vRelativeVelocity = v2-v1; instead of v1-v2?"

Nope, that made everything worse... :-D
This physics stuff can make you totally insane...can't think of anything else...ARGH!!!

Share this post


Link to post
Share on other sites
Quote:
Original post by VanKurt
"The only reason I can see is that your relative velocity is reversed. Can you try vRelativeVelocity = v2-v1; instead of v1-v2?"

Nope, that made everything worse... :-D
This physics stuff can make you totally insane...can't think of anything else...ARGH!!!


I just noticed but, did you update this part of the code as well?

float deltaVelAng1 = (b1->getColPoint().CrossProduct(impulse*colNormal))*b1->getInvInertia();



As well as the reciprocal one for the other body in the pair.
You should now use the impulse directly (ie. CrossProduct(impulse)). I guess you did as it should not compile otherwise, but better safe than sorry.


Share this post


Link to post
Share on other sites
Ooops :-)
Hadn't changed that - but it compiled without problems nevertheless ^^

The sad news: everything's still messed up :-(
Ahhh, well... I guess I'll have to rewrite that whole crap...umph

Share this post


Link to post
Share on other sites
Quote:
Original post by VanKurt
Ooops :-)
Hadn't changed that - but it compiled without problems nevertheless ^^

The sad news: everything's still messed up :-(
Ahhh, well... I guess I'll have to rewrite that whole crap...umph


Did you try with both velocity directions? :)

Share this post


Link to post
Share on other sites
Ok let's hunt the bug in case you already tried both velocities.
Using your original velocity and the vector impulse path add:

impulse = impulse.DotProduct(colNormal) * colNormal;

Right after the:

impulse /= (denom_m + denom_1 + denom_2);

You should be back with your no friction code and it cannot fail unless you have a bug in your operators or vector code.

Share this post


Link to post
Share on other sites
Hey, thank's a lot b34r! Very nice that you're still helping me :-)

After adding the line
"impulse = impulse.DotProduct(colNormal) * colNormal;"
everyting's back to normal (as you predicted) ;-)

The new function (just for reference) looks like this:



void CSimulator::ApplyImpulse( CRigidBody *b1, CRigidBody *b2, CVector2 colNormal )
{
// Calculate rel. velocity
CVector2 vRelativeVelocity;
CVector2 v1, v2;
v1 = b1->getVelocity() + CVector2( -b1->getVelocityAng()*b1->getColPoint().y, b1->getVelocityAng()*b1->getColPoint().x );
v2 = b2->getVelocity() + CVector2( -b2->getVelocityAng()*b2->getColPoint().y, b2->getVelocityAng()*b2->getColPoint().x );
vRelativeVelocity = v1-v2;


float fCr = 0.5f; //Temporary


// Find impulse
CVector2 impulse;
float denom_m;
float denom_1;
float denom_2;
impulse = (-(1+fCr) * vRelativeVelocity);
denom_m = (b1->getInvMass() + b2->getInvMass());
denom_1 = colNormal.DotProduct(globalCorePtr->mMath.FloatCrossProduct((b1->getColPoint().CrossProduct(colNormal))*b1->getInvInertia(), b1->getColPoint()));
denom_2 = colNormal.DotProduct(globalCorePtr->mMath.FloatCrossProduct((b2->getColPoint().CrossProduct(colNormal))*b2->getInvInertia(), b2->getColPoint()));
impulse /= (denom_m + denom_1 + denom_2);

// New line!
impulse = impulse.DotProduct(colNormal) * colNormal;

// Calculate changes in velocity for body1 and 2
CVector2 deltaVel1 = impulse * b1->getInvMass();
CVector2 deltaVel2 = impulse * b2->getInvMass();


// Now update body 1
if( b1->getMass() != 0 )
{
// Change linear velocity
b1->changeVelocity( deltaVel1 );

// Change angular velocity
float deltaVelAng1 = (b1->getColPoint().CrossProduct(impulse))*b1->getInvInertia();
b1->changeVelocityAng(deltaVelAng1);
}//end if


// Now update body 2
if( b2->getMass() != 0 )
{
// Change linear velocity
b2->changeVelocity( -deltaVel2 );

// Change angular velocity
float deltaVelAng2 = (b2->getColPoint().CrossProduct(impulse))*b2->getInvInertia();
b2->changeVelocityAng(-deltaVelAng2);
}//end if
}//end ApplyImpulse



What's next? :-)

Share this post


Link to post
Share on other sites
Quote:
Original post by VanKurt
Hey, thank's a lot b34r! Very nice that you're still helping me :-)
[...]
What's next? :-)


No problem, that's what I'm here for when I'm not trying to get help myself ;).
Ok so it looks like the tangential impulse is causing troubles... Right now you have a full impulse that should bring the velocity of the collision point to 0 in a perfectly inelastic collision.

What you can try:

- Setting fCr to 0.f if not already just so that the results are clearer.
- Split the full impulse into it's tangential and normal components.

normalImpulse = impulse.DotProduct(colNormal) * colNormal;
tangentImpulse = impulse - normalImpulse;

The full impulse is then recomposed as impulse = normalImpulse + tangentImpulse * kPseudoFriction;

With kPseudoFriction = 0 you have no friction, everything should be like it is right now.

- Try ramping kPseudoFriction from 0 to 1 by small increments and see wether you can get anything meaningful at all.

From there we may be able to figure where the problem lies.

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