Converting a ray into object space

Hello, I have been stuck on trying to convert a ray into object space, for simple collision detection, I have tried multiplying the coordinates of the ray with the objects inverse matrix ( x = x*_11 + y*_12 + z*_13, y = etc..) but that does not seem to work. There should not be any problems with detecting the collision as I am usuing the D3DXIntersect() function with this ray and the objects mesh.

The current code I am using is:

D3DXVECTOR3 vPos = pGlobal_Game->pData->objData[iID].Pos3d - pGlobal_Game->pData->objDataNo.Pos3d , vDirPos = pos3d - pGlobal_Game->pData->objDataNo.Pos3d, vDir;

iID being for the object that is doing the testing, n is for the object that is being tested against.

D3DXMatrixInverse(&inverse_m, NULL, &m);

vPos.x = vPos.x * inverse_m._11 + vPos.y * inverse_m._21 + vPos.z * inverse_m._31;
vPos.y = vPos.x * inverse_m._12 + vPos.y * inverse_m._22 + vPos.z * inverse_m._32;
vPos.z = vPos.x * inverse_m._13 + vPos.y * inverse_m._23 + vPos.z * inverse_m._33;

vDirPos.x = vDirPos.x * inverse_m._11 + vDirPos.y * inverse_m._21 + vDirPos.z * inverse_m._31;
vDirPos.y = vDirPos.x * inverse_m._12 + vDirPos.y * inverse_m._22 + vDirPos.z * inverse_m._32;
vDirPos.z = vDirPos.x * inverse_m._13 + vDirPos.y * inverse_m._23 + vDirPos.z * inverse_m._33;

vDir = vDirPos - vPos;
vPos = vPos;

fCurrentDist = GetDistBetweenVector(vPos, vDirPos);

Normalise(&vDir);

The Normalise function may be wrong.

BOOL bHit;
DWORD dwFace;
float bc_uPos, bc_vPos, fDist;

D3DXIntersect(pGlobal_Game->pData->Files[pGlobal_Game->pData->objDataNo.pObjT->iFileNo].m.pMesh, &vPos, &vDir, &bHit, &dwFace, &bc_uPos, &bc_vPos, &fDist, NULL, NULL);

if(bHit)
{
if(fDist < fMoveDist)
{
pGlobal_Game->pData->objData[iID].Pos3d = pGlobal_Game->pData->objDataNo.Pos3d - D3DXVECTOR3(0.0,-200.0f,0.f);
return true;

}

}

This code does not work in detecting the objects mesh at all, I have tried manyy other methods for making it work but they dont work either. However the code does work against the objects when they have not been rotated, which would mean that my rotation of the ray is wrong, what do I need to do to the ray co-ordinates to rotate it in the opposite manner as to the object im testing against

Any Help is appreaciated, thanks.

Ryan.



Answer this question

