2D Click to 3D Point

Disparity
Unregistered
 
Post: #1
How can I find the point on a plane that the user has clicked on? There's only one plane (the floor) in my particular example, but how hard would it be to do more?

I'm pretty sure "picking" is not what I want... and is the math required for the other method is beyond my abilities?

Advice?
Quote this message in a reply
Member
Posts: 118
Joined: 2002.08
Post: #2
I'm not sure about the question but why not send a ray straight from the cursor to the plane and check for the point of intersection.
Quote this message in a reply
Moderator
Posts: 133
Joined: 2008.05
Post: #3
Well, you could use picking. If you subdivided the floor into smaller quads, you could figure out which part of the floor was hit. Then it wouldn't be hard to do multiple planes either. You'd have each plane be made up of 20 quads, if you pick a piece 1-20 you're on plane 1, 21-41 you're on plane 2, etc. In fact, that'd probably the best way to do it.
Quote this message in a reply
Member
Posts: 153
Joined: 2004.12
Post: #4
I had to do this for my last couple of games, basicly you can either reverse engineer openGL matrix math, or use gluUnproject(), the later being MUCH easier (http://www.mevis.de/opengl/gluUnProject.html) If you set the winz parameter to 0 (for example if this was for mouse coordinates) It will return a ray from the camera through openGL space. Once you have the ray you do a little algebra. See this thread for how to find the intersection point of a ray and a plane. http://www.idevgames.com/forum/showthread.php?t=8784 The lasts two posts are what you want.

There was a long silence...
'I claim them all,' said the Savage at last.
Quote this message in a reply
Oldtimer
Posts: 834
Joined: 2002.09
Post: #5
Use gluUnproject and use in the current value for the z coordinate.
Quote this message in a reply
Disparity
Unregistered
 
Post: #6
Alright, thanks for the advice everyone. I got it to work, but it doesn't seem to be very accurate whenever the mouse is too far from the origin. The farther from the origin the more the more inaccurate the resulting point is. For example, when I click on (5,5,0) the resulting point is always further out, to say (5.5, 5.5, 0). And that gets worse as the mouse moves outwards... Any help with that?

And for some reason, using a winz of 1 worked, while 0 (and anything else for that matter) didn't. Perhaps this is part of the problem?

Here's the code:
Code:
GLdouble modelMatrix[16];
GLdouble projectMatrix[16];
GLint viewport[4];

NSPoint mouseLocation = [[self window] convertScreenToBase:[NSEvent mouseLocation]];
    
glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix);
glGetDoublev(GL_PROJECTION_MATRIX, projectMatrix);
glGetIntegerv(GL_VIEWPORT, viewport);
gluUnProject(mouseLocation.x, mouseLocation.y, 1.0, modelMatrix, projectMatrix, viewport, &objSpaceCoords[0], &objSpaceCoords[1], &objSpaceCoords[2]);
    
_vector eye;
eye.x = 5.0;
eye.y = 5.0;
eye.z = 10.0;

_vector normal;
normal.x = 0.0;
normal.y = 0.0;
normal.z = 1.0;

_vector pointOnPlane;
pointOnPlane.x = pointOnPlane.y = pointOnPlane.z = 0.0;
    
_vector ray;
ray.x = objSpaceCoords[0];
ray.y = objSpaceCoords[1];
ray.z = objSpaceCoords[2];
    
double T;
T = vectorDotProduct(normal, vectorSubtract(pointOnPlane, eye)) / vectorDotProduct(normal, ray);
pointInPlane = vectorAdd(eye, vectorScale(ray, T));
Quote this message in a reply
Member
Posts: 184
Joined: 2004.07
Post: #7
You need to use the depth value of the pixel clicked (you can read it in) as the z value. If you are using a constant z value that is the same as trying to find the world-space coordinates of a plane of that z value in view space, which I don't think is what you want.

