mouse to ray - sphere collision detection - c++

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!

It looks like you are doing something with Cramer's rule to solve for intersection. Consider substitution. The roots of the polynomial will tell you the points of intersection.
Starting with the 2D case what we want is to see if the orthogonal (and thus the minimum) distance of the point P located at the center of circle C has a distance less than the radius R of the circle C.
Basically. We find the minimum distance between the center of the circle and your ray/line. How do we do this? There are a few ways.
We know the shortest distance is a straight line that has the orthogonal slope (in R2 negative recip ) to our ray starting at the middle of the circle. We then find the intersection between our two lines. if we had to go more than length R we are outside and we don't care how far it is.
http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html
Solving the intersection of two lines and seeing it the intersection is further than R may not be the most efficient approach but fortunately wolfram has a better way to do essentially the same thing using higher level math.
Now considering a ray in R3 intersecting a sphere its basically the same thing, but "orthogonal" is harder to please than in R2, so we use double cross products. and solve parametric equations.
http://www.cs.umbc.edu/~olano/435f02/ray-sphere.html
This is a clever way to see if any portion of our ray satisfies the equation of our sphere as a constraint.
float a = Vector3::dot(d,d);
float b = Vector3::dot(d * 2, p);
float c = Vector3::dot(p,p) - r*r
// if the discriminant of the quadratic formula is positive
// we have at least one intersection
return (b*b - 4 * a * c) >= 0
In short. I only found Cramer's rule helpful in differential equations when my columns were functions and its derivitives. Usually when finding the Wronskian.

Related

How to get angle between two vectors in 3D

I have two objects one sphere and a cone. I want cone to always face the sphere as shown in the images.
we have constructed the cone in local coordinate system in such a way, that the tip of the cone points upward the y-axis and the center is at the origin (0,0,0).
The angle between two 3D vectors would be
float fAngle = std::acos(dot(sphereVector, coneVector) / magnitude(sphereVector * magnitude(coneVector)));
For cone to be always facing the sphere it need to be rotated in all three axis based on the position of the sphere but i am getting only one angle from the maths formula.
How do i calculate all the three angles for the cone that it is always perpendicular to the sphere.
First, you need the vector where the cone should point to:
direction = center_cone - center_sphere;
Then, we assume, that you've constructed your cone in the local coordinate system in such a way, that the tip of the cone points upward the y-axis and the center is at the origin (0,0,0).
The axises to rotate are:
x_axis(1, 0, 0);
y_axis(0, 1, 0);
z_axis(0, 0, 1);
Now, you simply have to project the axises to the direction vector to get the 3 angles.
Example:
float angle(vec a, vec b)
{
return acos(dot(a, b) / (magnitude(a) * magnitude(b)));
}
vec direction = normalize(center_cone - center_sphere);
float x_rot = angle(x_axis, direction);
float y_rot = angle(y_axis, direction);
float z_rot = angle(z_axis, direction);

How draw Hyperboloids with OpenGL C++?

I'm trying to draw quadrics in C ++ with OpenGL.
Quadrics are the Sphere, and Hyperboloides of a leaf and two leaves. To draw them I use only the parametric equations of each. The sphere if I drew
Example: The parametric equations of the Sphere are:
To draw the sphere with these equations, what I do in OpenGL is simply this for a sphere that is draw on the origin x0, y0 and z0 equal to zero:
void Sphere(){
GLfloat x, y, z, alpha, beta; // Storage for coordinates and angles
GLfloat radius = 1.0f;
int gradation = 20;
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
for (float alpha = 0.0; alpha < PI; alpha += PI/gradation)
{
glBegin(GL_TRIANGLE_STRIP);
for (beta = 0.0; beta < 2.01*PI; beta += PI/gradation)
{
x = radius*cos(beta)*sin(alpha);
y = radius*sin(beta)*sin(alpha);
z = radius*cos(alpha);
glVertex3f(x, y, z);
}
glEnd();
}
glutSwapBuffers();
}
I the result is this (a sphere):
Problem:
By applying the same logic to the draw hyperboloids, I do not get what I want. The parametric equations of the Hyperboloides are as follows:
When attempting to draw the hyperboloid of one sheet with the equations shown in the picture, this is the result:
If anyone knows anything about it, please if you can help me with this. Thank you all.
Note: I know there are other ways to draw a hyperboloid, but the way I draw them is through its parametric equations.
What is the code that produces this output, not the code that produces your sphere? Please provide a MCVE.
Here is my guess: the range of your loops produces only positive values of z, and you might try the range -PI to PI instead.

