//----------------------------------------------------------------------------
void DxRenderer::Draw (const ProjectedTexture& rkPTexture)
{
    float fNear, fFar, fLeft, fRight, fTop, fBottom;
    D3DXMATRIX kMatrixViewCamera;
    D3DXMATRIX kMatrixInverseViewCamera;
    D3DXMATRIX kMatrixViewProjector;
    D3DXMATRIX kMatrixProjectionProjector;
    D3DXMATRIX kMatTmp1, kMatTmp2;
    D3DXMATRIX kMatrixTextureProjector;

    // If multitexturing is not supported, then we cannot render a
    // projected texture onto the objects.  But render the objects anyway.
    if ( !m_bCapMultitexture )
    {
        Renderer::Draw(rkPTexture.GetObjects());
        return;
    }


    // Reserve an available texture unit, but use one on at the end of the
    // sequence to allow room for the model if it needs to use any
    // texture units prior to the ProjectedTexture texture.  If one is
    // not available, then we cannot render a projected texture onto
    // the objects.  But render the objects anyway.
    int iUnit = RequestTextureUnit();
    if ( iUnit < 0 )
    {
        Renderer::Draw(rkPTexture.GetObjects());
        return;
    }


    // Fill the texture state object and bind it.
    Texture* pkTexture = rkPTexture.GetTexture();
    TextureState* pkTS = rkPTexture.GetTextureState();
    pkTS->Set(iUnit,pkTexture);
    SetTextureState(pkTS);
    pkTS->Remove(iUnit);


    // Setup the inverse for the current camera viewing transform. - 
    // Texture coordinates will be obtained from camera space position,
    // but that camera viewing transform needs to be undone to get
    // back to world coordinates.
    ms_hResult = m_pqDevice->GetTransform(D3DTS_VIEW,&kMatrixViewCamera); 
    WML_DX_ERROR_CHECK(GetTransform);
    D3DXMatrixInverse(&kMatrixInverseViewCamera,NULL,&kMatrixViewCamera);
    

    // Setup viewing transform for the projector.
    // Setup a lookat matrix using the projected texture camera
    // viewpoint and view direction.
    const Vector3f& rkCameraLoc = rkPTexture.GetCamera()->GetLocation();
    const Vector3f& rkCameraDir = rkPTexture.GetCamera()->GetDirection();
    const Vector3f& rkCameraUp = rkPTexture.GetCamera()->GetUp();
    const Vector3f& rkCameraLookAt = rkCameraLoc + rkCameraDir;
    D3DXVECTOR3 kProjectorEye(rkCameraLoc.X(),rkCameraLoc.Y(),
        rkCameraLoc.Z());
    D3DXVECTOR3 kProjectorUp(rkCameraUp.X(),rkCameraUp.Y(),rkCameraUp.Z());
    D3DXVECTOR3 kProjectorLookAt(rkCameraLookAt.X(),rkCameraLookAt.Y(),
        rkCameraLookAt.Z());
    D3DXMatrixLookAtRH(&kMatrixViewProjector,&kProjectorEye,&kProjectorLookAt,
        &kProjectorUp);


    // Setup the projection transform for the projector - ProjectionProjector.
    // Setup a right-handed off-center perspective projection matrix using
    // the projected texture camera frustrum.
    rkPTexture.GetCamera()->GetFrustum(fNear,fFar,fLeft,fRight,fTop,fBottom);
    D3DXMatrixPerspectiveOffCenterRH(&kMatrixProjectionProjector,fLeft,fRight,
        fBottom,fTop,fNear,fFar);


    // Bias and scale the texture so that it covers the near plane.
    // Note that integral screen coordinates represent pixel centers
    // whereas integral texture coordinates represent texel boundaries.
    // We also need to know the dimensions of the texture being projected.
    Image* pkTextureImage = pkTexture->GetImage();
    float fOffsetX = 0.5f + (0.5f / pkTextureImage->GetWidth());
    float fOffsetY = 0.5f + (0.5f / pkTextureImage->GetHeight());
    D3DXMATRIX kMatrixScaleBiasTexture(
        0.5f,0.0f,0.0f,0.0f,
        0.0f,0.5f,0.0f,0.0f,
        0.0f,0.0f,1.0f,0.0f,
        fOffsetX,fOffsetY,0.0f,1.0f);


    // Create the projected texture transform by combining the
    // following transformations in the given order (left to right):
    //  . kMatrixInverseViewCamera
    //  . kMatrixViewProjector
    //  . kMatrixProjectionProjector
    //  . kMatrixScaleBiasTexture
    D3DXMatrixMultiply(&kMatrixTextureProjector,
        D3DXMatrixMultiply(&kMatTmp1,&kMatrixInverseViewCamera,
            &kMatrixViewProjector),
        D3DXMatrixMultiply(&kMatTmp2,&kMatrixProjectionProjector,
            &kMatrixScaleBiasTexture));
    ms_hResult = m_pqDevice->SetTransform(
        D3DTRANSFORMSTATETYPE(D3DTS_TEXTURE0+iUnit),&kMatrixTextureProjector);
    WML_DX_ERROR_CHECK(SetTransform);


    // Use the texture coordinate set defined for this stage, but the
    // texture coordinates for this stage are based on the transformed
    // vertex position.
    ms_hResult = m_pqDevice->SetTextureStageState(iUnit,D3DTSS_TEXCOORDINDEX,
        D3DTSS_TCI_CAMERASPACEPOSITION|iUnit);
    WML_DX_ERROR_CHECK(SetTextureStageState);


    // All four dimenions of the texture transform are processed.
    // The coordinates are then dealt with as a projected texture.
    ms_hResult = m_pqDevice->SetTextureStageState(iUnit,
        D3DTSS_TEXTURETRANSFORMFLAGS,D3DTTFF_COUNT4|D3DTTFF_PROJECTED);
    WML_DX_ERROR_CHECK(SetTextureStageState);


    // Draw the objects as usual
    Renderer::Draw(rkPTexture.GetObjects());

    // No longer need the reserved texture unit.
    ReleaseTextureUnit(iUnit);
}