Converting a ray into object space

  • carl64

    Thanks, I did try that before, but must of not implemented it correctly.

    After looking at the pick example I am still unsure of where it is that the ray is comverted into object space, as I can only see it converted to the inverse of the view matrix:

    // 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
    D3DXMATRIXA16 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;

    This is all the code I can see that is used for transforming the ray, is this what converts it to object space, as I would of thought that it would need to use the objects mesh, the ray direction and ray origin then are passed to the D3DXIntersect function.

    Note: I am currently using an old version of the directx 9 SDK ( around 2002, bad internet connection doesnt really allow for an update), that could be a possible problem.

    Thanks again for any help given.

    Ryan.


  • chsk

    Thanks for your reply, so your saying that I have to transform the ray and then transform it again by the inverse of the meshes matrix. The problem I have with that is that what do I transform the ray with in the first transformation

    Do I have to transform it just like in the pick example with the inverse view, setting the position as the inverse of the view matrix, and multiplying the direction with the inverse view

    Or do I have to do the same calculations but instead of with the view matrix, but with the (inverse) meshes matrix (where both the rotations and world transformations are stored)

    Or do I have to use a different Matrix Or the meshes rotation matrix

    Or am I just missing something all together

    To clarify my main question, I would like to know what transformations would have to be performed on the ray before transforming it with the meshes world matrix.

    Thanks for any replies.

    Ryan.


  • data_art

    Your mouse point x,y is in windows screen coordinates 0<x<width, 0<y<height

    Convert this to screen space -1<x'<1, -1<y'<1using x' = (x/(width/2))-1 (I think you have to flip Y as this point since 0,0 is bottom left in screen space)

    Then construct a screen space ray (x', y', 0) - (x',y',1) which is a 3d ray going into the screen at that point.

    Since

    ObjectSpace * WorldTransform = WorldSpace
    WorldSpace * ViewTransform = CameraSpace
    CameraSpace * ProjectionTransform = ScreenSpace

    Then you convert back by dividing byt the correct transforms, or multiplying by the inverse.

    So if you want object space coordiantes to do a mesh.intersect then

    objectray = screenray * projectionInverse * ViewInverse * worldInverse



  • merwinp

    The example from a book I'm going through at the moment is kind of like this:


    // Get the project, view, and inversed view matrices
    m_Device->GetTransform(D3DTS_PROJECTION, &matProj);
    m_Device->GetTransform(D3DTS_VIEW, &matView);
    D3DXMatrixInverse(&matInv, NULL, &matView);

    // Compute the vector of the pick ray in screen space
    m_Device->GetViewport(&vp);
    vecTemp.x = (((2.0f * XPos) / vp.Width) - 1.0f) / matProj._11;
    vecTemp.y = -(((2.0f * YPos) / vp.Height) - 1.0f) / matProj._22;
    // vecTemp.y = -(((2.0f * YPos) / vp.Height) + 1.0f) / matProj._22;
    vecTemp.z = 1.0f;

    // Transform the screen space ray
    vecRay.x = matInv._41;
    vecRay.y = matInv._42;
    vecRay.z = matInv._43;
    vecDir.x = vecTemp.x * matInv._11 +
    vecTemp.y * matInv._21 +
    vecTemp.z * matInv._31;
    vecDir.y = vecTemp.x * matInv._12 +
    vecTemp.y * matInv._22 +
    vecTemp.z * matInv._32;
    vecDir.z = vecTemp.x * matInv._13 +
    vecTemp.y * matInv._23 +
    vecTemp.z * matInv._33;

    // Transform ray and direction by object's world transformation matrix
    D3DXMatrixInverse(&matInv, NULL, m_Mesh->GetMatrix());
    D3DXVec3TransformCoord(&vecMeshRay, &vecRay, &matInv);
    D3DXVec3TransformNormal(&vecMeshDir, &vecDir, &matInv);
    // D3DXVec3Normalize(&vecMeshDir, &vecMeshDir);

    // Check for intersection
    D3DXIntersect(m_Mesh->GetMesh(), &vecMeshRay, &vecMeshDir, &Hit, &FaceIndex, &u, &v, &Dist, NULL, NULL);


  • PeterZ

    Thanks for your replies, I have been able to come up with some code that actually does work to some degree (it does detect the collision all the time, just some areas near the mesh, the collision is a bit off), but has a few errors within it, I will get back to you if I have any difficulties with finishing it off.

    Thanks again.

    Ryan


  • IanTaite

    Compare your code to the 'pick' example in the SDK, that does exactly what you are tryng to do.

  • gregaug

    From what I gather, after you've transformed the ray you then need to transform the ray and its direction by the inverse of the mesh's world transformation matrix:

    // Transform ray and direction by object's world transformation matrix
    D3DXMatrixInverse(&matInv, NULL, matMeshWorld);
    D3DXVec3TransformCoord(&vecMeshRay, &vecRay, &matInv);
    D3DXVec3TransformNormal(&vecMeshDir, &vecDir, &matInv);

    // Check for intersection
    D3DXIntersect(m_Mesh, &vecMeshRay, &vecMeshDir, &Hit, &FaceIndex, &u, &v, &Dist, NULL, NULL);


  • Converting a ray into object space