예제 #1
0
//--------------------------------------------------------------------------------------
// Name: CopyTexture()
// Desc: Copy the src texture to the dst texture. The scale can (and should) be changed.
//--------------------------------------------------------------------------------------
VOID PostProcess::CopyTexture( LPDIRECT3DTEXTURE9 pSrcTexture,
                               LPDIRECT3DTEXTURE9 pDstTexture,
                               LPDIRECT3DPIXELSHADER9 pPixelShader,
                               DWORD dwEdramOffset )
{
    if( NULL == pPixelShader )
        pPixelShader = m_pCopyTexturePS;

    // Make sure that the required shaders and objects exist
    assert( pPixelShader );
    assert( pSrcTexture && pDstTexture );

    XGTEXTURE_DESC SrcDesc;
    XGGetTextureDesc( pSrcTexture, 0, &SrcDesc );

    // Create and set a render target
    D3DSURFACE_PARAMETERS surfaceParams =
    {
        0
    };
    surfaceParams.Base = dwEdramOffset;
    PushRenderTarget( 0L, CreateRenderTarget( pDstTexture, &surfaceParams ) );

    // Scale and copy the src texture
    g_pd3dDevice->SetPixelShader( pPixelShader );

    g_pd3dDevice->SetTexture( 0, pSrcTexture );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_POINT );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );

    // Draw a fullscreen quad to sample the RT
    DrawFullScreenQuad();

    XGTEXTURE_DESC DstDesc;
    XGGetTextureDesc( pDstTexture, 0, &DstDesc );
    DWORD ColorExpBias = 0;

    if( DstDesc.Format == D3DFMT_G16R16_SIGNED_INTEGER )            ColorExpBias = ( DWORD )
            D3DRESOLVE_EXPONENTBIAS( 10 );
    else if( DstDesc.Format == D3DFMT_A16B16G16R16_SIGNED_INTEGER ) ColorExpBias = ( DWORD )
            D3DRESOLVE_EXPONENTBIAS( 10 );

    g_pd3dDevice->Resolve( D3DRESOLVE_RENDERTARGET0 | ColorExpBias, NULL, pDstTexture, NULL,
                           0, 0, NULL, 1.0f, 0L, NULL );

    // Cleanup and return
    PopRenderTarget( 0L )->Release();
    g_pd3dDevice->SetPixelShader( NULL );
}
예제 #2
0
//--------------------------------------------------------------------------------------
// Name: Downsample4x4Texture()
// Desc: Scale down pSrcTexture by 1/4 x 1/4 and place the result in pDstTexture
//--------------------------------------------------------------------------------------
VOID PostProcess::Downsample4x4Texture( LPDIRECT3DTEXTURE9 pSrcTexture,
                                        LPDIRECT3DTEXTURE9 pDstTexture )
{
    // Make sure that the required shaders and objects exist
    assert( m_pDownScale4x4PS );
    assert( pSrcTexture && pDstTexture );

    XGTEXTURE_DESC SrcDesc;
    XGGetTextureDesc( pSrcTexture, 0, &SrcDesc );

    // Create and set a render target
    PushRenderTarget( 0L, CreateRenderTarget( pDstTexture ) );

    // Get the sample offsets used within the pixel shader
    XMVECTOR avSampleOffsets[MAX_SAMPLES];
    GetSampleOffsets_DownScale4x4( SrcDesc.Width, SrcDesc.Height, avSampleOffsets );
    g_pd3dDevice->SetPixelShaderConstantF( PSCONST_avSampleOffsets, ( FLOAT* )avSampleOffsets, MAX_SAMPLES );
    g_pd3dDevice->SetPixelShader( m_pDownScale4x4PS );

    g_pd3dDevice->SetTexture( 0, pSrcTexture );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );

    // Draw a fullscreen quad
    DrawFullScreenQuad();

    g_pd3dDevice->Resolve( D3DRESOLVE_RENDERTARGET0, NULL, pDstTexture, NULL,
                           0, 0, NULL, 1.0f, 0L, NULL );

    // Cleanup and return
    PopRenderTarget( 0L )->Release();
    g_pd3dDevice->SetPixelShader( NULL );
}
예제 #3
0
//--------------------------------------------------------------------------------------
// Name: SampleLuminance()
// Desc: Measure the average log luminance in the scene.
//--------------------------------------------------------------------------------------
VOID PostProcess::SampleLuminance( LPDIRECT3DTEXTURE9 pSrcTexture, BOOL bInitial,
                                   LPDIRECT3DTEXTURE9 pDstTexture )
{
    // Make sure that the required shaders and objects exist
    assert( m_pSampleLumInitialPS );
    assert( m_pSampleLumFinalPS );
    assert( pSrcTexture && pDstTexture );

    XGTEXTURE_DESC SrcDesc;
    XGGetTextureDesc( pSrcTexture, 0, &SrcDesc );

    // Create and set a render target
    PushRenderTarget( 0L, CreateRenderTarget( pDstTexture ) );

    // Sample initial luminance
    if( bInitial )
    {
        // Initialize the sample offsets for the initial luminance pass.
        XMVECTOR avSampleOffsets[MAX_SAMPLES];
        GetSampleOffsets_DownScale3x3( SrcDesc.Width, SrcDesc.Height, avSampleOffsets );
        g_pd3dDevice->SetPixelShaderConstantF( PSCONST_avSampleOffsets, ( FLOAT* )avSampleOffsets, MAX_SAMPLES );
        g_pd3dDevice->SetPixelShader( m_pSampleLumInitialPS );

        g_pd3dDevice->SetTexture( 0, pSrcTexture );
        g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
        g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
        g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
        g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
    }
    else // ( bIntial == FALSE )
    {
        // Perform the final pass of the average luminance calculation. This pass
        // performs an exp() operation to return a single texel cooresponding to the
        // average luminance of the scene in m_pToneMapTexture.

        XMVECTOR avSampleOffsets[MAX_SAMPLES];
        GetSampleOffsets_DownScale4x4( SrcDesc.Width, SrcDesc.Height, avSampleOffsets );
        g_pd3dDevice->SetPixelShaderConstantF( PSCONST_avSampleOffsets, ( FLOAT* )avSampleOffsets, MAX_SAMPLES );
        g_pd3dDevice->SetPixelShader( m_pSampleLumFinalPS );

        g_pd3dDevice->SetTexture( 0, pSrcTexture );
        g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT );
        g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT );
        g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
        g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );
    }

    // Draw a fullscreen quad to sample the RT
    DrawFullScreenQuad();

    g_pd3dDevice->Resolve( D3DRESOLVE_RENDERTARGET0, NULL, pDstTexture, NULL,
                           0, 0, NULL, 1.0f, 0L, NULL );

    // Cleanup and return
    PopRenderTarget( 0L )->Release();
    g_pd3dDevice->SetPixelShader( NULL );
}
예제 #4
0
static void xdk_convert_texture_to_as16_srgb( D3DTexture *pTexture )
{
   pTexture->Format.SignX = GPUSIGN_GAMMA;
   pTexture->Format.SignY = GPUSIGN_GAMMA;
   pTexture->Format.SignZ = GPUSIGN_GAMMA;

   XGTEXTURE_DESC desc;
   XGGetTextureDesc( pTexture, 0, &desc );

   //convert to AS_16_16_16_16 format
   pTexture->Format.DataFormat = g_MapLinearToSrgbGpuFormat[ (desc.Format & D3DFORMAT_TEXTUREFORMAT_MASK) >> D3DFORMAT_TEXTUREFORMAT_SHIFT ];
}
예제 #5
0
//--------------------------------------------------------------------------------------
// Name: BuildMipMaps()
// Desc: Generate mip maps from the base texture
//--------------------------------------------------------------------------------------
VOID PostProcess::BuildMipMaps( LPDIRECT3DTEXTURE9 pTexture )
{
    // Make sure that the required shaders and objects exist
    assert( m_pCopyTexturePS );
    assert( pTexture );

    DWORD dwNumMipLevels = pTexture->GetLevelCount();

    // Create and set a render target
    PushRenderTarget( 0L, CreateRenderTarget( pTexture ) );

    // Scale and copy the src texture
    g_pd3dDevice->SetPixelShader( m_pCopyTexturePS );

    g_pd3dDevice->SetTexture( 0, pTexture );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_POINT );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );

    D3DVIEWPORT9 vp;
    g_pd3dDevice->GetViewport( &vp );

    for( DWORD i = 1; i < dwNumMipLevels; i++ )
    {
        XGTEXTURE_DESC Desc;
        XGGetTextureDesc( pTexture, i, &Desc );
        vp.Width = Desc.Width;
        vp.Height = Desc.Height;
        g_pd3dDevice->SetViewport( &vp );
        g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINMIPLEVEL, i - 1 );

        // Draw a fullscreen quad to sample the RT
        DrawFullScreenQuad();

        DWORD ColorExpBias = 0;

        if( Desc.Format == D3DFMT_G16R16_SIGNED_INTEGER )            ColorExpBias = (DWORD) D3DRESOLVE_EXPONENTBIAS(10);
        else if( Desc.Format == D3DFMT_A16B16G16R16_SIGNED_INTEGER ) ColorExpBias = (DWORD) D3DRESOLVE_EXPONENTBIAS(10);

        g_pd3dDevice->Resolve( D3DRESOLVE_RENDERTARGET0 | ColorExpBias, NULL, pTexture, NULL,
                               i, 0, NULL, 1.0f, 0L, NULL );
    }

    // Cleanup and return
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINMIPLEVEL, 13 );
    PopRenderTarget( 0L )->Release();
    g_pd3dDevice->SetPixelShader( NULL );
}
예제 #6
0
//--------------------------------------------------------------------------------------
// Name: BloomTexture()
// Desc: Bloom the pSrcTexture and place the result in pDstTexture
//--------------------------------------------------------------------------------------
VOID PostProcess::BloomTexture( LPDIRECT3DTEXTURE9 pSrcTexture,
                                BOOL bBloomAcrossWidth,
                                LPDIRECT3DTEXTURE9 pDstTexture,
                                FLOAT fSize, FLOAT fBrightness )
{
    // Make sure that the required shaders and objects exist
    assert( m_pBloomPS );
    assert( pSrcTexture && pDstTexture );

    XGTEXTURE_DESC SrcDesc;
    XGGetTextureDesc( pSrcTexture, 0, &SrcDesc );

    // Create and set a render target
    PushRenderTarget( 0L, CreateRenderTarget( pDstTexture ) );

    XMVECTOR avSampleOffsets[MAX_SAMPLES];
    XMVECTOR avSampleWeights[MAX_SAMPLES];

    if( bBloomAcrossWidth )
        GetSampleOffsets_Bloom( SrcDesc.Width, SrcDesc.Height, 0.0f * XM_PIDIV2, avSampleOffsets, avSampleWeights,
                                fSize, fBrightness );
    else
        GetSampleOffsets_Bloom( SrcDesc.Width, SrcDesc.Height, 1.0f * XM_PIDIV2, avSampleOffsets, avSampleWeights,
                                fSize, fBrightness );

    g_pd3dDevice->SetPixelShaderConstantF( PSCONST_avSampleOffsets, ( FLOAT* )avSampleOffsets, MAX_SAMPLES );
    g_pd3dDevice->SetPixelShaderConstantF( PSCONST_avSampleWeights, ( FLOAT* )avSampleWeights, MAX_SAMPLES );
    g_pd3dDevice->SetPixelShader( m_pBloomPS );

    g_pd3dDevice->SetTexture( 0, pSrcTexture );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );

    // Draw a fullscreen quad to sample the RT
    DrawFullScreenQuad();

    g_pd3dDevice->Resolve( D3DRESOLVE_RENDERTARGET0, NULL, pDstTexture, NULL,
                           0, 0, NULL, 1.0f, 0L, NULL );

    // Cleanup and return
    PopRenderTarget( 0L )->Release();
    g_pd3dDevice->SetPixelShader( NULL );
}
예제 #7
0
//--------------------------------------------------------------------------------------
// Name: Downsample2x2Texture()
// Desc: Scale down pSrcTexture by 1/2 x 1/2 and place the result in pDstTexture
//--------------------------------------------------------------------------------------
VOID PostProcess::Downsample2x2Texture( LPDIRECT3DTEXTURE9 pSrcTexture,
                                        LPDIRECT3DTEXTURE9 pDstTexture,
                                        DWORD dwEdramOffset )
{
    // Make sure that the required shaders and objects exist
    assert( m_pDownScale2x2PS );
    assert( pSrcTexture && pDstTexture );

    XGTEXTURE_DESC SrcDesc;
    XGGetTextureDesc( pSrcTexture, 0, &SrcDesc );

    // Create and set a render target
    D3DSURFACE_PARAMETERS surfaceParams = { 0 };
    surfaceParams.Base = dwEdramOffset;
    PushRenderTarget( 0L, CreateRenderTarget( pDstTexture, &surfaceParams ) );

    XMVECTOR avSampleOffsets[MAX_SAMPLES];
    GetSampleOffsets_DownScale2x2( SrcDesc.Width, SrcDesc.Height, avSampleOffsets );
    g_pd3dDevice->SetPixelShaderConstantF( PSCONST_avSampleOffsets, ( FLOAT* )avSampleOffsets, MAX_SAMPLES );

    // Create an exact 1/2 x 1/2 copy of the source texture
    g_pd3dDevice->SetPixelShader( m_pDownScale2x2PS );

    g_pd3dDevice->SetTexture( 0, pSrcTexture );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );

    // TODO: This should use border addressing with a black border!
    //m_pStarSourceTexture->Format.ClampX      = GPUCLAMP_CLAMP_TO_BORDER;
    //m_pStarSourceTexture->Format.ClampY      = GPUCLAMP_CLAMP_TO_BORDER;
    //m_pStarSourceTexture->Format.BorderColor = GPUBORDERCOLOR_ABGR_BLACK;
    //g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER );
    //g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER );

    // Draw a fullscreen quad to sample the RT
    DrawFullScreenQuad();

    g_pd3dDevice->Resolve( D3DRESOLVE_RENDERTARGET0, NULL, pDstTexture, NULL,
                           0, 0, NULL, 1.0f, 0L, NULL );

    // Cleanup and return
    PopRenderTarget( 0L )->Release();
    g_pd3dDevice->SetPixelShader( NULL );
}
예제 #8
0
//--------------------------------------------------------------------------------------
// Name: BrightPassFilterTexture()
// Desc: Run the bright-pass filter on m_pScaledSceneTexture and place the result
//       in m_pBrightPassTexture
//--------------------------------------------------------------------------------------
VOID PostProcess::BrightPassFilterTexture( LPDIRECT3DTEXTURE9 pSrcTexture,
                                           LPDIRECT3DTEXTURE9 pAdaptedLuminanceTexture,
                                           FLOAT fMiddleGrayKeyValue,
                                           LPDIRECT3DTEXTURE9 pDstTexture )
{
    // Make sure that the required shaders and objects exist
    assert( m_pBrightPassFilterPS );
    assert( pSrcTexture && pDstTexture );

    XGTEXTURE_DESC SrcDesc;
    XGGetTextureDesc( pSrcTexture, 0, &SrcDesc );

    // Create and set a render target
    PushRenderTarget( 0L, CreateRenderTarget( pDstTexture ) );

    // Get the offsets to be used within the GaussBlur5x5 pixel shader
    XMVECTOR avSampleOffsets[MAX_SAMPLES];
    XMVECTOR avSampleWeights[MAX_SAMPLES];
    GetSampleOffsets_GaussBlur5x5( SrcDesc.Width, SrcDesc.Height, avSampleOffsets, avSampleWeights );
    g_pd3dDevice->SetPixelShaderConstantF( PSCONST_avSampleOffsets, ( FLOAT* )avSampleOffsets, MAX_SAMPLES );
    g_pd3dDevice->SetPixelShaderConstantF( PSCONST_avSampleWeights, ( FLOAT* )avSampleWeights, MAX_SAMPLES );
    g_pd3dDevice->SetPixelShaderConstantF( PSCONST_fMiddleGray, &fMiddleGrayKeyValue, 1 );
    g_pd3dDevice->SetPixelShader( m_pBrightPassFilterPS );

    g_pd3dDevice->SetTexture( 0, pSrcTexture );
    g_pd3dDevice->SetTexture( 1, pAdaptedLuminanceTexture );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT );
    g_pd3dDevice->SetSamplerState( 1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
    g_pd3dDevice->SetSamplerState( 1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );
    g_pd3dDevice->SetSamplerState( 1, D3DSAMP_MINFILTER, D3DTEXF_POINT );
    g_pd3dDevice->SetSamplerState( 1, D3DSAMP_MAGFILTER, D3DTEXF_POINT );

    // Draw a fullscreen quad to sample the RT
    DrawFullScreenQuad();

    g_pd3dDevice->Resolve( D3DRESOLVE_RENDERTARGET0, NULL, pDstTexture, NULL,
                           0, 0, NULL, 1.0f, 0L, NULL );

    // Cleanup and return
    PopRenderTarget( 0L )->Release();
    g_pd3dDevice->SetPixelShader( NULL );
}
예제 #9
0
//--------------------------------------------------------------------------------------
// Name: GaussBlur5x5Texture()
// Desc: Perform a 5x5 gaussian blur on pSrcTexture and place the result in pDstTexture
//--------------------------------------------------------------------------------------
VOID PostProcess::GaussBlur5x5Texture( LPDIRECT3DTEXTURE9 pSrcTexture,
                                       LPDIRECT3DTEXTURE9 pDstTexture,
                                       DWORD dwEdramOffset )
{
    // Make sure that the required shaders and objects exist
    assert( m_pGaussBlur5x5PS );
    assert( pSrcTexture && pDstTexture );

    XGTEXTURE_DESC SrcDesc;
    XGGetTextureDesc( pSrcTexture, 0, &SrcDesc );

    // Create and set a render target
    D3DSURFACE_PARAMETERS surfaceParams =
    {
        0
    };
    surfaceParams.Base = dwEdramOffset;
    PushRenderTarget( 0L, CreateRenderTarget( pDstTexture, &surfaceParams ) );

    XMVECTOR avSampleOffsets[MAX_SAMPLES];
    XMVECTOR avSampleWeights[MAX_SAMPLES];
    GetSampleOffsets_GaussBlur5x5( SrcDesc.Width, SrcDesc.Height, avSampleOffsets, avSampleWeights );
    g_pd3dDevice->SetPixelShaderConstantF( PSCONST_avSampleOffsets, ( FLOAT* )avSampleOffsets, MAX_SAMPLES );
    g_pd3dDevice->SetPixelShaderConstantF( PSCONST_avSampleWeights, ( FLOAT* )avSampleWeights, MAX_SAMPLES );
    g_pd3dDevice->SetPixelShader( m_pGaussBlur5x5PS );

    g_pd3dDevice->SetTexture( 0, pSrcTexture );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );

    // Draw a fullscreen quad to sample the RT
    DrawFullScreenQuad();

    g_pd3dDevice->Resolve( D3DRESOLVE_RENDERTARGET0, NULL, pDstTexture, NULL,
                           0, 0, NULL, 1.0f, 0L, NULL );

    // Cleanup and return
    PopRenderTarget( 0L )->Release();
    g_pd3dDevice->SetPixelShader( NULL );
}
예제 #10
0
//--------------------------------------------------------------------------------------
// Name: Render()
// Desc: Renders the gamepad help image and its labelled callouts
//--------------------------------------------------------------------------------------
VOID Help::Render( Font* pFont, const HELP_CALLOUT* pTags, DWORD dwNumCallouts )
{
#ifdef ALLOW_CALLOUT_EDITTING
    // Use the shoulder buttons, triggers, and dpad to edit callout positions
    XINPUT_STATE CurrInputState;
    static XINPUT_STATE LastInputState = {0};
    static DWORD dwCurrCallout = 0;

    if( XInputGetState( 0, &CurrInputState ) == ERROR_SUCCESS )
    {
        if( ( 0 != (CurrInputState.Gamepad.wButtons&XINPUT_GAMEPAD_LEFT_SHOULDER) ) &&
            ( 0 == (LastInputState.Gamepad.wButtons&XINPUT_GAMEPAD_LEFT_SHOULDER) ) )
            dwCurrCallout = (dwCurrCallout+dwNumCallouts-1) % dwNumCallouts;
        if( ( 0 != (CurrInputState.Gamepad.wButtons&XINPUT_GAMEPAD_RIGHT_SHOULDER) ) &&
            ( 0 == (LastInputState.Gamepad.wButtons&XINPUT_GAMEPAD_RIGHT_SHOULDER) ) )
            dwCurrCallout = (dwCurrCallout+dwNumCallouts+1) % dwNumCallouts;

        HELP_CALLOUT_POS* pCallout = &g_pHelpCallouts[pTags[dwCurrCallout].wControl];
        XMFLOAT2* pPos1 = &pCallout->Button;
        XMFLOAT2* pPos2 = &pCallout->Placement[pTags[dwCurrCallout].wPlacement].Line;
        XMFLOAT2* pPos3 = &pCallout->Placement[pTags[dwCurrCallout].wPlacement].Text;

        if( CurrInputState.Gamepad.bLeftTrigger )
        {
            pPos1->x -= CurrInputState.Gamepad.wButtons&XINPUT_GAMEPAD_DPAD_LEFT  ? 1.0f : 0.0f;
            pPos1->x += CurrInputState.Gamepad.wButtons&XINPUT_GAMEPAD_DPAD_RIGHT ? 1.0f : 0.0f;
            pPos1->y -= CurrInputState.Gamepad.wButtons&XINPUT_GAMEPAD_DPAD_UP    ? 1.0f : 0.0f;
            pPos1->y += CurrInputState.Gamepad.wButtons&XINPUT_GAMEPAD_DPAD_DOWN  ? 1.0f : 0.0f;
        }
        else if( CurrInputState.Gamepad.bRightTrigger )
        {
            pPos2->x -= CurrInputState.Gamepad.wButtons&XINPUT_GAMEPAD_DPAD_LEFT  ? 1.0f : 0.0f;
            pPos2->x += CurrInputState.Gamepad.wButtons&XINPUT_GAMEPAD_DPAD_RIGHT ? 1.0f : 0.0f;
            pPos2->y -= CurrInputState.Gamepad.wButtons&XINPUT_GAMEPAD_DPAD_UP    ? 1.0f : 0.0f;
            pPos2->y += CurrInputState.Gamepad.wButtons&XINPUT_GAMEPAD_DPAD_DOWN  ? 1.0f : 0.0f;
            pPos3->x -= CurrInputState.Gamepad.wButtons&XINPUT_GAMEPAD_DPAD_LEFT  ? 1.0f : 0.0f;
            pPos3->x += CurrInputState.Gamepad.wButtons&XINPUT_GAMEPAD_DPAD_RIGHT ? 1.0f : 0.0f;
            pPos3->y -= CurrInputState.Gamepad.wButtons&XINPUT_GAMEPAD_DPAD_UP    ? 1.0f : 0.0f;
            pPos3->y += CurrInputState.Gamepad.wButtons&XINPUT_GAMEPAD_DPAD_DOWN  ? 1.0f : 0.0f;
        }

        memcpy( &LastInputState, &CurrInputState, sizeof(XINPUT_STATE) );
    }
#endif // ALLOW_CALLOUT_EDITTING

    // Calculate a scale factor based on the video mode
    D3DDISPLAYMODE ModeDesc;
    XGTEXTURE_DESC TextureDesc;
    g_pd3dDevice->GetDisplayMode( 0, &ModeDesc );
    XGGetTextureDesc( m_pGamepadTexture, 0, &TextureDesc );

    FLOAT fScale = ModeDesc.Width / 1280.0f;
    FLOAT h = fScale * 720 * 0.48f;
    FLOAT w = TextureDesc.Width * h / TextureDesc.Height;
    FLOAT x = ( ModeDesc.Width - w ) / 2;
    FLOAT y = ( ModeDesc.Height - h ) / 2;

    D3DRECT rcSaved = pFont->m_rcWindow;
    pFont->SetWindow( 0, 0, ModeDesc.Width, ModeDesc.Height );

    // Setup the gamepad vertices
    struct VERTEX
    {
        FLOAT sx, sy;
        FLOAT tu, tv;
    };

    // Set up state for rendering the gamepad
    g_pd3dDevice->SetVertexDeclaration( g_pHelpVertexDecl );
    g_pd3dDevice->SetVertexShader( g_pHelpVertexShader );
    g_pd3dDevice->SetTexture( 0, m_pGamepadTexture );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
    g_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
    g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
    g_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
    g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
    g_pd3dDevice->SetRenderState( D3DRS_VIEWPORTENABLE, FALSE );
    g_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
    g_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
    g_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
    g_pd3dDevice->SetRenderState( D3DRS_BLENDOP, D3DBLENDOP_ADD );

    // Draw the scene-darkening quad
    {
        VERTEX GamepadVertices[4] =
        {
            {                0.0f,                 0.0f, 0.0f, 0.0f },
            { 1.0f * ModeDesc.Width,                 0.0f, 1.0f, 0.0f },
            { 1.0f * ModeDesc.Width, 1.0f * ModeDesc.Height, 1.0f, 1.0f },
            {                0.0f, 1.0f * ModeDesc.Height, 0.0f, 1.0f },
        };

        g_pd3dDevice->SetPixelShader( g_pDarkPixelShader );
        g_pd3dDevice->DrawPrimitiveUP( D3DPT_QUADLIST, 1, GamepadVertices, sizeof( GamepadVertices[0] ) );
    }

    // Draw the gamepad image
    {
        VERTEX GamepadVertices[4] =
        {
            { x,   y,   0.0f, 0.0f },
            { x + w, y,   1.0f, 0.0f },
            { x + w, y + h, 1.0f, 1.0f },
            { x,   y + h, 0.0f, 1.0f },
        };

        g_pd3dDevice->SetPixelShader( g_pHelpPixelShader );
        g_pd3dDevice->DrawPrimitiveUP( D3DPT_QUADLIST, 1, GamepadVertices, sizeof( GamepadVertices[0] ) );
    }

    // Set state to draw the lines
    g_pd3dDevice->SetTexture( 0, m_pLineTexture );
    g_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );

    for( DWORD i = 0; i < dwNumCallouts; i++ )
    {
        if( pTags[i].wControl >= ARRAYSIZE( g_vHelpCallouts ) )
            continue;

        HELP_CALLOUT_POS* pCallout = &g_pHelpCallouts[pTags[i].wControl];
        XMFLOAT2 line1 = pCallout->Button;
        XMFLOAT2 line2 = pCallout->Placement[pTags[i].wPlacement].Line;
        line1.x = fScale * ( line1.x - 640 ) + ModeDesc.Width / 2;
        line1.y = fScale * ( line1.y - 360 ) + ModeDesc.Height / 2;
        line2.x = fScale * ( line2.x - 640 ) + ModeDesc.Width / 2;
        line2.y = fScale * ( line2.y - 360 ) + ModeDesc.Height / 2;
        XMVECTOR vc = XMVector2Normalize( XMVectorSet( line2.y - line1.y, -line2.x + line1.x, 0, 0 ) );

#ifdef ALLOW_CALLOUT_EDITTING
        if( dwCurrCallout == i )
            vc *= 2;
#endif // ALLOW_CALLOUT_EDITTING

        // Initialize the callout line vertices
        VERTEX LineVertices[4] =
        {
            { ( line1.x - 2 * vc.x ), ( line1.y - 2 * vc.y ), 0.0f, 0.0f },
            { ( line1.x + 2 * vc.x ), ( line1.y + 2 * vc.y ), 1.0f, 0.0f },
            { ( line2.x + 2 * vc.x ), ( line2.y + 2 * vc.y ), 1.0f, 1.0f },
            { ( line2.x - 2 * vc.x ), ( line2.y - 2 * vc.y ), 0.0f, 1.0f },
        };

        g_pd3dDevice->DrawPrimitiveUP( D3DPT_QUADLIST, 1, LineVertices, sizeof( LineVertices[0] ) );
    }

    // Turn the viewport back on
    g_pd3dDevice->SetRenderState( D3DRS_VIEWPORTENABLE, TRUE );

    // Prepare font for rendering
    pFont->Begin();
    static FLOAT fFontXScale = 1.0f;
    static FLOAT fFontYScale = 1.0f;
    pFont->SetScaleFactors( fFontXScale * fScale, fFontYScale * fScale );

    // Render the callouts
    for( DWORD i = 0; i < dwNumCallouts; i++ )
    {
        if( pTags[i].wControl >= ARRAYSIZE( g_vHelpCallouts ) )
            continue;

        HELP_CALLOUT_POS* pCallout = &g_pHelpCallouts[pTags[i].wControl];
        XMFLOAT2 pos = pCallout->Placement[pTags[i].wPlacement].Text;
        pos.x = fScale * ( pos.x - 640 ) + ModeDesc.Width / 2;
        pos.y = fScale * ( pos.y - 360 ) + ModeDesc.Height / 2;

        // Draw the callout text
        pFont->DrawText( pos.x, pos.y, 0xffffffff,
                         pTags[i].strText, g_pHelpCallouts[pTags[i].wControl].dwFontFlags );
    }

    // Flush the text drawing
    pFont->SetScaleFactors( 1.0f, 1.0f );
    pFont->End();

    pFont->m_rcWindow = rcSaved;
}