Solved

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

Posted on 2003-12-01
1
7,479 Views
Last Modified: 2013-12-26
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
      m_SkinMesh.AdvanceTime(fElapsedTime);

      // 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 fAngleRad = (float)atan(fT/fA); // This is the angle in radians
            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);
            sprintf(strBuffer, _T("Angle(RAD)          : %3.02f"), fAngleRad);
            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;
}

0
Comment
Question by:josamoto
1 Comment
 
LVL 45

Accepted Solution

by:
sunnycoder earned 500 total points
ID: 9856603
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
0

Featured Post

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

Suggested Solutions

What is RenderMan: RenderMan is a not any particular piece of software. RenderMan is an industry standard, defining set of rules that any rendering software should use, to be RenderMan-compliant. Pixar's RenderMan is a flagship implementation of …
Artificial Intelligence comes in many forms, and for game developers, Path-Finding is an important ability for making an NPC (Non-Playable Character) maneuver through terrain.  A* is a particularly easy way to approach it.  I’ll start with the algor…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

22 Experts available now in Live!

Get 1:1 Help Now