Archived

This topic is now archived and is closed to further replies.

endo

reflection vectors for bouncing

Recommended Posts

I have used the following code to calculate the reflection vector for my ball, the problem is it just bounces back in the direction it came from Any idea why? I followed the theory from one of the tutorials on the nehe site.
  	if( ball.getActive( ) )		//if inactive ball is drawn but doesnt move

	{
		ball.moveBall( );
		MyPolygon hit;

		if( ballPaddleCollision( paddle, *ball.getLoc( ), ball.getRadius( ), hit ) )
		{
//			cout << *hit.getNormal( ) << endl;

//			cout << *ball.getMove( ) << endl;

			Vector3d newVector;
			Vector3d hitNormal = *hit.getNormal( );
			Vector3d ballMovement = *ball.getMove( );
			hitNormal.normalise( );
			ballMovement.normalise( );
			newVector = hitNormal + ballMovement * dotProduct( -ballMovement, hitNormal ) * 2;
			newVector = newVector * ballMovement.length( );
			ball.setMove( newVector );
		}
	}  

Share this post


Link to post
Share on other sites
This line seems not to be right:

newVector = hitNormal + ballMovement * dotProduct( -ballMovement, hitNormal ) * 2;
shouldn''t it be:
newVector = ballMovement + hitNormal * dotProduct( -ballMovement, hitNormal ) * 2;

Hope that was the bug.

Share this post


Link to post
Share on other sites
Sorry, doesnt seem to have made any difference - although I''m sure its more correct than the way I had it

Share this post


Link to post
Share on other sites
a-((a.b)/(b.b))*b is the component of a orthogonal to b. If the magnitude of b is 1 then that reduces to a-(a.b)*b. You don''t want orthogonal, but the reflection so it is a-2*(a.b)*b. (a.b)*b is the component of a parallel to b and (-a).b = -(a.b). If you normal either points the wrong direction or is does not have a magnitude of one then you don''t get the right answer.

Share this post


Link to post
Share on other sites
I''m sure the normal is correct and as you can see the code normalises it before it is used.

Share this post


Link to post
Share on other sites
That may very well be your problem. You wrote

newVector = newVector * ballMovement.length( );

ballMovement''s length is 1 if it is normalized. In fact, once your vectors are normalized, you have just lost the magnitude of your speed, which isn''t very good. You can try this

Normal = Normalize(Normal);
finalspeed = initialspeed + Normal * Dot(initialspeed, Normal) * 2

Don''t normalize the initialspeed. This should work if your normal is pointing outside the paddle. However, if your code really bounces the ball back in the direction it came from, then your code for evaluating the normal might have a problem.

Cédric

Share this post


Link to post
Share on other sites
It seems the speed has to be normalised otherwise it tends to be amplified every time a collision occurs. Quite funny but not very helpful.

I am still sure that the normal is fine, it is a flat paddle at the bottom of the screen. When I print all the vertex data, including normal, to a file I get this:

normal : ( 0 , 1 , 0 )
( 52 , -88 , -10 )
( 52 , -88 , 10 )
( 82 , -88 , 10 )
( 82 , -88 , -10 )


This looks like it corresponds to the top polygon of the paddle exactly, apart from the changes due to translation.

Share this post


Link to post
Share on other sites
Let''s take a look at your code (with omnibrain''s change)

Vector3d newVector;
Vector3d hitNormal = *hit.getNormal( );
Vector3d ballMovement = *ball.getMove( );
hitNormal.normalise( );
ballMovement.normalise( );
newVector = ballMovement + hitnormal * dotProduct( -ballMovement, hitNormal ) * 2;
newVector = newVector * ballMovement.length( );
ball.setMove( newVector );

If normal = (0,1,0) and ballmovement = (3,-4,0)

hitNormal = (0,1,0)
ballmovement = Normalize(ballmovement) = (3/5, -4/5, 0)
newVector = (3/5 + 0, -4/5 + 1 * -4/5 * 2, 0) = (3/5, 4/5, 0)
newVector = newVector * 1 (since ballMovement is normalized)

