Search code examples
c++directxdirectx-9

How to draw text in a scene?


I have some code laying around which I downloaded from the internet long long ago, and I wanted to play with it.

So I extracted the interesting parts, created a proxy d3d9.dll and hooked up to a game.

Now I have a question because I cannot find the difference in the drawing between these two code pieces which determine if the vertice must be drawn on the screen or in the game scene

(I mean, like - a HUD is on screen and you always see it, and a vehicle is in the game scene, when you turn around you don't see it).

The first piece is the code I want to edit, I want to make it to display text in the game scene, not on the screen as static text.

The code which is used in the DLL is this:

float                   m_fTexCoords[224][4];

float CD3DFont::DrawLength ( const char *szText ) const
{
    float   len = 0.0f;
    float   sub = ( m_dwCreateFlags & FCR_BORDER ) ? 2.0f : 0.0f;

    for ( const char *p = szText; *p; p++ )
    {
        int c = *(unsigned char *)p - 32;
        if ( c >= 0 && c < 224 )
            len += ( (m_fTexCoords[c][2] - m_fTexCoords[c][0]) * m_texWidth - sub ) - m_chrSpacing * 2;
    }

    return len;
}

HRESULT CD3DFont::Print ( float x, float y, DWORD color, const char *szText)
{
    if ( !m_isReady )
        return E_FAIL;

    float   strWidth = DrawLength( szText );

    x -= (float)m_chrSpacing;

    if ( FAILED(this->BeginRender()) )
        return E_FAIL;

    DWORD   fvf;
    m_pD3Ddev->GetFVF( &fvf );
    m_pD3Ddev->SetFVF( D3DFVF_BITMAPFONT );
    m_pD3Ddev->SetTexture( 0, m_pD3Dtex );
    m_pD3Ddev->SetStreamSource( 0, m_pD3Dbuf, 0, sizeof(d3dvertex_s) );

    while ( *szText )
    {
        UINT        usedTriangles = 0;
        d3dvertex_s *pVertex;

        if ( FAILED(m_pD3Dbuf->Lock(0, 0, (void **) &pVertex, D3DLOCK_DISCARD)) )
        {
            m_pD3Ddev->SetFVF( fvf );
            this->EndRender();
            return E_FAIL;
        }


        for ( ; *szText; szText++ )
        {
            int c = *(unsigned char *)szText - 32;
            if ( !(c >= 0 && c < 224) )
                continue;

            float   tx1 = m_fTexCoords[c][0];
            float   tx2 = m_fTexCoords[c][2];
            float   ty1 = m_fTexCoords[c][1];
            float   ty2 = m_fTexCoords[c][3];
            float   w = ( tx2 - tx1 ) * m_texWidth;
            float   h = ( ty2 - ty1 ) * m_texHeight;

            *pVertex++ = Init2DVertex( x - 0.5f, y - 0.5f, color, tx1, ty1 );           //topleft
            *pVertex++ = Init2DVertex( x + w - 0.5f, y - 0.5f, color, tx2, ty1 );       //topright
            *pVertex++ = Init2DVertex( x - 0.5f, y + h - 0.5f, color, tx1, ty2 );       //bottomleft
            *pVertex++ = Init2DVertex( x + w - 0.5f, y - 0.5f, color, tx2, ty1 );       //topright
            *pVertex++ = Init2DVertex( x + w - 0.5f, y + h - 0.5f, color, tx2, ty2 );   //bottomright
            *pVertex++ = Init2DVertex( x - 0.5f, y + h - 0.5f, color, tx1, ty2 );       //bottomleft
            if ( m_dwCreateFlags & FCR_BORDER )
                w -= 2.0f;

            x += w - ( m_chrSpacing * 2 );

            usedTriangles += 2;
            if ( usedTriangles >= m_maxTriangles )
                break;
        }

        if ( usedTriangles > 0 )
        {
            m_pD3Dbuf->Unlock();
            m_pD3Ddev->DrawPrimitive( D3DPT_TRIANGLELIST, 0, usedTriangles );
        }
    }

    m_pD3Ddev->SetFVF( fvf );
    this->EndRender();

    return S_OK;
}

Then I saw a function named "DrawLine", so I decided to check it out and see what it does, and it draws a line in the game world, not a static line on the screen, so when I turn around and don't see the specific positions - I won't see the line:

bool CD3DRender::DrawLine ( const D3DXVECTOR3 &a, const D3DXVECTOR3 &b, DWORD dwColor )
{
    if ( FAILED(CD3DBaseRender::BeginRender()) )
        return false;

    ////////////////////////////////////////////////////
    // Make sure we have a valid vertex buffer.
    if ( m_pD3Dbuf == NULL )
    {
        return false;
    }

    m_pD3Ddev->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG2 );
    m_pD3Ddev->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 );
    //m_pD3Ddev->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
    //m_pD3Ddev->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );

    m_pD3Ddev->SetRenderState( D3DRS_CLIPPING, false );
    m_pD3Ddev->SetRenderState ( D3DRS_ZENABLE, false );
    //m_pD3Ddev->SetRenderState ( D3DRS_LIGHTING, false );
    D3DLVERTEX  lineList[2];

    //////////////////////////////////////////////////
    // Lock the vertex buffer and copy in the verts.
    m_pD3Dbuf->Lock( 0, 0, (void **) &lineList, D3DLOCK_DISCARD | D3DLOCK_NOSYSLOCK ); // flogs: D3DLOCK_NOSYSLOCK, D3DLOCK_DISCARD
    {
        lineList[0].x = a.x;
        lineList[0].y = a.y;
        lineList[0].z = a.z;
        lineList[0].color = dwColor;
        lineList[0].specular = dwColor;

        lineList[1].x = b.x;
        lineList[1].y = b.y;
        lineList[1].z = b.z;
        lineList[1].color = dwColor;
        lineList[1].specular = dwColor;
    }

    m_pD3Dbuf->Unlock();

    // store FVF to restore original at the end of this function
    DWORD       fvf;
    m_pD3Ddev->GetFVF( &fvf );
    m_pD3Ddev->SetFVF( D3DFVF_LVERTEX );
    //m_pD3Ddev->SetFVF( D3DFVF_PRIMITIVES );

    ////////////////////////////////////////////////////
    // Draw!
    m_pD3Ddev->DrawPrimitiveUP( D3DPT_LINESTRIP, 1, lineList, sizeof(lineList) / 2 );

    // reset states
    m_pD3Ddev->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
    m_pD3Ddev->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
    m_pD3Ddev->SetRenderState ( D3DRS_ZENABLE, true );
    m_pD3Ddev->SetRenderState( D3DRS_CLIPPING, true );

    // restore FVF
    m_pD3Ddev->SetFVF( fvf );

    CD3DBaseRender::EndRender();

    return true;
}

Now the question is: where is the difference? Where does this code tell:

"Hey draw the text on the player screen and let it stay there"?

and

"Hey draw this in the game world, draw only the piece the player can see"?

Can anyone point me in the right direction? Or maybe how to edit the Print function to show the text in the game world?

thanks in advance.


concise example of what I want to achieve:

What Print now does (green box) and what I want it to do (red box)

(screens taken from another game where such "3d texts" exists but the program is closed source): enter image description here enter image description here

You can see the position of the text in the red box has changed on-screen but not in-game. I want to do that too. (you can see the red and blue #'s too)

The DrawLine code already creates the line in the game, but it's only a line, not text, so how does it finally happen that the other is and the other no?


Solution

  • I can only guess what the rest of your code does. It seems that the Print method is called in an environment where the transform matrices have been set to form an orthographic view while the DrawLine method uses the game's matrices.

    However, you don't want to draw text in the scene. This would result in a misalignment and wrong scaling of the text. What you acutally want to do is getting the position on the screen where a scene position would be and draw the text there. You can use the D3DXVec3Project function for that. Be sure to provide the correct transform matrices. Not the updated ones for 2D-rendering.