Picking in Dx

Started by
6 comments, last by ganbree 15 years, 8 months ago
I have asked, looked up, tried this problem way too many times. I have followed this site: Here as well as I have googled countless number of articles, ideas on how to do it. I cannot for the life of me figure out what's going on. Here is my code after copying/updating the functions used (I could not find anything on the d3dmath_ functions).

POINT mouseCoords;
	GetCursorPos(&mouseCoords);

	float dx = TANGENT_OF_FOV * ((mouseCoords.x / WIDTH_DIV_2 - 1.0f) / ASPECT);
	float dy = TANGENT_OF_FOV * (1.0f - mouseCoords.y / HEIGHT_DIV_2);

	D3DXMATRIX invMatrix, viewMatrix;
	
	renderer.d3ddev->GetTransform(D3DTS_VIEW, &viewMatrix);
	D3DXMatrixInverse(&invMatrix, NULL, &viewMatrix);

	D3DXVECTOR3 p1;
	p1.x = dx * NEAR_VIEW_PLANE;
	p1.y = dy * NEAR_VIEW_PLANE;
	p1.z = NEAR_VIEW_PLANE;

	D3DXVECTOR3 p2;
	p2.x = dx * FAR_VIEW_PLANE;
	p2.y = dy * FAR_VIEW_PLANE;
	p2.z = FAR_VIEW_PLANE;

	D3DXVECTOR4 pOut1;
	D3DXVECTOR4 pOut2;

	D3DXVec3Transform(&pOut1, &p1, &invMatrix);
	D3DXVec3Transform(&pOut2, &p2, &invMatrix);

However, both pOut1 and pOut2 dont have any values even close to what I want - or I don't know how to use them to do what I want. I want to be able to test an intersection with 2 triangles (i.e. a square) to see if a mouse click/right click has clicked on a particular tile. Notes: when I first started this, simply doing:

