Sphere and rectangle collision, adjusting for intersection

Started by
30 comments, last by simotix 13 years, 11 months ago
I am currently doing a sphere to rectangle collision in which case I can find if it has collision, no collision or resting contact. While finding the resting contact I have to get the intersection depth. However, if how can I adjust the spheres position so that way it is no longer intersecting? I tried to scurry the internet, however, I just do not see much on the topic. Here is my currently implementation.

	D3DXMATRIX mTransform = *pCollisionRectangle->GetTransform();
	D3DXMATRIX mInverseTransform;
	D3DXMatrixInverse(&mInverseTransform, NULL, &mTransform);

	D3DXVECTOR4 vLocalPoint4;
	D3DXVECTOR3 vLocalPoint; 
	D3DXVec3Transform(&vLocalPoint4, &pCollisionSphere->GetOrigin(), &mInverseTransform);
	vLocalPoint = D3DXVECTOR3(vLocalPoint4.x, vLocalPoint4.y, vLocalPoint4.z);
	clamp(vLocalPoint.x, -pCollisionRectangle->GetExtents().x, pCollisionRectangle->GetExtents().x);
	clamp(vLocalPoint.y, -pCollisionRectangle->GetExtents().y, pCollisionRectangle->GetExtents().y);
	vLocalPoint.z = 0;

	D3DXVECTOR4 vClosest4;
	D3DXVec3Transform(&vClosest4, &vLocalPoint, &mTransform);
	D3DXVECTOR3 vClosest(vClosest4.x, vClosest4.y, vClosest4.z);
	
	D3DXVECTOR3 vDelta = pCollisionSphere->GetOrigin() - vClosest;

	float dist = D3DXVec3Dot(&vDelta, &vDelta);

	if(dist > pCollisionSphere->GetRadius() * pCollisionSphere->GetRadius())
		return NO_COLLISION;

	float d = sqrt(dist);

	D3DXVECTOR3 vCollisionNormal = vDelta / d;
	float fIntersectionDepth = (pCollisionSphere->GetRadius() - d);

	if(fIntersectionDepth == 0.0f)
		return RESTING_CONTACT;

	return HAS_COLLISION;
Advertisement
"is no longer intersecting" is not a detailed description of what you want.

The simplest correction would be to shift the sphere's origin along what you've called the collision normal, because the intersection depth is given for that direction:
pCollisionSphere->GetOrigin() += vCollisionNormal * fIntersectionDepth

However, that makes the correction without taking any history into account. What I mean is that if the collision occured due to a motion of the (e.g.) sphere, then repelling the sphere in an arbitrary direction and amount (here arbitrary w.r.t. the motion) may look unnaturally. In this case the sphere seems to suddenly stick with the plane as soon as the collision occurs. This may or may not what you want. So you have to do the collision correction w.r.t. the "physical" effect that you've in mind, but we don't know what that is ...
Quote:Original post by haegarr
However, that makes the correction without taking any history into account. What I mean is that if the collision occured due to a motion of the (e.g.) sphere, then repelling the sphere in an arbitrary direction and amount (here arbitrary w.r.t. the motion) may look unnaturally. In this case the sphere seems to suddenly stick with the plane as soon as the collision occurs. This may or may not what you want. So you have to do the collision correction w.r.t. the "physical" effect that you've in mind, but we don't know what that is ...



The sphere would be in motion, so I am not sure if my initial collision detection would have to change also. I know that the collision detection being a sphere/plane and a in motion sphere/plane is a little bit different. What would be the most natural looking way to adjust for intersection?
1) find point on box surface closest to the sphere centre.
2) if point outside sphere, no collision.
3) else reflect against normal (point - sphere.centre); That's your collision plane.

Everything is better with Metal.

I've recently composed a little tutorial on how to sweep a sphere against a polygon.
Quote:Original post by oliii
1) find point on box surface closest to the sphere centre.
2) if point outside sphere, no collision.
3) else reflect against normal (point - sphere.centre); That's your collision plane.


Maybe I worded my question wrong.

I know how to detect collision with a sphere and rectangle, but if the sphere and rectangle are colliding and the sphere is inside the rectangle, I am not sure what is the proper way to adjust the sphere so it is no longer intersecting it.
Quote:I know how to detect collision with a sphere and rectangle, but if the sphere and rectangle are colliding and the sphere is inside the rectangle, I am not sure what is the proper way to adjust the sphere so it is no longer intersecting it.
Was this the answer you were looking for?
Quote:Original post by haegarr
The simplest correction would be to shift the sphere's origin along what you've called the collision normal, because the intersection depth is given for that direction:
pCollisionSphere->GetOrigin() += vCollisionNormal * fIntersectionDepth
Like haegarr said, this may not always give the desired results (depending on the circumstances), but geometrically speaking, it will resolve the intersection in the most direct way possible, if that's what you're looking for.
Not entirely, just in case for the strange reason that it is not actually in resting contact but the intersection depth is zero (very rare, but it could happen). Plus if the sphere is approaching the rectangle on the x axis (rectangle is 90 x rotated), it would pop above the rectangle rather then move back on the x.

I am trying to put together a solution that is a mix between mine and Eric_Brown's. However, I am not sure if that is more of a hack
Well I just tested implementing browns tutorial with my code, and I must have messed something up to say the least. In fact, I am not even sure if hacking his code into mine is suppose to be the correct formular. Here is what I came up with, P4 will be (-1.#, -1.#, -1.#) the first time there is collision (and the sphere is intersecting the rectangle).

// If there is no collision, will in intersect?	D3DXVECTOR3 P1 = pRigidBodySphere->GetOrigin() - (vCollisionNormal * pRigidBodySphere->GetRadius());	float s = d - (D3DXVec3Dot(&P1, &vCollisionNormal));	D3DXVECTOR3 P2 = P1 + (pRigidBodySphere->GetVelocity() * s);	D3DXVECTOR3 P3;	// Calculate the nearest point	{		float t = (D3DXVec3Dot(&vCollisionNormal, &P2) - d) / D3DXVec3Dot(&vCollisionNormal, &vCollisionNormal);		P3 = P2 - (t * vCollisionNormal);	}		float sweep = D3DXVec3Dot(&(P3 - pRigidBodySphere->GetOrigin()), &pRigidBodySphere->GetVelocity())*2 -	4 * (D3DXVec3Dot(&pRigidBodySphere->GetVelocity(), &pRigidBodySphere->GetVelocity())) *	D3DXVec3Dot(&(P3 + pRigidBodySphere->GetOrigin()), &(P3 - pRigidBodySphere->GetOrigin())) - (pRigidBodySphere->GetRadius()*2);	float t = (sqrt(sweep) - D3DXVec3Dot(&(2 * (P3-pRigidBodySphere->GetOrigin())), &pRigidBodySphere->GetVelocity())) / D3DXVec3Dot(&(2*pRigidBodySphere->GetVelocity()), &pRigidBodySphere->GetVelocity());	D3DXVECTOR3 P4 = P3 - (pRigidBodySphere->GetVelocity() *t);


Did I do something wrong here or is this the wrong way to go about this?
Quote:Well I just tested implementing browns tutorial with my code, and I must have messed something up to say the least.
We were just discussing in another thread that the method described in that tutorial is not actually correct (see the thread here for further details).

This topic is closed to new replies.

Advertisement