Reading in the depth value of a pixel is not always a good idea because it can stall the rendering pipeline, so you may just want to use a geometry library to do the intersection of a ray and a plane.
Quote this message in a reply
Oldtimer
Posts: 834
Joined: 2002.09
Post: #8
You can avoid stalling the pipeline if you read the depth value just after you swap the buffers. That way you're always one frame behind with the mouse coordinate, but it shouldn't make much of a difference unless you're rewriting Duck Hunt. (Hey, I wonder if I was inspired by the above post's avatar?)
Quote this message in a reply
Member
Posts: 184
Joined: 2004.07
Post: #9
Fenris Wrote:(Hey, I wonder if I was inspired by the above post's avatar?)
Duck Hunt and OpenGL? Nintendo should make Duck Hunt 3D.

(Okay, I googled, and at least someone did)
Quote this message in a reply
Disparity
Unregistered
 
Post: #10
Well, it now works perfectly as I'm using two different depths (1 and 0) to create a ray from the camera, and then intersect it with my plane. I found some code on another thread... but I can't find the link... Annoyed
Code:
gluUnProject(mouseLocation.x, mouseLocation.y, 0.0, modelMatrix, projectMatrix, viewport, &x, &y, &z);
ray.origin.x = x;
ray.origin.y = y;
ray.origin.z = z;

gluUnProject(mouseLocation.x, mouseLocation.y, 1.0, modelMatrix, projectMatrix, viewport, &x, &y, &z);
ray.direction.x = x - ray.origin.x;
ray.direction.y = y - ray.origin.y;
ray.direction.z = z - ray.origin.z;

ray.direction = vectorNormalize(ray.direction);
hangt5, did you not have the same problem w/ only one depth?
Quote this message in a reply
Member
Posts: 153
Joined: 2004.12
Post: #11
I got the unprojected point with a winZ of 0.
I created a vector from that point to my camera position.
its a WHOLE lot easier then reading in the depth value of a pixel.

Heres some code.
Code:
    double xPos = mLocation.x;
    double yPos = mLocation.y;
    
    double zPos = 0.0f;
        
    GLdouble fx, fy, fz;
    
    gluUnProject(xPos,yPos,zPos,modelViewMatrix, projectionMatrix, viewportMatrix, &fx,  &fy, &fz);
    
    LCPoint *unProject = [LCPoint set:fx:fy:fz];
    
    ray = [unProject minus:cameraPosition];
    [ray normalize];
    
    //T = [planeNormal.(pointOnPlane - rayOrigin)]/planeNormal.rayDirection;
    //pointInPlane = rayOrigin + (rayDirection * T);

    LCPoint *p1;
    
    float dot1, dot2;
    
    LCPoint *pointOnPlane = [LCPoint set:0:0:0];
    LCPoint *rayOrigin = [LCPoint set:cameraPosition];
    LCPoint *planeNormal = [LCPoint set:0:1:0];
    LCPoint *rayDirection = [LCPoint set:ray];
    
    p1 = [pointOnPlane minus:rayOrigin];
    
    dot1 = ([planeNormal x] * [p1 x]) + ([planeNormal y] * [p1 y]) + ([planeNormal z] * [p1 z]);
    dot2 = ([planeNormal x] * [rayDirection x]) + ([planeNormal y] * [rayDirection y]) + ([planeNormal z] * [rayDirection z]);
    
    float t = dot1/dot2;
    
    [rayDirection multiply:t];
    
    LCPoint *pointInPlane = [cameraPosition plus:rayDirection];

Make sure to populate your projectionmatrix/viewportMatrix/modelViewMatrix after gluLookAt. Not before.

There was a long silence...
'I claim them all,' said the Savage at last.
Quote this message in a reply
Disparity
Unregistered
 
Post: #12
How would one find the point on a heightfield (w/ triangles)? I'm sure I'll be able to figure out the ray-triangle intersection, but how would I efficiently handle this?

Would a quadtree work here?
Quote this message in a reply
Disparity
Unregistered
 
Post: #13
Hrm... I think I'll answer my own question. I'll do a quadtree, and then do some ray-cube intersections to find out which triangles I need to check against. Then I'll only take the closer triangle (if more than one is in the path of the ray).
Quote this message in a reply
kberg
Unregistered
 
Post: #14
Use a 2D DDA algorithm:

http://www.kenmusgrave.com/grid_tracing.ps (postscript file)
Quote this message in a reply
Nibbie
Posts: 1
Joined: 2008.07
Post: #15
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  I need some recomendations: Click Adventures thomas_a 21 12,435 May 15, 2005 10:18 AM
Last Post: thomas_a