Orbiting object around orbiting object

How do I get to orbit green circle around orange and blue around green ?
I found many solutions which works fine with rotating around static point(int this case orange circle) but didn't find any good maths equation which would work for both static and moving points.
angle += sunRot;
if(angle > 360.0f)
{
angle = 0.0f;
}
float radian = glm::radians(angle);
float radius = glm::distance(position, rotCenter);
float x = rotCenter.x + (radius * cosf(radian));
float z = rotCenter.z + (radius * sinf(radian));
glm::vec3 newPos = glm::vec3(x, 0, z);
setPosition(newPos);
Here is what I'm trying to achieve (Thanks to #George Profenza for sharing link)
Base all your calculations on the radius and angle of the current object where possible and store the radius and angle with the object.
In particular, do not calculate the radius based on the x/y coordinates in every iteration: If the base object has moved between steps, your calculated radius will be slightly off and the error will accumulate.
You should be able to nest coordinate spaces using opengl using glPushMatrix(), glPopMatrix() calls. Here's a basic example(press mouse to see coordinate spaces).
The syntax isn't c++, but it's easy to see what I mean.
You can do this multiple ways:
polar coordinate formula
manually multiplying transformation matrices
simply using push/pop matrix calls (along with translate/rotate where needed), which does the matrix multiplication for you behind the scenes.
Just in case you want to try the polar coordinate formula:
x = cos(angle) * radius
y = sin(angle) * radius
Where angle is the current rotation of a circle and the radius is it's distance from the centre of rotation.

Picking Ray is inaccurate