float dx = ((mouseCoords.x / WIDTH_DIV_2 - 1.0f) / ASPECT);
float dy = (1.0f - mouseCoords.y / HEIGHT_DIV_2);
i.e., the first step on the site, would set a my dy between -1 and 1 and my dx between -.7something and .7something which I think is bad. The site says it should be between -1 and 1 for both. Perhaps that is my issue, but I cannot figure out what I am doing wrong =( I wasn't sure if that was to be expected or not, so I continued on and needless to say, it didn't go as expected. Any help would be appreciated - this is incredibly frusterating. (As a side note, how I have been doing clicking is to go through every visible tile/position and project that to screen coordinates and test the mouse coords to be within x/y of the screen coord. this works, but that seems incredibly inefficient and it is also not nearly as precise and does not give me the versatility I would like) Thanks!
Advertisement
Ok,

Picking is a used to select and perform mouse related input on 3D objects.

To do picking first you get calculate a ray that goes out from your camera through every point that would show on the screen as on that pixel. (I've just made this very confusing.)

You then check if this ray intersects with anything in your scene, then you take the closest of these objects and that is what the hover is hovering over.

If you are using meshes then the D3DXIntersect function with perform the intersection test for you.

POINT mouseCoords;	GetCursorPos(&mouseCoords);	float dx = TANGENT_OF_FOV * ((mouseCoords.x / WIDTH_DIV_2 - 1.0f) / ASPECT);	float dy = TANGENT_OF_FOV * (1.0f - mouseCoords.y / HEIGHT_DIV_2);	D3DXMATRIX invMatrix, viewMatrix;		renderer.d3ddev->GetTransform(D3DTS_VIEW, &viewMatrix);	D3DXMatrixInverse(&invMatrix, NULL, &viewMatrix);	D3DXVECTOR3 p1;	p1.x = dx * NEAR_VIEW_PLANE;	p1.y = dy * NEAR_VIEW_PLANE;	p1.z = NEAR_VIEW_PLANE;	D3DXVECTOR3 p2;	p2.x = 0;	p2.y = 0;	p2.z = FAR_VIEW_PLANE - FAR_VIEW_PLANE;	D3DXVECTOR4 pOut1;	D3DXVECTOR4 pOut2;	D3DXVec3Transform(&pOut1, &p1, &invMatrix);	D3DXVec3Transform(&pOut2, &p2, &invMatrix);


I've changed the code slightly so that it p2 is the direction of the ray rather than the end point. Because that's what you need for the Intersect function. Just perform a frustrum cull on all the objects before you try to test them for being picked, else objects beyong the far view plane might be picked.

It seems I'm rambling, I hope this is useful.

http://msdn.microsoft.com/en-us/library/bb172882(VS.85).aspx
Quote:Original post by ganbree
If you are using meshes then the D3DXIntersect function with perform the intersection test for you.


There's also D3DXIntersectTri if you are just testing against triangles. You just pass it the three vertices so it's pretty flexible. You just need to be sure you've transformed the ray into the same frame of reference as the triangles, but you seem to have that bit covered.

Regarding converting a screen coord into a value between -1 to 1 for each axis, I would think that the easiest way is to convert the screen coord first into a value between 0-2 the subtract 1 from it?

So if your screen is 1024 across, X pos 512 could be converted like:

512/1024=0.50.5*2=11-1=0


Zero would obviously be

0/1024=00*2=00-1=-1


and 1024 would be

1024/1024=11*2=22-1=1


Unless my brain is not working properly, that should return the correct value for any screen coordinate between 0 and MAX, and the same can be easily applied to the Y by substituting 1024 for 768 (for example) then multiplying the result by -1 to invert the axis.

I'm sure there are more efficient or simpler ways to do this though.
double post, sorry, see below
Thank you for the posts.

First: So about converting mouse to between -1 and 1, both axis SHOULD have 1/-1 as their limit?

Second: Ganbree - is that all I need to use IntersectTri(which I'll be using)? I ask because I need to convert pOut1/2 BACK to D3DXVector3's because thats what it uses. Am I right to use w,x,y and ignore z when converting or..?

Also, as I have it now (which basically assigns the w,x,y coordinates to a d3dxvector3 so I can use IntersectTri), it does not work, or at least I am not calling this correctly. I tried passing a very large triangle to this and it doesnt work when I click seemingly in the right triangle area.

float dx = TANGENT_OF_FOV * ((mouseCoords.x / WIDTH_DIV_2 - 1.0f) / ASPECT);	float dy = TANGENT_OF_FOV * (1.0f - mouseCoords.y / HEIGHT_DIV_2);	D3DXMATRIX invMatrix, viewMatrix;		renderer.d3ddev->GetTransform(D3DTS_VIEW, &viewMatrix);	D3DXMatrixInverse(&invMatrix, NULL, &viewMatrix);	D3DXVECTOR3 p1;	p1.x = dx * NEAR_VIEW_PLANE;	p1.y = dy * NEAR_VIEW_PLANE;	p1.z = NEAR_VIEW_PLANE;	D3DXVECTOR3 p2;	p2.x = 0;	p2.y = 0;	p2.z = FAR_VIEW_PLANE - FAR_VIEW_PLANE;	D3DXVECTOR4 pOut1;	D3DXVECTOR4 pOut2;	D3DXVec3Transform(&pOut1, &p1, &invMatrix);	D3DXVec3Transform(&pOut2, &p2, &invMatrix);	D3DXVECTOR3 pVect(pOut1.w, pOut1.x, pOut1.y);;	D3DXVECTOR3 pDir(pOut2.w, pOut2.x, pOut2.y);	bool pass = false;	D3DXVECTOR3 t1(0.0f, 0.0f, 0.0f);	D3DXVECTOR3 t2(-40.0f, 0.0f, 0.0f);	D3DXVECTOR3 t3(-20.0f, 0.0f, 50.0f);	float bU, bV, fDist;	pass = D3DXIntersectTri(&t1, &t2, &t3, &pVect, &pDir, &bU, &bV, &fDist);	if(pass)	{		renderer.text.addChat("yes!", "SUCCESS!!!");	}
I've not ever used this, but the following is how Frank Luna computes a picking ray based on a mouse coordinate in Introduction to 3D Game Programming:

struct Ray { D3DXVECTOR3 origin,direction; };Ray Calc(int X,int Y) // screen coords{    float px=0,py=0;    D3DVIEWPORT9 vp; Device->GetViewport(&vp);    D3DXMATRIX proj;    Device->GetTransform(D3DTS_PROJECTION,&proj);    px=(((2.0f*x)/vp.Width)-1.0f)/proj(0,0);    py=(((-2.0f*y)/vp.Height)+1.0f)/proj(1,1);    Ray ray;    ray.origin=D3DXVECTOR3(0,0,0);    ray.direction=D3DXVECTOR3(px,py,1.0f);    return ray;}


He then goes on to transform the ray by the inverse of the world matrix of the object to intersect-test (TransformCoord for origin and TransformNormal for direction).

At that point, I guess the origin and direction can be plugged into D3DXIntersectTri okay.
I am not quite sure the what the divide by proj(0,0) and (1,1) is - I get the vp.width/height as the screen width/height.

I am sorry if I am such a beginner with this stuff, I avoided picking for so long :(


Edit: Alright, not sure why it does it, but I assumed that:

 float px = (((2.0f * mouseCoords.x) / vp.Width) - 1.0f) / proj(0,0); float py = (((-2.0f * mouseCoords.y) / vp.Height) + 1.0f) / proj(1,1);andfloat dx = TANGENT_OF_FOV * ((mouseCoords.x / WIDTH_DIV_2 - 1.0f) / ASPECT);float dy = TANGENT_OF_FOV * (1.0f - mouseCoords.y / HEIGHT_DIV_2);


should give the same results. dy == py, but dx != px and I am not sure why. The 2nd one comes from the web site listed on first post, while the 1st is what EasilyConfused posted. Hm =/ This is driving me insane!

[Edited by - Crazyfool on July 30, 2008 7:25:27 PM]
The resource I used when I first used picking was this. It was helpful enougth to me that I bookmarked it! Something I don't do enougth. Reading it the entire way through should allow you to glean enougth information to be able to implement it properly.

Hope this helps.

This topic is closed to new replies.

Advertisement