The ol' 2D click -> 3D world coordinate question

Started by
31 comments, last by ilkhan_v4 22 years ago
I searched the archives for this problem and didn''t find a satisfactory answer. I''m basically trying to find out where the player clicked on "terrain" in an RTS no matter what the position of the camera is (I don''t care about rotation much, though, just scaling with zoom and pan). I''m using DirectX 8 and Visual Basic 6.0. One person suggested using D3DXVec3Unproject to get the points on the near and far planes, and then use D3DXPlaneIntersectLine (using a plane based on the "terrain") to find where the point would be at the terrain. Great. Except... in VB D3DXPlaneIntersectLine returns another plane, not a vector. I''m having trouble figuring out what the a, b, c, and d in the plane type mean, or if there''s another, more elegant way of doing what I want to do. Any help? Again, forgive me since this has been asked before. This is my first post here.
Advertisement
In the DX8 SDK there is a D3D sample app that can identify the triangle of a 3D object (A tiger in this case) using the position of the cursor, maybe this would help?

  Downloads:  ZeroOne Realm

  Downloads:  ZeroOne Realm

The a, b, c, and d components of the Plane are the coefficients used to define the plane in the plane equation. You can pretty much ignore them for now. Instead, use D3DXPlaneFromPoints() to create a plane out of a triangle. Then, since, as you said, the VB version of D3DPlaneIntersectLine() returns a plane (o.O), you will need to check around the net to find the equation for a ray-plane intersection. You should be able to figure it out from there.

Z,
______________"Evil is Loud"
Thanks, I''ll look for the ray-plane intersection equation.

Like I said, I''m not really looking at this for picking a triangle or mesh, but finding where a unit in my game should move to. I''ll post any solution I find for anyone else interested.
One way that can be made to be fairly robust:

When the user clicks:

Encode the X, Y, Z positions of the terrain as color (R = X, Y = G, B = Z) and draw the terrain (but don't present it onscreen!)

Now, look at the resulting picture (you could either lock the back buffer or render into a texture) and find the value of RGB at the point where the mouse is.

Voila! you have the position. (the render also interpolates...) Now, clear the back buffer and continue!

Depending on the amount of precision you need, you may have to get more clever about how you pack the colors, but this is a nice place to start. One of the problems with vector methods is that one tall mountain could really screw you up. Unless you check all the triangles, clicking on a mountain could seem like a click on terrain behind it. It becomes a question of whether it's faster to render and check or loop through all the geometry. In some cases, you could try to render just that one pixel to speed things up (I've done this in OpenGL, but not DX8). Remember to turn off lighting, textures, etc. This can be pretty zippy if done right.
Also, it can make for a good general solution draw the whole scene with all objects:

R = 0 for terrain and 1-255 for vehicles
G = X for terrain, 0 for vehicles
B = Y for terrain, 0 for vehicles

(clever packing could yield better results)

Render the scene and check the value. If red is not 0, then you selected vehicle[R], if it is 0, then read the position for G and B and index that into your height map to get Z.

Vertex shaders also open up many possibilities...


Edited by - CrazedGenius on September 8, 2001 6:57:06 PM
Author, "Real Time Rendering Tricks and Techniques in DirectX", "Focus on Curves and Surfaces", A third book on advanced lighting and materials
That''s an interesting idea, CrazedGenius, but if you want more resolution than 256x256, you''ll need to step up to more than 24-bits of color (which I think you mentioned). Like I said, a mountain wouldn''t obscure the clicking too much since I don''t do much rotation. It''s a consideration, though.

Being that I expect around 200 units to be in play at once, memory is at more of a premium than processor cycles, so finding a quicker solution isn''t as important as finding one that can work with the data I already have (a mesh).

I appreciate everyone''s comments so far. It''s rare to have people come up with productive answers instead of just telling me to use C++ and buzz off. Still, other than the offscreen buffer method, I haven''t been able to come up with a more robust way of doing this. Most of what I''ve found on ray-plane intersection seems redundant with using the D3DX function. Why the $@!~@$#^ does it have to return a plane??
First thing, take a look at Magic Software''s free code. In there you''ll find routines for ray/bounding box intersections as well as many, many other useful functions.