I'm trying to implement a picking ray via instructions from this website.
Right now I basically only want to be able to click on the ground to order my little figure to walk towards this point.
Since my ground plane is flat , non-rotated and non-translated I'd have to find the x and z coordinate of my picking ray when y hits 0.
So far so good, this is what I've come up with:
//some constants
float HEIGHT = 768.f;
float LENGTH = 1024.f;
float fovy = 45.f;
float nearClip = 0.1f;
//mouse position on screen
float x = MouseX;
float y = HEIGHT - MouseY;
//GetView() returns the viewing direction, not the lookAt point.
glm::vec3 view = cam->GetView();
glm::normalize(view);
glm::vec3 h = glm::cross(view, glm::vec3(0,1,0) ); //cameraUp
glm::normalize(h);
glm::vec3 v = glm::cross(h, view);
glm::normalize(v);
// convert fovy to radians
float rad = fovy * 3.14 / 180.f;
float vLength = tan(rad/2) * nearClip; //nearClippingPlaneDistance
float hLength = vLength * (LENGTH/HEIGHT);
v *= vLength;
h *= hLength;
// translate mouse coordinates so that the origin lies in the center
// of the view port
x -= LENGTH / 2.f;
y -= HEIGHT / 2.f;
// scale mouse coordinates so that half the view port width and height
// becomes 1
x /= (LENGTH/2.f);
y /= (HEIGHT/2.f);
glm::vec3 cameraPos = cam->GetPosition();
// linear combination to compute intersection of picking ray with
// view port plane
glm::vec3 pos = cameraPos + (view*nearClip) + (h*x) + (v*y);
// compute direction of picking ray by subtracting intersection point
// with camera position
glm::vec3 dir = pos - cameraPos;
//Get intersection between ray and the ground plane
pos -= (dir * (pos.y/dir.y));
At this point I'd expect pos to be the point where my picking ray hits my ground plane.
When I try it, however, I get something like this:
(The mouse cursor wasn't recorded)
It's hard to see since the ground has no texture, but the camera is tilted, like in most RTS games.
My pitiful attempt to model a remotely human looking being in Blender marks the point where the intersection happened according to my calculation.
So it seems that the transformation between view and dir somewhere messed up and my ray ended up pointing in the wrong direction.
The gap between the calculated position and the actual position increases the farther I mouse my move away from the center of the screen.
I've found out that:
HEIGHT and LENGTH aren't acurate. Since Windows cuts away a few pixels for borders it'd be more accurate to use 1006,728 as window resolution. I guess that could make for small discrepancies.
If I increase fovy from 45 to about 78 I get a fairly accurate ray. So maybe there's something wrong with what I use as fovy. I'm explicitely calling glm::perspective(45.f, 1.38f, 0.1f, 500.f) (fovy, aspect ratio, fNear, fFar respectively).
So here's where I am lost. What do I have to do in order to get an accurate ray?
PS: I know that there are functions and libraries that have this implemented, but I try to stay away from these things for learning purposes.
Here's working code that does cursor to 3D conversion using depth buffer info:
glGetIntegerv(GL_VIEWPORT, #fViewport);
glGetDoublev(GL_PROJECTION_MATRIX, #fProjection);
glGetDoublev(GL_MODELVIEW_MATRIX, #fModelview);
//fViewport already contains viewport offsets
PosX := X;
PosY := ScreenY - Y; //In OpenGL Y axis is inverted and starts from bottom
glReadPixels(PosX, PosY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, #vz);
gluUnProject(PosX, PosY, vz, fModelview, fProjection, fViewport, #wx, #wy, #wz);
XYZ.X := wx;
XYZ.Y := wy;
XYZ.Z := wz;
If you do test only ray/plane intersection this is the second part without DepthBuffer:
gluUnProject(PosX, PosY, 0, fModelview, fProjection, fViewport, #x1, #y1, #z1); //Near
gluUnProject(PosX, PosY, 1, fModelview, fProjection, fViewport, #x2, #y2, #z2); //Far
//No intersection
Result := False;
XYZ.X := 0;
XYZ.Y := 0;
XYZ.Z := aZ;
if z2 < z1 then
SwapFloat(z1, z2);
if (z1 <> z2) and InRange(aZ, z1, z2) then
begin
D := 1 - (aZ - z1) / (z2 - z1);
XYZ.X := Lerp(x1, x2, D);
XYZ.Y := Lerp(y1, y2, D);
Result := True;
end;
I find it rather different from what you are doing, but maybe that will make more sense.

Move objects to specified points in a scene?

I have been trying to throw together a chess game using a 3dsmax model. At this point, I have been able to import the model, highlight the selected game piece I am interested in moving, and choose a square I want to move to. Here is a screenshot of the current state:
http://img26.imageshack.us/img26/9555/chessk.png
The black circle represents where I clicked, and you can see where the pawn went. I haven't done specific calculations on where it should go. Whenever I click on the board with a selected piece, it always moves in the same direction. It's because just threw in this dummy code to start off:
if ( isObjectSelected && isSquareSelected && moveObject )
{
glPushMatrix();
glTranslatef(0.2f, 0.0f, 0.0f); //PLACEHOLDER-DUMMY CODE
}
glDrawElements( GL_TRIANGLES, pMaterial->triangleCount * 3, GL_UNSIGNED_INT, model.getIndexBuffer() + pMaterial->startIndex );
if ( isObjectSelected && isSquareSelected )
glPopMatrix();
What I was considering doing was after the model was done loading, is to somehow check which square on the board a game piece occupies. Then, when a piece is selected and a "move to" square is selected, find the x,y,z glTranslate3f to move to that center square.
Is this the best way? It seems as the game progresses, I will need to store the glTranslate of each piece individually. And when a piece goes from it's 2nd to 3rd spot, I should calculate the glTranslate from the original starting point to the 3rd spot, right?
But how would you figure out whether or not a game piece occupies a square, and how would you figure out glTranslate3f(X, Y, Z) between two squares? Here is an example of a square from my .OBJ file
#
# object Square58
#
v -37.1874 18.6313 80.7864
v -67.0955 18.6313 91.4436
v -56.4384 18.6313 121.3513
v -26.5306 18.6313 110.6938
# 4 vertices
vn 0.0000 1.0000 -0.0000
# 1 vertex normals
vt 0.0000 0.0000 0.0000
# 1 texture coords
I am assuming I would need to find the center of each square and say once the app knows this piece is in square1, and you clicked on sqaure4, calculate the translate & go. I am just not sure how to calculate the center of each square, and figure out what the translate coords should be from square1-->square4.
OR how I would determine which pieces occupies which square from the beginning. I can hard code this in during load, but it would help me more in understanding if there was a sound way to accomplish this.
Each square & game piece is a struct GroupObject like:
//A chess piece or square
struct GroupObject
{
std::vector<Material *> materials;
std::string objectName;
std::string groupName;
int index;
std::vector<Vector3 *> vertices;
Vector3 center;
};
And Vector3 looks like:
#ifndef VECTOR3_H
#define VECTOR3_H
#include <math.h>
class Vector3
{
public:
Vector3(float X = 0.0f, float Y = 0.0f, float Z = 0.0f)
{
x = X;
y = Y;
z = Z;
}
Vector3 operator+=(const Vector3 &vec)
{
return (*this = (*this + vec) );
}
Vector3 operator+(const Vector3 &vec)
{
return Vector3(vec.x + x, vec.y + y, vec.z + z);
}
Vector3 operator-=(const Vector3 &vec)
{
return (*this = (*this - vec) );
}
Vector3 operator-(const Vector3 &vec)
{
return Vector3(x - vec.x, y - vec.y, z - vec.z);
}
Vector3 operator*=(float num)
{
return (*this = (*this * num) );
}
Vector3 operator*(float num)
{
return Vector3(x * num, y * num, z * num);
}
Vector3 operator/=(float num)
{
return (*this = (*this / num) );
}
Vector3 operator/(float num)
{
return Vector3(x / num, y / num, z / num);
}
Vector3 operator-(void)
{
//invert direction of vector
return Vector3(-x, -y, -z);
}
float Dot(Vector3 &vec)
{
return (x * vec.x + y * vec.y + z * vec.z);
}
Vector3 operator*(const Vector3 &vec)
{
//cross product
return Vector3( y * vec.z - z * vec.y,
z * vec.x - x * vec.z,
x * vec.y - y * vec.x );
}
float Length(void)
{
//length of vector
return sqrt( x * x + y * y + z * z);
}
Vector3 Normalize(void)
{
float length = Length();
x /= length;
y /= length;
z /= length;
return *this;
}
float Distance(Vector3 &vec)
{
//find distance between two separate vectors
float disX = vec.x - x;
float disY = vec.y - y;
float disZ = vec.z - z;
return sqrt(disX * disX + disY * disY + disZ * disZ);
}
bool operator==(Vector3 &vec)
{
return (vec.x == x && vec.y == y && vec.z == z);
}
bool operator!=(Vector3 &vec)
{
return !(vec == *this);
}
public:
float x, y, z;
};
#endif
I was going to use this to store the center of each square like:
Vector3 square->center = X, Y, Z
Hopefully this would help it determining where to move, and if something occupies that space, but figured I should get some help since I haven't done this before. I am also not sure how I should calculate it, or still completely clear on how to figure out whether a square has a game piece that occupies it at the beginning, or it's empty.
Any and all help is welcome. Thank you.
UPDATE
Right now I had a buddy look in 3ds max and on a square object properties it said dimensions were x: 40.565 y: 40.565
Then I picked two squares, one in front of the other. I calculated the center of each by added all the vertices together and divided by the sum of the vertices. I got this:
#square 1
x: 111.12125
y: 18.631268
z: 78.286982
#square 2
x: 82.276817
y: 17.615297
z: 88.545845
But from my original example, the only way I could get even close to moving to the right spot was moving glTranslatef(0.2f, 0.0f, 0.0f);
The difference between the two centers listed above is much larger. I am doing something incorrect, but not seeing it.
[a-bit-offtopic]
When i was coding my univercity project, i used to draw my 'world' (which has something similar with check-desk: it is endless and have no colour distincts) using simple math formulas for determining each line cross points. When i got those points, i was able to simply determine which particular square cursor is pointing. To be honest, i used simple mathematics to determine those centers in the last ('production') version.
Here and there are my old screens.
So, what's the trouble? When you're doing glPushMatrix(), your matrix resets to its defaults - the position becomes (0, 0, 0) and rotations are reset. If you wanna put your object in the point of intersect {line, plane}, where the line is the ray from camera origin through cursor position (recall camera theory in OpenGL: actually, screen is the plane in front of camera 'position' point) and the plane is something in front of that ray. Note: if you'll not define the plane - you'll be not able to determine intersection point.
Now, when you'll got intersection point, just do glTranslatef(<intersection_point>) and then draw your pawn or whatever.
If you are interested in pure code on how to determine intersection point - just notify me in comments.
Hope this will help.
UPD: here's the intersection point code i've mentioned before. It uses sf::Vector3f structure/class - it is taken from SFML (i'm using it for all my GL projects to handle input events and forget 'bout window creation). You can define it as a simple structure with 3 floats. Function arguments are the mouse cursor' coordinates. Calling this would provide you with intersection point coordinates.
sf::Vector3f ScreenToSpace(int x, int y)
{
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble posX, posY, posZ;
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetIntegerv(GL_VIEWPORT, viewport);
winX = (float) x;
winY = (float) viewport[3] - (float) y;
glReadPixels(x, int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
return sf::Vector3f((float) posX, (float) posY, (float) posZ);
}
Here's the example on how you should use this (as far as i've understood your problem):
sf::Vector3f v = ScreenToSpace(App.GetInput().GetMouseX(), App.GetInput().GetMouseY()); // get global coordinates
v = SpaceToDeskCorrection(v.x, v.y, v.z); // align v to the check-desk cell center
glPushMatrix(); // reset matrix state (incl. position and rotation) and save previous one
glTranslatef(v.x, v.y, v.z); // set matrix position to the intersection point
DrawPawn(); // well, you'll understand this line, right? =)
glPopMatrix();
I have recently done a checkers game myself, 2D but the same principles apply. First of all I would suggest you keep an array with board squares, which as you defined in your struct has a centre var.
To know where each piece is, you could for instance add a variable which is a pointer to a given square, or simply a representation like 'E4' or 'A8' (square code). Having this, you would automatically have the x,y coordinates of where the piece is, and once detecting which square you want to move it to, you just substitute the pointer to the new piece and get its centre information, which is where you want to translate the piece to. This also has the added benefit that it makes it easier for you to check later when a piece takes over another one from the adversary, or simply if it can move to a certain square (go through all the pieces, if any of them is laying on the square you want to go to, don't allow the move).
To find out the centre of any square, assuming you don't know it when you create the board (don't you ?), and do an average over all the constituting vertices x and y coordinates (if the board is laying on the xy plane). For instance, Square58 centre = (v1(x)+v2(x)+v3(x)+v4(x) ; v1(y)+v2(y)+v3(y)+v4(y)).
I hope that was clear enough, if not just let me know what you didn't get and I'll try to explain.
EDIT: Sorry, I wrote something wrong. In the Square58 centre.... line, you divide the final sum by 4 (to get the average).. ie:
Square58 centre = ((v1(x)+v2(x)+v3(x)+v4(x))/4 ; (v1(y)+v2(y)+v3(y)+v4(y))/4).