Ray vs Sphere Issue

Started by
6 comments, last by Cydriic 9 years, 1 month ago

Hi Everyone,

I'm having this weird issue with my code for projectile collisions in my 3D game.

My gun functions with casting a Ray and checking for collisions against Spheres around my objects , located in the appropriate Collision Tile, of my Uniform Grid Tile System.

Now everything works fin, but sometimes for a reason I can't figure out, my Ray Collides with spheres behind the character firing the gun. And so it screwes up everything, and since I have a sorting process right after collision detections that determines which collision happened first to determine which object takes the fire (because my projectile does not travel through objects) it makes it not work as it should.

Here is some code that shows you what going on.

DragonSC is a car object and here is the collision code that determines if the DragonSC's Collision Sphere, collides with the Ray being Casted.

x,y,z are the starting coordinates of my ray, starting at the Tip of the Fired Gun, xa, ya, za are the end coordinates of the ray (10000.0f further down the Line). Roty is the Angle the Firing character is facing and Rotx is the Angle at which the gun is firing, meaning at 90.0f the gun would be firing straight up in the air, parrallel to the Y axis.

Is there anything not right in my algorythm, I checked it 100 times using Sources on Stack Overflow and my Mathematics for 3D Game Programming & Computer Graphics Book by Eric Lengyel.

bool CDragonSC::FastProjectileCollisions(GLfloat x, GLfloat y, GLfloat z, GLfloat Rotx, GLfloat Roty)
{
//COMPUTE END POINT OF THE LINE 10000.0F UNITS FURTHER FROM THE START
GLfloat xa = x - (float)sin(Roty*piover180) * 10000.0f;
GLfloat ya = y - (float)sin(Rotx*piover180) * 10000.0f;
GLfloat za = z - (float)cos(Roty*piover180) * 10000.0f;
//COMPUTE COEFFICIENTS
GLfloat a = (xa-x)*(xa-x)+(ya-y)*(ya-y)+(za-z)*(za-z);
GLfloat b = 2*((xa-x)*(x-m_x)+(ya-y)*(y-m_y)+(za-z)*(z-m_z));
GLfloat c = (x-m_x)*(x-m_x)+(y-m_y)*(y-m_y)+(z-m_z)*(z-m_z)-( 640000 );
//COMPUTE DELTA
GLfloat Delta=(b*b)-4*a*c;
//INITIALIZE SOME VARIABLES
GLfloat d, d2, xc, yc, zc, xc2, yc2, zc2;
//NO COLLISION
if(Delta < 0)
return false;
//ONE COLLISION
if(Delta == 0)
{
d = b/(2*a);
//COLLISION COORDINATES
xc = x + d*(xa-x);
yc = y + d*(ya-y);
zc = z + d*(za-z);
//SET OBJECTS COLLISION COORDINATES
m_Cx = xc;
m_Cy = yc;
m_Cz = zc;
return true;
}
//TWO COLLISION POINTS
if(Delta > 0)
{
d=(-b-sqrt(Delta))/(2*a);
d2=(-b+sqrt(Delta))/(2*a);
//COLLISION COORDINATES
xc = x + d*(xa-x);
yc = y + d*(ya-y);
zc = z + d*(za-z);
xc2 = x + d2*(xa-x);
yc2 = y + d2*(ya-y);
zc2 = z + d2*(za-z);
//SET OBJECTS COLLISION COORDINATES
m_Cx = xc;
m_Cy = yc;
m_Cz = zc;
return true;
}
return false;

}

Thanks

Advertisement

Maybe try to cull the spheres behind the character? Since the gun is firing in the facing direction of the player, you can take the ray as a normal for a plane crossing the player, and then don't check collision in spheres that are on one of the sides of the plane.

New game in progress: Project SeedWorld

My development blog: Electronic Meteor

Thanks, it's a good Idea.

I thought about it and it would work, but it seems to be limited.

I mean, is this a common problem with ray casting or am I missing something here? Is the culling of spheres behind the character that is firing the ACTUAL thing that is missing?

I'm trying first to not have to do that.

You need to check the values of d and d2 (in your code) to use the smallest one, these are distance along the ray's direction to the intersection. If the smallest one is negative, there is no intersection since the sphere is behind.

Something like this (note line "if ( t1 < T(0) )"):


template < typename T >
Bool Ray3D<T>:: intersectsSphere( const Vector3D<T>& position, T radius, T& distance ) const
{
	Vector3D<T> d = position - origin;
	T dSquared = d.getMagnitudeSquared();
	T rSquared = radius*radius;
	
	if ( dSquared < rSquared )
	{
		// The ray starts inside the sphere and so there is an intersection.
		distance = T(0);
		return true;
	}
	else
	{
		// Find the closest point on the ray to the sphere's center.
		T t1 = math::dot( d, direction );
		
		if ( t1 < T(0) )
		{
			// The ray points away from the sphere so there is no intersection.
			return false;
		}
		
		// Find the distance from the closest point to the sphere's surface.
		T t2Squared = rSquared - dSquared + t1*t1;
		
		if ( t2Squared < T(0) )
			return false;
		
		// Compute the distance along the ray of the intersection.
		distance = t1 - math::sqrt(t2Squared);
		
		return true;
	}
}

Sphere culling at least improves performance (even if sphere collision is already simple enough to test). What I am not clear about in your original post is that when you say the ray collides with spheres behind the player, does this mean it is ONLY colliding with those spheres, or is it also colliding with the spheres in front, and sometimes in the back, depending on the ray direction where the spheres are located? Are you sure the ray is always firing forward from the player?

I also think you can try this alternative method of culling, which I just remembered. For every sphere, make a ray connecting the sphere to the player. But Aressera just beat me to it, and has code to show for it. But the short of it is, get the dot product with the sphere ray and the firing ray. Acceptable spheres would have a positive dot product.

New game in progress: Project SeedWorld

My development blog: Electronic Meteor

If you don't somehow ignore what's behind you, then you're not dealing with a ray, you're dealing with a line instead -- because ray's are directed, of course.

You can do that by culling, or you can do that by accepting all collisions on a preliminary basis, and then rejecting all but the nearest non-negative collision (or nearest negative, I suppose, depending on whether your frame of reference looks in a positive or negative direction down the axis). But, you need to preserve the signedness of the distance then, if you just have distance it's always positive, you can do a simple half-space test against a plane through your ray origin, and perpendicular to its direction.

throw table_exception("(? ???)? ? ???");

Yessir, CCRicer , you are totally right, it at least improves performance.

And to answer your question, yes the ray will collide with object in front and behind, not only behind if the collision behind happens.

Aressera , Thank you very much for your great answer I did not know that. I will do that.

Ravyne, you're awesome.

All your answers together are helping me a great deal, thank you very much!

Peace my Matrix Brothers. :)

IT WORKS!!!!

WOUHOU ! you guys are the best in the universe.

Thank you!

This topic is closed to new replies.

Advertisement