If you want to pick entire objects in a 3d scene, rather than individual polygons, stick to using bounding boxes.

Secondly, you need convert the mouse click into a 3d ray, I''m not going to try and explain the theory behind that, but there is sample code in the DX SDK.

Here''s a cut n'' paste from my project code that shows how simple the whole process is. You won''t be able to use it directly but it should make for some interesting reading..

  ///////////////////////////////////////////////////////////////////////////////// CheckModelSelect()//						See if the user has clicked any of the on-screen models//						by testing if the click was inside any of the models//						bounding boxes//unsigned int CMouseView::CheckModelSelect(float x, float y, CMattsDirect3D *D3D){	D3DMATRIX matProj;	D3D->lpDevice->GetTransform( D3DTRANSFORMSTATE_PROJECTION, &matProj );    // Compute the vector of the pick ray in screen space	Mgc::Vector3 v;	v[0] =  ( ( ( 2.0f * x ) / m_fScWidth  ) - 1 ) / matProj._11;	v[1] =  -( ( ( 2.0f * y ) / m_fScHeight ) - 1 ) / matProj._22;	v[2] =  1.0f;	// Transform the screen space pick ray into 3D space	m_cPickRay.Direction() = Mgc::Vector3(			v[0]*m_cInverseViewMatrix._11 + v[1]*m_cInverseViewMatrix._21 + 			v[2]*m_cInverseViewMatrix._31,			v[0]*m_cInverseViewMatrix._12 + v[1]*m_cInverseViewMatrix._22 + 			v[2]*m_cInverseViewMatrix._32,			v[0]*m_cInverseViewMatrix._13 + v[1]*m_cInverseViewMatrix._23 + 			v[2]*m_cInverseViewMatrix._33);	m_cPickRay.Origin() = Mgc::Vector3(	m_cInverseViewMatrix._41, 										m_cInverseViewMatrix._42, 										m_cInverseViewMatrix._43);	// cycle through all the models..	for (unsigned int t = 0; t < m_uiNumModels; t++)	{		ComputeSelectedBoundingBox( t );		// TO DO:  Check against all bounding boxes, and return the first timed intersection		if( Mgc::TestIntersection( m_cPickRay, m_cSelectedBox ) )			return t;	}	return MV_NONSELECTED;}  


The variables should be self explanatory either by their names or their use, sorry I don''t have the time to explain it more thoroughly.

Just take advantage of that Magic Software source code. If you have any trouble using it in your project let me know.

Cheers

Matt




Just something to think about.... using bounding boxes around irregularly shaped objects could create issues. Imagine many units crowded together. If the boxes overlap, which one are you clicking on?? The color approach always works because it "sees" exactly what the user sees. Using the full 24 bits for units let''s you manage ALOT of units...
Author, "Real Time Rendering Tricks and Techniques in DirectX", "Focus on Curves and Surfaces", A third book on advanced lighting and materials
hm.. raytracing for intersection knows this, too.. it returns a t-value, and the lowest one is the object you clicked on..

and dont render your scene 2 times.. doing it offline is much faster..

we wanna play, not watch the pictures

If that's not the help you're after then you're going to have to explain the problem better than what you have. - joanusdmentia

My Page davepermen.net | My Music on Bandcamp and on Soundcloud

Well, I still haven''t figured out how to do this seemingly simple problem. I have been messing with the plane that D3DX returns and if I correlate a and b to x and y, it does seem to be the right direction, but the wrong distance. It always comes up short and I have no idea why. I''m probably just missing the bigger picture here, since my 3D math isn''t as good as it should be for doing game programming. I did fail Calc II twice. Badly. Although that shouldn''t matter unless the solution lies in me integrating something. But anyway...

Correct me if I''m wrong, but the intersection between a line and a plane is a point, right? I''m still wondering why it''s returning a plane. 3dModelMan, thanks for the code, but I''m more interested in WHERE the mouse was clicked, not IF it intersects something. The site you gave was very helpful, but it seems to be centered around using the guy''s game engine. Still very useful, though, thanks.

This topic is closed to new replies.

Advertisement