• Advertisement
Sign in to follow this  

mouse to ray - sphere collision detection help

This topic is 2299 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all

I have been trying to get a working algorithm that detects intersection between a ray (representing the bullets from a gun) and a sphere around the enemy.... I tried a few found on the net but none seems to work properly, maybe I am doing something wrong...

This is the one I am currently using:


//// Ray-sphere intersection.
// p=(ray origin position - sphere position),
// d=ray direction,
// r=sphere radius,
// Output:
// i1=first intersection distance,
// i2=second intersection distance
// i1<=i2
// i1>=0
// returns true if intersection found,false otherwise.//


bool Player::RaySphereIntersect(const Vector3 &p, const Vector3 &d, double r, double &i1, double &i2){
double det,b;
b = -Vector3::dot(p,d);
det = b*b - Vector3::dot(p,p) + r*r;
if (det<0){
return false;
}
det= sqrt(det);
i1= b - det;
i2= b + det;

// intersecting with ray?
if(i2<0)
return false;
if(i1<0)
i1=0;
return true;
}


Where I use the position of the enemy as sphere position, roughly the position of the player's gun as ray origin and the projected mouse coordinates for ray direction... This is the OpenGL code I am using to project the mouse coords to the the far plane:



Vector3 projectedMouse(float mx, float my){

GLdouble model_view[16];
GLint viewport[4];
GLdouble projection[16];

GLfloat winX, winY, winZ;
GLdouble dx, dy, dz, bx, by, bz;

glGetDoublev(GL_MODELVIEW_MATRIX, model_view);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetIntegerv(GL_VIEWPORT, viewport);

winX = (float)mx;
winY = (float)viewport[3] - (float)my;

glReadPixels ((int)mx, (int)winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
gluUnProject(winX, winY, 1, model_view, projection, viewport, &dx, &dy, &dz);

projectedAim = Vector3(dx, dy, dz);

return projectedAim;
}


Which seems right cos I am drawing a GL Line with it and it looks fine... So maybe it's the intersection code, but nothing seems to work.... I tried this other one that should return the intersection point distance, but for any given enemy position, it still gives me very random results:

double intersectRaySphere(Vector3 rO, Vector3 rV, Vector3 sO, double sR)

Vector3 Q = sO-rO;
double c = Q.magnitude();
double v = Vector3::dot(Q,rV);
double d = sR*sR - (c*c - v*v);

// If there was no intersection, return -1
if (d < 0.0) return (-1.0f);

// Return the distance to the [first] intersecting point
return (v - sqrt(d));


they have both been slightly modified to match the Math function in the library that I am using.... can anyone spot something wrong with them, or suggest another one? this is driving me crazy....

Thank you!

Share this post


Link to post
Share on other sites
Advertisement
It looks like the algorithms you have assume that the line direction is a normal vector. Are you normalizing the vector before you pass it in?

Share this post


Link to post
Share on other sites
from what I gather:


c is magnitude of ray origin to sphere center.

v is c converted along vector rv since rv is a unit vector the dot product wth lengthed vector Q would return length along rv direction

There is a line segment perpendicular to rV passing through sphere center at a length v along rV direction. The length of this segment is (c*c) - (v*v).

the ray intersecting with the sphere will have the first (closest) intersection point at distance sR (radius of shphere) from sphere center sO.

ths is also alon vector rV. so now we have two points along rV. we know the length to one is sRr and the other forms a segment perpendicular to rV. The collision point and v distance point form a right triangle wth sR as the length of the hypotenuse.

so the distance ALONG rV to the collision point is v- sqrt( (sr*sr) - ((c*c)-(v*v)) ). they represented d as the big portion in the brackets.

This function appears to work fine.

Are you applying your final distance along rV to indicate collision point? if rV is not normalized you may end up with strange results.

your collision point should be rO + (rV*finaldist).

Share this post


Link to post
Share on other sites

It looks like the algorithms you have assume that the line direction is a normal vector. Are you normalizing the vector before you pass it in?


Thank you for your response! You know I suspected that, but I was kind of confused by it.... I assumed that the ray direction vector would simply be the ray from the gun to the projected mouse coordinates on the far plane, and have therefore used the difference of the two for the vector that I pass...

So what would I normalize then? simply the unprojected mouse coordinates? Or the difference between ray origin and unprojected cursor?

Thank you very much

Share this post


Link to post
Share on other sites


This function appears to work fine.

Are you applying your final distance along rV to indicate collision point? if rV is not normalized you may end up with strange results.

your collision point should be rO + (rV*finaldist).


Thanks a lot for your explanation! Well, still no luck tho. So here is what I am passing into the function:

double d = intersectRaySphere(entity.getPosition(), shootingRay, Vector3(0, 100, -300), 30);


where the first paramenter is the position of the character, the second is the unprojected mouse coordinates on the far plane according to the function above, the third is the position of the stationary enemy and the fourth is a radius... I usually get similar d values in the order of 0.1 to 0.3 when I hover the mouse over the center of the object when positioned in different spots...still it's not really precise enough for shooting....

I tried normalizing the unprojected mouse coords but I then get it never returns any collision.... The Normalization code i have is:


inline void Vector3::normalize()
{
float invMag = 1.0f / magnitude();
x *= invMag, y *= invMag, z *= invMag;
}


any ideas?

Share this post


Link to post
Share on other sites
[color="#000088"]double d [color="#666600"]= intersectRaySphere[color="#666600"](entity[color="#666600"].getPosition[color="#666600"](), shootingRay[color="#666600"], [color="#660066"]Vector3[color="#666600"]([color="#006666"]0[color="#666600"], [color="#006666"]100[color="#666600"], [color="#666600"]-[color="#006666"]300[color="#666600"]), [color="#006666"]30[color="#666600"]);you have the unprojected coordinates on the far plane and the source of the gun (character). what you are passing in is not the proper ray.

to get the ray do

farCoords = projectedMouse[color="#666600"]( mx[color="#666600"], [color="#000088"]my[color="#666600"]);

then shootingRay = farCoords - entity.getPosition(); now i know this isn't the gun muzzle position but you can easily sub that in for the getposition(),, im just using stuff from your code that I can name.
then shootingRay.normalize();

then do
[color="#000088"]double d [color="#666600"]= intersectRaySphere[color="#666600"](entity[color="#666600"].getPosition[color="#666600"](), shootingRay[color="#666600"], [color="#660066"]Vector3[color="#666600"]([color="#006666"]0[color="#666600"], [color="#006666"]100[color="#666600"], [color="#666600"]-[color="#006666"]300[color="#666600"]), [color="#006666"]30[color="#666600"]);
this will fire a ray from your entity at a sphere of radius 30 around the enemy character's coordinates of (0,100,-300)

Share this post


Link to post
Share on other sites

[color="#000088"]double d [color="#666600"]= intersectRaySphere[color="#666600"](entity[color="#666600"].getPosition[color="#666600"](), shootingRay[color="#666600"], [color="#660066"]Vector3[color="#666600"]([color="#006666"]0[color="#666600"], [color="#006666"]100[color="#666600"], [color="#666600"]-[color="#006666"]300[color="#666600"]), [color="#006666"]30[color="#666600"]);you have the unprojected coordinates on the far plane and the source of the gun (character). what you are passing in is not the proper ray.

to get the ray do

farCoords = projectedMouse[color="#666600"]( mx[color="#666600"], [color="#000088"]my[color="#666600"]);

then shootingRay = farCoords - entity.getPosition(); now i know this isn't the gun muzzle position but you can easily sub that in for the getposition(),, im just using stuff from your code that I can name.
then shootingRay.normalize();

then do
[color="#000088"]double d [color="#666600"]= intersectRaySphere[color="#666600"](entity[color="#666600"].getPosition[color="#666600"](), shootingRay[color="#666600"], [color="#660066"]Vector3[color="#666600"]([color="#006666"]0[color="#666600"], [color="#006666"]100[color="#666600"], [color="#666600"]-[color="#006666"]300[color="#666600"]), [color="#006666"]30[color="#666600"]);
this will fire a ray from your entity at a sphere of radius 30 around the enemy character's coordinates of (0,100,-300)


That worked. It's the normalization bit that I was missing....gosh that took a whole day of my life trying to figure this out... you're a star ;)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement