Jump to content
  • Advertisement
Sign in to follow this  
kelmer

mouse coords to 3D coords

This topic is 3156 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

Are there any good samples/tutorials on these?? I've been searching the forums but only found how to convert the mouse coords so that 0,0 is the center of screen, which maybe is useful but with my little knowledge I can't find a use for. Especifically, I need to move an arm into a 3D space by just clicking anywhere on the screen making the arm move to point in that direction. thanks,

Share this post


Link to post
Share on other sites
Advertisement
There is a DX example available, I believe it's called Pick. Basically it's something like, you transform your 2D mouse coordinates to world coordinates and then use a ray intersection to calculate what the depth of your point is.

Share this post


Link to post
Share on other sites
you can use openGL unProject to do this. it just reverses the projection process by multiplying a projected 3d point by the inverse of the model view projection matrix.in this case your projected point would be mouse x, mouse y and the depth at the current pixel.

unProject also needs the depth of the pixel you can obtain the depth by doing glReadPixels but if you dont want to do a glReadPixels for whatever rerason you can get 2 3d positions by unProjecting at depth = 0 (near) and depth = 1 (far) both at the same x,y coordinate. when used together this makes a ray striaght out of the screen and you can use some ray intersection to get the 3d position in the world.

Edit : sorry the name of the function is gluUnProject

Share this post


Link to post
Share on other sites
Its actualy not that hard.

I adapted this from my own library. It should convert The position of the mouse (win) to 3D.

Because it requires the depth buffer to have your scene in it I call it at the end of the rendering ready to be used in the next frame.

You will need to change Rotation. and Position. to whatever you use to store your camera's position or pass them as params as you prefer.


bool GetMousePosition(int MouseWinX,int MouseWinY, double * Mouse3DX,double * Mouse3DY,double * Mouse3DZ)
{
//Translate to the camera's position and rotation
glLoadIdentity();

glRotatef(Rotation.Y,0,1,0);
glRotatef(Rotation.X,1,0,0);
glRotatef(Rotation.Z,0,0,1);
glTranslatef(Position.X,Position.Y,Position.Z);

GLint view[4];
GLdouble model[16], proj[16];
GLfloat winX, winY, winZ;
glGetDoublev( GL_MODELVIEW_MATRIX, model );
glGetDoublev( GL_PROJECTION_MATRIX, proj );
glGetIntegerv( GL_VIEWPORT, view );
winX = (float)MouseWinX;
winY = (float)view[3]-MouseWinY;
glReadPixels((int)winX,(int)winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
gluUnProject(winX, winY, winZ, model, proj, view, &Mouse3DX, &Mouse3DY, &Mouse3DZ);
return true;
}




[Edited by - empirical2 on November 3, 2009 11:57:11 AM]

Share this post


Link to post
Share on other sites
I tried with unProject too, but the coordinate I got was not very accurate, eg. I set y = 0, but always got it a bit more or less than 0, dunno why....

Share this post


Link to post
Share on other sites
Quote:
Because it requires the depth buffer to have your scene in it I call it at the end of the rendering ready to be used in the next frame.

_I don't think I'm getting this part, what do you mean "you call it"? How do you "call" the depth buffer??

Share this post


Link to post
Share on other sites
What is saying that if you need to query the depth buffer for any reason, do so after all sceen geometry has been rendered (and the depth buffer has been populated with depth values).

Share this post


Link to post
Share on other sites
Okay, how do I do that? I tried without the depth buffer calling and although x and y seem to be right, z is just "too close to the camera", i.e. I draw a solid cube where the user clicked and it is drawn just in front of the camera blocking all the scene.


Sorry for my ignorance

Share this post


Link to post
Share on other sites
My code is near identicle to that posted except I don't read the buffer. Here it is:
/************************************************************
*
* Function : GLGetMouseRay
*
* Description : Calculates the ray in world space at the
* position of the mouse
*
*************************************************************/


void GLGetMouseRay(Int32 MouseX, Int32 MouseY, SRay3d &MouseRay)
{
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY;
GLdouble posX, posY, posZ;

// Retrieve the opengl matrices
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );

// Convert to bottom-left origin
winX = (Float32)MouseX;
winY = (Float32)viewport[3] - (Float32)MouseY;

// Get the position vector
gluUnProject( winX, winY, 0.0f, modelview, projection, viewport, &posX, &posY, &posZ);
MouseRay.pos = SVector3d((Float32)posX, (Float32)posY, (Float32)posZ);

// Get ANY point along the ray
gluUnProject( winX, winY, 1.0f, modelview, projection, viewport, &posX, &posY, &posZ);
MouseRay.dir = SVector3d((Float32)posX, (Float32)posY, (Float32)posZ);

// Calculate the direction vector from the 2 points on the ray
MouseRay.dir = MouseRay.dir - MouseRay.pos;
MouseRay.dir.Normalize();
}





