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?

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.
LongJumper
Unregistered

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.
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.
Oldtimer
Posts: 832
Joined: 2002.09
Post: #5
Use gluUnproject and use in the current value for the z coordinate.
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));```
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.
Oldtimer
Posts: 832
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?)
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)
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...
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?
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.
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?

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).
kberg
Unregistered

Post: #14
Use a 2D DDA algorithm:

http://www.kenmusgrave.com/grid_tracing.ps (postscript file)
Nibbie
Posts: 1
Joined: 2008.07
Post: #15