Notice that newVector = (3/5, 4/5, 0) regardless of the magnitude of ballmovement. You could have chose (6, -8, 0) and still get the same results. If you insist on keeping the code as it is, then you have to store the length of ballMovement _before_ normalizing it, and multiply by that variable at the end, instead of multiplying by ballMovement.length().

Cédric

Share this post


Link to post
Share on other sites
If you are getting an amplification on reflection then your normal doesn''t have a magnitude of one. Lets use a simple case. You are bouncing off the x axis and the point of collision is the origin. Your velocity vector is (x,y) and the normal is (0,1). Your velocity vector after reflection is (x,y)-2*((0,1).(x,y))*(0,1) = (x,y)-2*y*(0,1) = (x,y)-(0,2y) = (x,y-2y)=(x,-y). Now how did your magnitude change?

Share this post


Link to post
Share on other sites
Updated code:

Vector3d newVector;
Vector3d hitNormal = *hit.getNormal( );
Vector3d ballMovement = *ball.getMove( );

hitNormal.normalise( );
ballMovement.normalise( );

newVector = (ballMovement + hitNormal) * dotProduct( -ballMovement, hitNormal ) * 2;

ball.setMove( newVector );

This is how it stand at present, the speed after collision is fine - looks the same. The problem is in the angle of reflection, it is inconsistent. Looking at it carefully, the ball reflects at an angle increasing closer to 90 degrees, parallel with the normal. Any other thoughts? I was hoping to solve this and make some significant progress today....

Share this post


Link to post
Share on other sites
Now, this doesn''t work at all. The idea of collision is that a certain normal force is applied over a brief period of time. Integrating this force gives us the impulse, which is a change in the linear momentum of the object (= speed * mass).

To deal with collisions properly (ie.: in a convincing way), you should express it like so (although I''m sure there are other ways to look at it):

finalspeed = initialspeed + impulse (vectors)

impulse is a vector orientated along the surface normal, so

impulse = Normal * k

The impulse should not be affected by the component of speed that is parallel to the plane, only the component that is parallel to the normal. This is why we use the dot product, and so we get the formula I posted in my original post:

finalspeed = initialspeed + Normal * Dot(initialspeed, -Normal) * r

where r is a coefficient of "restitution" that varies between 1 and 2.

r = 1.5
Vector3d newVector;
Vector3d hitNormal = *hit.getNormal( );
Vector3d ballMovement = *ball.getMove( );
hitNormal.normalise( );
newVector = ballMovement + hitNormal * dotProduct(ballMovement, -hitNormal ) * r;
ball.setMove( newVector );

Cédric

Share this post


Link to post
Share on other sites
The problem must be elsewhere then, I copied cedricl's code exactly and had a problem with the ball accelerating after the collision. The code I posted last is the closest I have get except for the problem with the reflection angle - so I'm still stuck

[edited by - endo on May 3, 2002 8:10:02 PM]

Share this post


Link to post
Share on other sites
Sorry for bumping this, but what is the likely cause of my reflection vector ebcoming increasingly closer to 90 degrees, ie parallel with normal?

Share this post


Link to post
Share on other sites
hey i like this board. just got done proving division of a scalar against a vector is defined anon. thought hey time for me to get a name =)

anyway:

this has to be wrong:
ballMovement.normalise( ); newVector = newVector * ballMovement.length( );

i took out a middle line to make it obvious. ballMovement.length() == 1 as true by definition of a normal at the point you grab it. you need to save its length ahead of time. then normalise it.

havent decided if thats your error yet. im still reading the thread =) but its definatly one bug.

[edit bah it was in the thread.]

[edited by - declspec on May 5, 2002 9:49:59 AM]

Share this post


Link to post
Share on other sites
picture the crossproduct vector of the
-move X normal = TowardsYou. right?

But now imagine
newmove X normal = -TowardsYou

normal X -TowardsYou = newmove // ops on a cross product

replacement of towardsyou

normal X - ( -move X normal) = newmove

note:
in this version move doesnt need to be normalized first (read squareroots removed). the info about magnitude is preserved and applied in the crossproduct ops.

just a different way to look at the prob. using crossproducts implies having to solve out each component of the vector. thats where it went wrong. a third approach would be rotations.

