Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Clicking a 3D object in-game

Status
Not open for further replies.

Supra

Programmer
Dec 6, 2000
422
US
I'm attempting to be able to "target" a 3D object in-game. For example, let's say my game is a 3D field with a moose standing in it. When I click the moose with my mouse (no pun intended), I want the game to realize that he has been selected and show "A Moose" in a part of the game's interface. I'm not worried about the interface, I just cannot figure out how to take 2-dimensional mouse coordinates and decide whether an object is clicked in a 3-dimensional world. Obviously the distance from the object greatly affects the way it is selected, because the farther you are from the object, the smaller it is, and ultimately you have a smaller "hotspot", so to speak. I attempted this myself and it went very poorly. I could turn away from the moose and click anywhere on the screen and it would detect that I'd clicked the moose. This is because of the mouse coordinates. It doesn't matter which direction you're facing, the coordinates will always be the same. Here is how I presume it could be done, but I'm not sure, so I'm asking for assistance:
Code:
'This is not a language, just easier to explain!

'This is how far the moose is
   DistanceFromMoose = Moose.Z - Me.Z;

'This is how much space is on each side of the moose
'Just to make sure I can actually see him on my screen
   BlankScreenRight = (Screen.Width - Moose.X) + Moose.Width;
   BlankScreenLeft = Screen.Width - Moose.X;

'I know these are wrong.. my head hurts :(
   HotSpotWidth = Moose.Width \ DistanceFromMoose;
   HotSpotHeight = Moose.Height \ DistanceFromMoose;
   HotSpotY = Screen.Height - DistanceFromMoose;

'Try to see if we've clicked him
   if (Me.X >= (Moose.X - BlankScreenLeft) and Me.X <= (Moose.X + BlackScreenRight)) {
      if (ClickX >= Moose.X and ClickX <= (Moose.X + HotSpotWidth) and ClickY >= HotSpotY and ClickY <= (HotSpotY + HotSpotHeight)) {
         'The moose was clicked
      }
   }
Now I know the code above is probably way off, but this is why I need your help :) Anyone?
 
Now that I'm re-reading my post, the code to make sure the moose is on the screen won't work :\
 
This is why 3D graphics is much easier when you know the math behind it :). If you're up for a challenge and really want to learn the math, write a simple ray tracer. I had to for a class assignment once, and I learned a lot from it.

Anyway, if your moose is a D3DXMesh, you can look in the Pick sample in the DX SDK and see an example of how this is done. Picking objects is so much easier in OpenGL than D3D; I don't know why they don't provide any better implementation. In OGL you could do this without too much hassle, in maybe 30 lines of code, but as you will see, it takes about 150 lines to get it to work here.
 
Indeed the pick example is the one you should look at.
I would like to add to that that the concept of the
"ray trace" is simple: you determine a line from the
camera to the point where the mouse click is (transformed
to the 3d world) and check for every triangle of the
moose if there is an intersection. However, the
implementation is complicated. You have to have
at least a notion of what object space, world space,
view space and projection space is and how the
corresponding matrices in the render pipeline transform
from one to the other. For example, see how in the Pick
function of the pick example the pick ray is determined from the mouse position:

// Get the pick ray from the mouse position
if( GetCapture() )
{
D3DXMATRIX matProj;
m_pd3dDevice->GetTransform(
D3DTS_PROJECTION, &matProj );

POINT ptCursor;
GetCursorPos( &ptCursor );
ScreenToClient( m_hWnd, &ptCursor );

// Compute the vector of the pick
// ray in screen space
D3DXVECTOR3 v;
v.x = ( ( ( 2.0f * ptCursor.x ) /
m_d3dsdBackBuffer.Width ) - 1 ) / matProj._11;
v.y = -( ( ( 2.0f * ptCursor.y ) /
m_d3dsdBackBuffer.Height ) - 1 ) / matProj._22;
v.z = 1.0f;

// Get the inverse view matrix
D3DXMATRIX matView, m;
m_pd3dDevice->GetTransform( D3DTS_VIEW, &matView );
D3DXMatrixInverse( &m, NULL, &matView );

// Transform the screen space pick ray
// into 3D space
vPickRayDir.x = v.x*m._11 + v.y*m._21 + v.z*m._31;
vPickRayDir.y = v.x*m._12 + v.y*m._22 + v.z*m._32;
vPickRayDir.z = v.x*m._13 + v.y*m._23 + v.z*m._33;
vPickRayOrig.x = m._41;
vPickRayOrig.y = m._42;
vPickRayOrig.z = m._43;
}


I use this but must say I do not fully understand it.....
Can someone clarify this? I am interested too.....

The part hereafter is more straightforward and
understandable (the checking if the ray intersected with
a triangle of the object). See the example.
 
This code takes the point where you click and creates a vector starting at the camera position and going in the direction you clicked, with the center of the screen corresponding to the vector [0, 0, 1]. It then transforms the vector into view coordinates by taking the view matrix and multiplying by the vector. After this code, the vector starts at the camera position and points in the direction clicked.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top