# How do I convert a pick ray's intersection into a 3D World Space (x,y,z) coordinate?

I presume that everybody is quite familiar with picking rays, generated from a x,y point from a mouse coordinate. Take a look at the DX9SDK, and check out the Pick sample. For an even better example, take a look at :
http://www.mvps.org/directx/articles/improved_ray_picking.htm

The above website has a project download (http://www.mvps.org/directx/articles/pick.zip) which you can check out. It generates a 3D ray in world space and determines if that particular ray intersects with a given mesh.

I want to know, how I can convert the 3D ray into a 3D line, with two endpoints, so that I can use DrawPrimitive() to render a line.

What I am trying to do ultimately, is get an (x,y,z) coordinate in 3D world space of the endpoint, other than the origin of the line. I want the (x,y,z) vertex exactly where the line intersects with the mesh object, in 3D space.

I'm writing a demo which renders a flat terrain/plane object, with a skinned mesh animation walking across it, following a linear path. I want to use the parameters from the D3DXIntersect() to calculate a new (x,z) coordinate (I'm not concerned about the y coordinate as there is no height yet to my terrain) to create a new destination for my character to walk to.

These are the steps I followed:
1. Subtracted the direction vector from the origin vector I passed to D3DXIntersect() to generate another point on the line.
2. Used these coordinates with a little bit of trigonometry to calculate the sharp angle, at the camera point.
3. Using the above calculated angle, I attemted to calculate the new (x(width),z(depth)) coordinate using theorem of Pythagoras and some trig.

D3DXIntersect() modifies a parameter which indicates the depth on the ray. I used this depth as the longest line in the rectangular triangle (the skew line) along with the angle of the sharp edge (closest to the eye point) to calculate the (x,z) coordinate of the right angle.

My calculations are not working perfectly, when I project the line using DrawPrimitive(), the line is only more or less right, I shouldn't be able to see the line as I'll be looking down the line from its originating point.

Here is my code for my Render() and SetMouseXY() functions, if it helps. I might be using the totally wrong approach, and if you know better, I would love to hear from you.

void CD3DApplication::SetMouseXY(CPoint point)
{
D3DXMATRIXA16 matProj;
m_pD3DDevice->GetTransform( D3DTS_PROJECTION, &matProj );

// Set mouse coordinates
m_ptMouse = CPoint(point);

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

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

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

D3DXVec3Normalize(&m_vPickRayDir,&m_vPickRayDir);

m_vPickRayOrig.x = m._41;
m_vPickRayOrig.y = m._42;
m_vPickRayOrig.z = m._43;

m_vPickRayOrig += m_vPickRayDir * m_SkinMesh.GetObjectRadius() / 64.0f;

// Transform the screen space pick ray into 3D space

// Check if ray intersects with floor.

// Convert the ray to model space
D3DXMATRIX matInv;
D3DXVECTOR3 vPickRayDir_tmp, vPickRayOrig_tmp;

D3DXMatrixInverse(&matInv, NULL, &m_FloorMesh.m_matComposite);
D3DXVec3TransformCoord(&vPickRayOrig_tmp, &m_vPickRayOrig, &matInv);
D3DXVec3TransformNormal(&vPickRayDir_tmp, &m_vPickRayDir, &matInv);

BOOL bHit=false;
DWORD dwIndex;
float fU,fV;

D3DXIntersect(m_FloorMesh.m_pMesh,&vPickRayOrig_tmp,&vPickRayDir_tmp,&bHit,&dwIndex,&fU,&fV,&m_fDist,NULL,NULL);

if(bHit)
m_bTerrainHit = true;
else
{
m_bTerrainHit = false;
m_fDist = 0.0f;
}
}

short CD3DApplication::Render()
{
HRESULT hResult;

D3DXVECTOR3 vPt2 = D3DXVECTOR3(0,0,0);

// Validate device pointer
if(m_pD3DDevice == NULL)
return RENDER_FAILED;

// Clear the backbuffer to blue
m_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);

// Retrieve elapsed time, 1st pass skips function body, only to set the time
if(m_dwPreviousTime == RENDER_FIRSTPASS)
{
m_dwPreviousTime = timeGetTime();
return RENDER_SKIPPED;
}
DWORD dwCurrentTime = timeGetTime();
FLOAT fElapsedTime = (dwCurrentTime - m_dwPreviousTime) / 1000.0f;

// Set animation track time to update frame hierarchy's transformation matrices