[edited by - declspec on May 5, 2002 10:14:34 AM]

notes that make this approach seem solid:
towardsyou is arbitrary but note that the target resultant vector, the normal vector and the negation of the move vector are all coplanar. coplanar crossproducts will have scalar transformations of the resultant vector.

thats to say i draw two lines on a page crossproduct them and i get a line coming out of the page. now no matter what i do to the lines in the page their cross product will always be on that ray coming out of the page. so that accounts for using the direction right. the up and down of it using your old "curl your fingers in the right hand through the first vector first" aint hard to see.

now bout magnitude. the vectors share magnitude by our game rules.

so we know the resultant vector over both cross products will be on the same ray and have the same magnitude. sooo that accounts for magnitude and direction.... hence my recomendation of crossproducts to avoid division.

note again:
that the magnitude of the crossproduct is equal to the two magnitudes mulitplied by each other and the sine of the angle between them. i say this cause if the ball had -move = normal the resultant crossproduct vector would be 0. while the above is still mathematically clean important when you actual write the code would be to note the possibility of 0. never pass a 0 case without examining what will happen as they are lightning rods for bugs.




[edited by - declspec on May 5, 2002 10:23:54 AM]

[edited by - declspec on May 5, 2002 10:30:14 AM]

Share this post


Link to post
Share on other sites
Thanks for the reply declspec, but seeing as my maths brain is still catching up with my C++ brain I am having difficulty picturing 3d vector cross products Most of your post could well have been in Mongolian and I probably would've understood just as well.

Anyway despite this, it is better now than it was. Now the reflection vector is exactly the same as the original, which seems like progress to me. Here is the code I used based on what you said:

.
.
.
Vector3d newVector;
Vector3d hitNormal = *hit.getNormal( );
Vector3d ballMovement = *ball.getMove( );

GLfloat length = ballMovement.length( );
hitNormal.normalise( );
ballMovement.normalise( );

newVector = ballMovement + hitNormal * -crossProduct( -ballMovement, hitNormal ) * 2;
newVector = newVector*length;

ball.setMove( newVector );
.
.
.

The sign before the crossProduct(..) appears to make no difference



[edited by - endo on May 5, 2002 10:46:45 AM]

Share this post


Link to post
Share on other sites
ok well skipping to the code i think might work since your actively working this thread i would try:

newVector = CrossProduct( -ballMovement, hitNormal);
newVector = CrossProduct( hitNormal, -newVector);

no normalizing... none replace the code starting with the normalizing with the above.


if( ballPaddleCollision( paddle, *ball.getLoc( ), ball.getRadius( ), hit ) ) {// cout << *hit.getNormal( ) << endl;// cout << *ball.getMove( ) << endl; Vector3d newVector;
Vector3d hitNormal = *hit.getNormal( ); //assuming this is magnitude 1
newVector = CrossProduct( -ballMovement, hitNormal);
newVector = CrossProduct( hitNormal, -newVector);
Vector3d ball.setMove( newVector );
}

Share this post


Link to post
Share on other sites
Nice try, now the ball hits the polygon and travels off along the x axis. The normal of the polygon it is hitting is definitly ( 0, 1, 0 ) just in case that was the next question

Share this post


Link to post
Share on other sites
hmm interesting. wonder where i went wrong. you did see you read a guide to this didnt ya. got a link we''ll try to sort out the guides version as it relates to your code. also i''ll plug some numbers. still curious where i went wrong =)

Share this post


Link to post
Share on other sites
I have a workaround for the problem but I''m sure there must be a proper way to fdo it and a reason why it doesn''t work as it should in my program, kinda worrying really.....

Anyway I just added the original absolute Y value to the new vector which was travelling along the X axis.

newVector = crossProduct( -ballMovement, hitNormal );
newVector = crossProduct( hitNormal, -newVector );
newVector.triple[ Y ] += absolute( ballMovement.triple[ Y ] );

ball.setMove( newVector );

Certainly not ideal but it''ll have to do until I find a better way. Now off to the library to find some maths books...

Share this post