So I'm getting 2 points on the mouse ray (position & direction) by calling unproject twice with 2 different z values: 0 and 1 (front and back of depth buffer). Using the pos and dir, I'm creating a direction vector by subtracting the dir from the pos and normalizing the result. Now I have the ray, I can do various ray intersection tests to see if it intersects with any geometry.

For a simple test, try ray/sphere intersection. The SRay3d struct contains a position and direction vector. The SSphere3d struct contains a position radius. Usng the function below, you can test the newly-created mouse ray against an arbitrary bounding sphere in world space:

Float32 RaySphereIntersection(const SRay3d &Ray, const SSphere3d &Sphere)
{
// We solve this second-degree equation in t:
// distance(p+t*v,center)==radius
// If we define w = p-center
// we can write that as
// <w+t*v,w+t*v> == radius*radius
// <w,w> + 2.0f*t*<w,v> + t*t*<v,v> - radius*radius == 0
// <v,v>*t*t + 2.0f*<w,v>*t + <w,w>-radius*radius == 0
// A*t*t + B*t*t + C*t*t == 0

SVector3d w = Ray.pos - Sphere.pos;
SVector3d temp = Ray.dir;
Float32 A = Ray.dir.Dot(temp);
Float32 B = 2 * w.Dot(Ray.dir);
Float32 C = w.Dot(w) - Sphere.radius * Sphere.radius;

Float32 D = B * B - 4.0f * A *C;
return D >= 0.0f ? (-B - sqrt(D))/(2.0f * A) : 0.0f;
}





Try placing some spheres in your world and try clicking them (maybe have a flag that sets the colour 2 red or something upon collision). In this example I use the distance from the sphere to the ray instead of the ray/sphere intersection. Some rough psuedo-code would look like this:

void Render()
{
// camera/view position stuff...

bool flgSelected = GetSelectedSphere(idxSelected);

for(int ctr = 0; ctr < num_spheres, ctr++)
{
glPushMatrix();
glTranslatef(spheres[ctr].x, spheres[ctr].y, spheres[ctr].z);

if(flgSelected && idxSelected == ctr)
{
glColor3f(1.0f, 0.0f, 0.0f);
}
else
{
glColor3f(1.0f, 1.0f, 1.0f);
}

spheres[ctr].Draw();

glPopMatrix();
}
}

void MouseClick(int X, int Y)
{
mouseX = X;
mouseY = Y;
}

/************************************************************
*
* Function : RayPointDistance
*
* Description : Returns the distance between a given ray and
* point
*
*************************************************************/


Float32 RayPointDistance(const SRay3d &Ray, const SVector3d &Point)
{
SVector3d sp = (Point - Ray.pos);

Float32 t = sp.Dot(Ray.dir);

SVector3d v = sp - (Ray.dir * t);

return v.Dot(v);
}


bool GetSelectedSphere(UIndex &idxSPhere)
{
UInt32 selected = 0;
Float32 shortest = -1.0f;
Float32 f;
SVector3d vtxPos;

// Grab 3d mouse vector from 2d click location
GLGetMouseRay(mouseX, mouseY, mouseRay);

for(UInt32 ctr=0; ctr<num_spheres; ctr++)
{
vtxPos = spheres[ctr].xyzPos;

f = RayPointDistance(worldWindow.mouseRay, vtxPos);

// First 'near hit' OR selection is closer
if((-1.0f == shortest && f <= SELECT_NEAR) || (f >= 0.0f && shortest > f))
{
shortest = f;
selected = ctr;
}
}

// Shortest will be <0 if no vert selected
if(shortest >= 0.0f)
{
idxVert = selected;
return true;
}
else
{
return false;
}

}




The SELECT_NEAR constant is a value that, should no actual ray/sphere intersection occur, will select the closest sphere to the ray. I can't garuntee the above code will work straight out the box, but it should give you a vague idea how a simple mouse selection with spheres could work.

I've mixed the in-built data types (int, float et.) with my own typecasts (UInt32, Float32 etc.) so remove these and replace with the in-built types (UInt32 = unsigned int, Float32 = float, Int32 = int etc.)

[Edited by - JackTheRapper on October 30, 2009 5:21:52 AM]

Share this post


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

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!