// Begin rendering
if(SUCCEEDED(m_pD3DDevice->BeginScene()))
{
SetupMatrices();
SetupViewMatrix();
SetupProjectionMatrix();
SetupLights();
// Output stats
// --------------------------------------------------

char cBufferX[8], cBufferY[8];
int nDec1 = 0, nSign1 = 0;
int nDec2 = 0, nSign2 = 0;
int nDec3 = 0, nSign3 = 0;
int nDec4 = 0, nSign4 = 0;

char strBuffer[32];

m_pFont->DrawText(10,10,  D3DCOLOR_ARGB(255,0,255,0), "Skinned Mesh (by Josamoto Musashi)");
m_pFont->DrawText(10,22,  D3DCOLOR_ARGB(255,0,255,0), "-------------------------------");
m_pFont->DrawText(10,46,  D3DCOLOR_ARGB(255,0,255,0), "Mouse Coordinates...");

m_pFont->DrawText(10,58,  D3DCOLOR_ARGB(255,0,255,0), CString("Mouse (x,y)         : ") +
_itoa(m_ptMouse.x, cBufferX, 10) + ", " + _itoa(m_ptMouse.y, cBufferY, 10) );

sprintf(strBuffer, _T("Ray Direction       : %3.02f, %3.02f, %3.02f"), m_vPickRayDir.x, m_vPickRayDir.y, m_vPickRayDir.z);
m_pFont->DrawText(10,72,  D3DCOLOR_ARGB(255,0,255,0), strBuffer);

sprintf(strBuffer, _T("1st Endpoint        : %3.02f, %3.02f, %3.02f"), m_vPickRayOrig.x, m_vPickRayOrig.y, m_vPickRayOrig.z);
m_pFont->DrawText(10,86,  D3DCOLOR_ARGB(255,0,255,0), strBuffer);

// Calc second end point of line from direction
vPt2 = m_vPickRayOrig + m_vPickRayDir;
sprintf(strBuffer, _T("2nd Endpoint        : %3.02f, %3.02f, %3.02f"), vPt2.x, vPt2.y, vPt2.z);
m_pFont->DrawText(10,100,  D3DCOLOR_ARGB(255,0,255,0), strBuffer);

sprintf(strBuffer, _T("Distance            : %3.02f"), m_fDist);
m_pFont->DrawText(10,114, D3DCOLOR_ARGB(255,0,255,0), strBuffer);

// Calculate the angle in relation to the x,z axis
float fT = vPt2.x - m_vPickRayOrig.x;
float fA = vPt2.z - m_vPickRayOrig.z;
float fAngleDeg = (float)(fAngleRad * 180 / D3DX_PI);

sprintf(strBuffer, _T("Angle(DEG)          : %3.02f"), fAngleDeg);
m_pFont->DrawText(10,128,  D3DCOLOR_ARGB(255,0,255,0), strBuffer);
m_pFont->DrawText(10,142,  D3DCOLOR_ARGB(255,0,255,0), strBuffer);

// Use the angle to calculate the x(width), z(depth) coordinate. y(height) will be 0.0f
float fX = (float)(m_fDist * sin(fAngleRad));
float fZ = m_vPickRayOrig.z + (float)(m_fDist * cos(fAngleRad));

D3DXVECTOR3 vPtNew = D3DXVECTOR3(fX,0.0f,fZ);
sprintf(strBuffer, _T("New Endpoint        : %3.02f, %3.02f, %3.02f"), vPtNew.x, vPtNew.y, vPtNew.z);
m_pFont->DrawText(10,154,  D3DCOLOR_ARGB(255,0,255,0), strBuffer);

if(m_bTerrainHit)
m_pFont->DrawText(10,580, D3DCOLOR_ARGB(255,0,255,0), "Terrain hit!");
else
m_pFont->DrawText(10,580, D3DCOLOR_ARGB(255,0,255,0), "Terrain not hit!");

// Vertices for rendering our 3d space reference axis
CUSTOMVERTEX vertices[] =
{
// X-Axis (Red)
{  0.0f,  0.5f,  0.0f, },
{ 25.0f,  0.5f,  0.0f, },

// Y-Axis (Green)
{  0.0f,  0.5f,  0.0f, },
{  0.0f, 30.0f,  0.0f, },

// Z-Axis (Blue)
{  0.0f,  0.5f, 45.0f, },
{  0.0f,  0.5f,  0.0f, },

// Projected ray (Yellow)
{ vPtNew.x,  vPtNew.y,  vPtNew.z, },
{ m_vPickRayOrig.x,  m_vPickRayOrig.y,  m_vPickRayOrig.z, },
};

// Create vertex buffer
if(FAILED(hResult = m_pD3DDevice->CreateVertexBuffer(8 * sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &m_pVertexBuffer, NULL)))
return 0;

// Fill the vertex buffer
VOID* pVertices;
if(FAILED(hResult = m_pVertexBuffer->Lock(0, sizeof(vertices), (void**)&pVertices, 0)))
return 0;
memcpy(pVertices, vertices, sizeof(vertices));
m_pVertexBuffer->Unlock();

// Render the 3d space reference axis
// --------------------------------------------------
D3DMATERIAL9 matRed, matGreen, matBlue, matYellow;

D3DXCOLOR colorRed(D3DCOLOR_RGBA(255,0,0,255));
D3DXCOLOR colorGreen(D3DCOLOR_RGBA(0,255,0,255));
D3DXCOLOR colorBlue(D3DCOLOR_RGBA(0,0,255,255));
D3DXCOLOR colorYellow(D3DCOLOR_RGBA(255,255,0,255));

ZeroMemory(&matRed, sizeof(D3DMATERIAL9));
matRed.Diffuse.r = matRed.Ambient.r = colorRed.r;
matRed.Diffuse.g = matRed.Ambient.g = colorRed.g;
matRed.Diffuse.b = matRed.Ambient.b = colorRed.b;
matRed.Diffuse.a = matRed.Ambient.a = colorRed.a;

ZeroMemory(&matGreen, sizeof(D3DMATERIAL9));
matGreen.Diffuse.r = matGreen.Ambient.r = colorGreen.r;
matGreen.Diffuse.g = matGreen.Ambient.g = colorGreen.g;
matGreen.Diffuse.b = matGreen.Ambient.b = colorGreen.b;
matGreen.Diffuse.a = matGreen.Ambient.a = colorGreen.a;

ZeroMemory(&matBlue, sizeof(D3DMATERIAL9));
matBlue.Diffuse.r = matBlue.Ambient.r = colorBlue.r;
matBlue.Diffuse.g = matBlue.Ambient.g = colorBlue.g;
matBlue.Diffuse.b = matBlue.Ambient.b = colorBlue.b;
matBlue.Diffuse.a = matBlue.Ambient.a = colorBlue.a;

ZeroMemory(&matYellow, sizeof(D3DMATERIAL9));
matYellow.Diffuse.r = matYellow.Ambient.r = colorYellow.r;
matYellow.Diffuse.g = matYellow.Ambient.g = colorYellow.g;
matYellow.Diffuse.b = matYellow.Ambient.b = colorYellow.b;
matYellow.Diffuse.a = matYellow.Ambient.a = colorYellow.a;

m_pD3DDevice->SetStreamSource(0, m_pVertexBuffer, 0, sizeof(CUSTOMVERTEX));
m_pD3DDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
m_pD3DDevice->SetTexture(0, NULL);

m_pD3DDevice->SetMaterial(&matRed);
m_pD3DDevice->DrawPrimitive(D3DPT_LINELIST, 0, 1);

m_pD3DDevice->SetMaterial(&matGreen);
m_pD3DDevice->DrawPrimitive(D3DPT_LINELIST, 2, 1);

m_pD3DDevice->SetMaterial(&matBlue);
m_pD3DDevice->DrawPrimitive(D3DPT_LINELIST, 4, 1);

m_pD3DDevice->SetMaterial(&matYellow);
m_pD3DDevice->DrawPrimitive(D3DPT_LINELIST, 6, 1);

D3DXMATRIX matWorld;
D3DXVECTOR3 vObjectCenter;
m_SkinMesh.GetObjectCenter(vObjectCenter);
//D3DXMatrixTranslation(&matWorld, -vObjectCenter.x, -vObjectCenter.y, -vObjectCenter.z);
D3DXMatrixTranslation(&matWorld, 0, 0, 0);

// Render the mesh object
// --------------------------------------------------
m_FloorMesh.SetupMatrices(matWorld);
m_FloorMesh.Render();
m_SkinMesh.SetupMatrices(matWorld);
m_SkinMesh.Render();

// End scene rendering
// --------------------------------------------------
m_pD3DDevice->EndScene();
}

// Present the backbuffer contents to the display
m_pD3DDevice->Present(NULL, NULL, NULL, NULL);

// Save current time for our next render
m_dwPreviousTime = dwCurrentTime;

// Return success
return RENDER_SUCCESS;
}

###### Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Commented:
I am not into directx so cant appreciate your code much but I could make out what you are after
see if this helps
http://www.siggraph.org/education/materials/HyperGraph/raytrace/rayplane_intersection.htm

Experts Exchange Solution brought to you by