Link to post
Share on other sites
lol i dont see where i went wrong in the formation yet. but i see why it flys off to the side. the normal is pure y. the first crossproduct is pure z (two x, y planer vectors will always make that true). the resultant on the second cross product is going to be pure x cause its a pure y crossed by a pure z. its good your normal is a clean one component vector it made the bad math conceptually stand out.

now to where i went wrong in the math.

Share this post


Link to post
Share on other sites
If you wanna mail me (see my profile) I can send my code straight to you, but I understand if you dont want to it about 1500 lines worth already.

I originally followed the collision code on the nehe site, here is a direct link to the tutorial:
[url]http://nehe.gamedev.net/tutorials/lesson.asp?l=30[/url]

My actual collision code isn''t identical though, I cobbled it together from different sources so that I could understand as much as possible.

Gotta eat now, but I''ll check my mail in about an hour and sen the code then if you like. Thanks again for the help, a least it works now and I can move on a bit

Share this post


Link to post
Share on other sites
my darn TI has dead batteries. i went back over the math.

assertion 1:
-move X normal = TowardsYou

confident thats true.

assertion 2:
newmove X normal = -TowardsYou

again confident that is true. (basis: same sine value means same magnitude direction is easy to see as opposite)

conclusion 3:
normal X -TowardsYou = newmove // ops on a cross product

thats where i went wrong. thinking thats a bad operation. man. how much math have i done wrong cause of that i wonder. the basis was i x j = k implies j x k = i but im thinking it matters that both are normalized... hrmm wish my TI had batteries.

----------------------------------------------------

but skip that
the case of the normal being (0,1,0) made me see what they are doing with dot products.

in the case of (0,1,0) we can just negate the y component of move and we get the right newmove.

newmove = move
newmove.y = -newmove.y

right?
for that special case. but what about the general case?

for that we need to express the move vector not in terms of i and j but in terms of the normal and something at 90 degrees of the normal.

we can make use of that crossproduct again. you know the bug version result

basis= CrossProduct(-move,normal);//negated for conceptual clarity
basis= CrossProduct(basis,normal);

//No potential bug here it comes out in the resulting projection. if its one way the proj is negative if tis another its positive

That gives us a vector at 90 degrees to the normal vector but sharing a plane with the move vector amd the normal vector. ie if both move and normal have a 0 z it will have a 0 z.

now we dot product
proj.y = DotProduct(normal,move);
proj.x = DotProduct(basis,move);

all thats saying is in terms of the normal move is this big in that direction (projection). and in terms of the normal to the normal move is this big in that direction (projection).

now as per the specific case we negate the normal component but leave the x alone.

proj.y = - proj.y

done? no the scale values are just in terms of the normal / normal to the normal.

newmove = proj.x * basis; //note this is a scaler times a vector
newmove += proj.y * normal;

bugless? heck no. but i been known to get lucky. the crossproducts are just there to produce a vector at 90 degrees to the normal to express the move vector in terms of the normal vector. there are other ways i bet.

EDIT:
thanks for listening to me but im really just reinventing the wheel. im sure this has been solved out before. now that i have explored the prob ill reread your code. and try to stick to the question =)
i decided this code should work:


basis= CrossProduct(-move,normal);
basis= CrossProduct(basis,normal);//its orth to the normal

proj.y = DotProduct(normal,move);
proj.x = DotProduct(basis,move);

proj.y = - proj.y

newmove = proj.x * basis; //use the proj data to reform the vector
newmove += proj.y * normal;

[edited by - declspec on May 5, 2002 12:34:44 PM]

[edited by - declspec on May 6, 2002 1:03:32 AM]

Share this post


Link to post
Share on other sites
Thanks so much for putting in the effort, for the moment I'll leave it as it is, just adding the y component to the result. It seems to be working fine, but I'll be after some maths books to try and learn some of this tuff in a few days

EDIT:
Seems I was wrong, it only seems to work for the polygons facing upwards. When its hits the sides or bottom it all goes pear-shaped

[edited by - endo on May 5, 2002 2:57:08 PM]

Share this post


Link to post
Share on other sites
http://www.gamasutra.com/features/20020118/vandenhuevel_03.htm

here is a good article on calculating collision and bounce.

Share this post


Link to post
Share on other sites