65.9K
CodeProject 正在变化。 阅读更多。
Home

理解 Direct3D 10 应用程序代码

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (11投票s)

2010年12月29日

CPOL

11分钟阅读

viewsIcon

52743

downloadIcon

1882

一篇描述 Direct3D 程序代码中一些重要部分的文章。

前言

在 Direct3D 中,您几乎所做的一切都是为了操作图形硬件本身。这就是为什么 DirectX 不仅仅是一个游戏平台,而是一个硬件接口。我们最关心的重要硬件是 GPU,即图形处理单元。GPU 是一个独立的集成电路芯片,它执行“着色器”文件。将着色器想象成在 GPU 上执行的迷你程序:它们用 HLSL 编写,并作为 FX(效果)文件加载,扩展名为 .fx。虽然 CPU(微处理器)执行的计算可以控制整个系统,但 GPU 执行图形相关的计算,将图形输出到显示器。因此,了解您计算机图形硬件的功能是一个好主意。您可以通过访问 http://www.nvidia.com 的“下载最新驱动程序更新”部分来识别您的 GPU(及其功能)。

创建任何 Direct3D 程序都需要先创建一个窗口,一个由窗口句柄标识的标准窗口。您可以使用普通的 Win32 API 函数或其他技术,如 MFC。创建主窗口的目的是接收消息并提供一个画布,Direct3D 将在该画布上渲染绘制或图像。Direct3D 将在其窗口上绘制所有数据。创建 Direct3D 对象后,我们接着创建 Direct3D 设备。Direct3D 设备是 Direct3D 中最重要的接口。

DirectX 图形基础结构:DXGI

DXGI 是构成所有最新版本 Direct3D 底层的组件。它负责 Direct3D 的基本任务,例如验证适当的分辨率速率和将图像显示在屏幕上。DXGI 实际上不是 DirectX API 的一部分,而是作为 DirectX API 和其他图形组件的基础。也就是说,它充当 Direct3D 和硬件之间的接口。

2.jpg

由于我们所有的渲染都是通过 Direct3D 设备完成的,因此我们需要做的第一件事就是构建一个设备。这还包括构建一个交换链,它由一个后台缓冲区和一个主图面组成。完成此操作需要我们填充一个名为 DXGI_SWAP_CHAIN_DESC 的结构,然后调用 D3D10CreateDeviceAndSwapChain() 函数。这些结构将在我们的第一个示例之后讨论。

理解交换链

GPU 在其内存中包含一个指向像素缓冲区的指针,该缓冲区包含当前显示在屏幕上的图像。当您需要渲染某些内容(如 3D 模型或图像)时,GPU 会更新此数组并将信息发送到显示器进行显示。然后,显示器从上到下重绘屏幕,用新图像替换旧图像。问题在于显示器刷新速度不够快,无法满足实时渲染的需求。如果在显示器刷新时渲染了另一个模型到 GPU,显示的图像将被切成两半,上半部分包含旧图像,下半部分包含新图像。这种效果称为撕裂,为了防止这种情况,DXGI 实现了一个称为交换(swapping)的功能。

根据 DirectX 浏览器中的文献,在 Direct3D 10 中,设备对象用于执行渲染和资源创建。在 Direct3D 11 中,应用程序使用即时上下文(immediate context)在缓冲区上执行渲染,而设备包含创建资源的方法。交换链负责将设备渲染到的缓冲区的内容显示在实际的显示器屏幕上。交换链包含两个或多个缓冲区,主要是前缓冲和后缓冲。这些是设备渲染以在显示器上显示的纹理。前缓冲是当前呈现给用户的内容。此缓冲区是只读的,不能被修改。后缓冲是设备将要绘制的渲染目标。一旦完成绘制操作,交换链将通过交换两个缓冲区来呈现后缓冲。后缓冲变成前缓冲,反之亦然。

DXGI 不直接将新图像渲染到显示器,而是将您的图像绘制到第二个像素缓冲区,称为后缓冲。前缓冲是当前显示的缓冲区。您将所有图像绘制到后缓冲,完成后,DXGI 将用后缓冲的内容更新前缓冲,丢弃旧图像。但是,如果图像在刷新期间仍在传输,仍然可能发生撕裂。这就是为什么 DXGI 为每个缓冲区(前缓冲和后缓冲)使用一个指针,并简单地切换它们的值。然后,后缓冲成为前缓冲(反之亦然),因此不会发生撕裂。

3.jpg

示例

下面显示的图像是一个最初渲染空白屏幕的应用程序的输出。用户按下空格键,一个彩色、旋转的茶壶出现。用户再次按下空格键,茶壶的颜色会改变。如果用户按下 Alt-Enter,输出将进入全屏模式。现在,我们必须逐步分析代码,或者至少是主要的源代码文件。该文件将说明它首先要做的是构建一个 Direct3D 设备,因为所有渲染都是通过 Direct3D 设备完成的。但是,从上往下,我们还需要了解许多其他内容才能编写自己的 Direct3D 应用程序。

space.JPG

spacegreen.JPG

好的。所以显示了一个旋转的茶壶。用户按下空格键,茶壶改变颜色。如果用户按下 Alt-Enter 键,旋转的图像将进入全屏模式。这是应用程序的主要源代码。它将提供一个很好的框架来逐步了解编写 Direct3D 应用程序的一些核心概念。

#include "DXUT.h"
#include "DXUTmisc.h"
#include "SDKmisc.h"
#include "resource.h"
#include "DXUTShapes.h"

WNDCLASS     g_WindowClass;
BOOL                                g_bFullscreen = FALSE; //TRUE;
bool                         g_bRenderModel = true;
IDXGIFactory*                       g_pDXGIFactory = NULL;
HWND                                g_hWndPrimary = NULL;

struct DEVICE_OBJECT
{
    UINT Ordinal;
    ID3D10Device* pDevice;
    ID3DX10Font* pFont;
    ID3DX10Sprite* pSprite;
    CDXUTTextHelper* pTxtHelper;
    ID3DX10Mesh* pMesh;
    ID3D10Effect* pEffect;

    ID3D10InputLayout*            pInputLayout;
    ID3D10DepthStencilState*    pDepthStencilStateDepthEnable;
    ID3D10DepthStencilState*    pDepthStencilStateDepthDisable;
    ID3D10BlendState*        pBlendStateDisable;
    ID3D10RasterizerState*      pRasterizerStateNoCull;
    ID3D10EffectTechnique*      pRender;

    ID3D10EffectMatrixVariable* pmWorldViewProjection;
    ID3D10EffectMatrixVariable* pmWorld;
    
    // ScreenQuad Related Items
    ID3D10Buffer*                       pScreenQuadVB;
    ID3D10InputLayout*                  pQuadLayout;
    ID3D10EffectTechnique*              pTechQuad;
};

struct WINDOW_OBJECT
{
    HWND hWnd;
    IDXGIAdapter* pAdapter;
    IDXGIOutput* pOutput;
    IDXGISwapChain* pSwapChain;
    DEVICE_OBJECT* pDeviceObj;
    ID3D10RenderTargetView* pRenderTargetView;
    ID3D10DepthStencilView* pDepthStencilView;
    UINT Width;
    UINT Height;
    DXGI_ADAPTER_DESC AdapterDesc;
    DXGI_OUTPUT_DESC OutputDesc;
};

struct ADAPTER_OBJECT
{
    IDXGIAdapter* pDXGIAdapter;
    CGrowableArray <idxgioutput*> DXGIOutputArray;
};

CGrowableArray <device_object*>     g_DeviceArray;
CGrowableArray <adapter_object*>    g_AdapterArray;
CGrowableArray <window_object*>     g_WindowObjects;

DXGI_FORMAT g_dispFormat = DXGI_FORMAT_R10G10B10A2_UNORM;

const WCHAR g_szWindowClass[] = { L"Press the Space Bar" };
const WCHAR g_szWindowedName[] = { L"Press the Space Bar" };
const WCHAR g_szFullscreenName[] = { L"Press the Space Bar" };

struct SCREEN_VERTEX
{
    D3DXVECTOR4 pos;
    D3DXVECTOR2 tex;

    static const DWORD FVF;
};

struct TEST_MODE
{
    float    rangeScale;
    bool    bRenderModel;
    float    modelColor[4];
};

// Test Mode Definitions
static const TEST_MODE    g_testModes[] = {
            { 0.25f, false, 1.0f, 0.0f, 0.0f, 1.0f },
            { 0.25f, true,  1.0f, 1.0f, 1.0f, 1.0f },
            { 0.25f, true,  1.0f, 0.0f, 0.0f, 1.0f },
            { 0.25f, true,  0.0f, 1.0f, 0.0f, 1.0f },
            { 0.25f, true,  0.0f, 0.0f, 1.0f, 1.0f },

            { 0.5f, false,  0.0f, 0.0f, 1.0f, 1.0f },
            { 0.5f, true,   1.0f, 1.0f, 1.0f, 1.0f },
            { 0.5f, true,   1.0f, 0.0f, 0.0f, 1.0f },
            { 0.5f, true,   0.0f, 1.0f, 0.0f, 1.0f },
            { 0.5f, true,   0.0f, 0.0f, 1.0f, 1.0f },

            { 1.0f, false,  1.0f, 0.0f, 0.0f, 1.0f },
            { 1.0f, true,   1.0f, 1.0f, 1.0f, 1.0f },
            { 1.0f, true,   1.0f, 0.0f, 0.0f, 1.0f },
            { 1.0f, true,   0.0f, 1.0f, 0.0f, 1.0f },
            { 1.0f, true,   0.0f, 0.0f, 1.0f, 1.0f },
        };

const UINT    g_nTestModes = ARRAYSIZE(g_testModes);
UINT        g_iCurrentTestMode = 0;

//------------------------------------------------------
// Forward declarations 
//------------------------------------------------------
HRESULT OnD3D10CreateDevice( DEVICE_OBJECT* pDeviceObj );
void OnD3D10FrameRender( WINDOW_OBJECT* pWindowObj, 
                         double fTime, float fElapsedTime );
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, 
        WPARAM wParam, LPARAM lParam );
void RenderText( WINDOW_OBJECT* pWindowObj );

HRESULT InitD3D10();
HRESULT EnumerateAdapters();
HRESULT EnumerateOutputs( ADAPTER_OBJECT* pAdapterObj );
HRESULT CreateWindowClass( HINSTANCE hInstance );
HRESULT CreateMonitorWindows();
HRESULT SetWindowAssociation();
HRESULT CreateDevicePerAdapter( D3D10_DRIVER_TYPE DriverType );
HRESULT CreateSwapChainPerOutput();
HRESULT ResetSwapChains();
HRESULT CreateViewsForWindowObject( WINDOW_OBJECT* pWindowObj );
HRESULT SetupMultiMon();
HRESULT ReleaseSwapChains();
void DeviceCleanup();
void FullCleanup();
void RenderToAllMonitors( double fTime, float fElapsedTime );
void PresentToAllMonitors();

void DrawFullScreenQuad10( DEVICE_OBJECT* pDeviceObj, 
     ID3D10EffectTechnique* pTech, UINT Width, UINT Height );

int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                     LPWSTR lpCmdLine, int nCmdShow )
{
    
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

    // Init D3D10
    if( FAILED( InitD3D10() ) )
    {
        MessageBox( NULL, L"This application requires Direct3D10 and " 
          L"Vista to run.  This application will now exit.", L"Error", MB_OK );
        FullCleanup();
        return 1;
    }

    // Enumerate Adapters
    if( FAILED( EnumerateAdapters() ) )
    {
        MessageBox( NULL, L"Could not enumerate adapters.  " 
           L"This application will now exit.", L"Error", MB_OK );
        FullCleanup();
        return 1;
    }

    // Create the window class
    if( FAILED( CreateWindowClass( hInstance ) ) )
    {
        MessageBox( NULL, L"Could not create window class.  " 
          L"This application will now exit.", L"Error", MB_OK );
        FullCleanup();
        return 1;
    }

    // Setup the system for multi-mon
    if( FAILED( SetupMultiMon() ) )
    {
        FullCleanup();
        return 1;
    }

    // Start the timer
    CDXUTTimer Timer;
    float fElapsedTime = 0.0f;
    double fTime = 0.0f;
    Timer.Start();
    fTime = Timer.GetTime();

    // Now we're ready to receive and process Windows messages.
    bool bGotMsg;
    MSG msg;
    msg.message = WM_NULL;
    PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );

    while( WM_QUIT != msg.message )
    {
        // Use PeekMessage() so we can use idle time to render the scene. 
        bGotMsg = ( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) != 0 );

        if( bGotMsg )
        {
            // Translate and dispatch the message
            if( g_hWndPrimary == NULL ||
                0 == TranslateAccelerator( g_hWndPrimary, NULL, &msg ) )
            {
                TranslateMessage( &msg );
                DispatchMessage( &msg );
            }
        }
        else
        {
            // Render a frame during idle time (no messages are waiting)
            RenderToAllMonitors( fTime, fElapsedTime );
            PresentToAllMonitors();
        }

        fTime = Timer.GetTime();
        fElapsedTime = Timer.GetElapsedTime();
    }

    FullCleanup();

    return 0;
}


HRESULT OnD3D10CreateDevice( DEVICE_OBJECT* pDeviceObj )
{
    HRESULT hr;

    V_RETURN( D3DX10CreateSprite( pDeviceObj->pDevice, 500, 
                                  &pDeviceObj->pSprite ) );
    V_RETURN( D3DX10CreateFont( pDeviceObj->pDevice, 15, 0, FW_BOLD, 1, 
                                FALSE, DEFAULT_CHARSET,
                                OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 
                                DEFAULT_PITCH | FF_DONTCARE,
                                L"Arial", &pDeviceObj->pFont ) );
    pDeviceObj->pTxtHelper = new CDXUTTextHelper( NULL, NULL, 
        pDeviceObj->pFont, pDeviceObj->pSprite, 15 );

    // Load the effect file
    WCHAR str[MAX_PATH];
    V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"10BitScanout10.fx" ) );
    DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
    
    dwShaderFlags |= D3D10_SHADER_DEBUG;
    #endif
    V_RETURN( D3DX10CreateEffectFromFile( str, NULL, NULL, "fx_4_0", 
              dwShaderFlags, 0, pDeviceObj->pDevice, NULL,
              NULL, &pDeviceObj->pEffect, NULL, NULL ) );

    pDeviceObj->pRender = pDeviceObj->pEffect->GetTechniqueByName( "Render" );
    pDeviceObj->pTechQuad = pDeviceObj->pEffect->GetTechniqueByName( "RenderQuad" );

    pDeviceObj->pmWorldViewProjection = 
      pDeviceObj->pEffect->GetVariableByName( "g_mWorldViewProjection" )->AsMatrix();
    pDeviceObj->pmWorld = 
      pDeviceObj->pEffect->GetVariableByName( "g_mWorld" )->AsMatrix(); 

    // Create an input layout
    const D3D10_INPUT_ELEMENT_DESC layout[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  
          D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, 
          D3D10_INPUT_PER_VERTEX_DATA, 0 },
    };
    D3D10_PASS_DESC PassDesc;
    pDeviceObj->pRender->GetPassByIndex( 0 )->GetDesc( &PassDesc );
    V_RETURN( pDeviceObj->pDevice->CreateInputLayout( 
       layout, sizeof( layout ) / sizeof( layout[0] ),
       PassDesc.pIAInputSignature,
       PassDesc.IAInputSignatureSize, &pDeviceObj->pInputLayout ) );

    // Create a shape
    DXUTCreateTeapot( pDeviceObj->pDevice, &pDeviceObj->pMesh );

    // Create a screen quad for all render to texture operations
    SCREEN_VERTEX svQuad[4];
    svQuad[0].pos = D3DXVECTOR4( -1.0f, 1.0f, 0.5f, 1.0f );
    svQuad[0].tex = D3DXVECTOR2( 0.0f, 0.0f );
    svQuad[1].pos = D3DXVECTOR4( 1.0f, 1.0f, 0.5f, 1.0f );
    svQuad[1].tex = D3DXVECTOR2( 1.0f, 0.0f );
    svQuad[2].pos = D3DXVECTOR4( -1.0f, -1.0f, 0.5f, 1.0f );
    svQuad[2].tex = D3DXVECTOR2( 0.0f, 1.0f );
    svQuad[3].pos = D3DXVECTOR4( 1.0f, -1.0f, 0.5f, 1.0f );
    svQuad[3].tex = D3DXVECTOR2( 1.0f, 1.0f );
    
    D3D10_BUFFER_DESC vbdesc =
    {
        4 * sizeof( SCREEN_VERTEX ),
        D3D10_USAGE_DEFAULT,
        D3D10_BIND_VERTEX_BUFFER,
        0,
        0
    };

    D3D10_SUBRESOURCE_DATA InitData;
    InitData.pSysMem = svQuad;
    InitData.SysMemPitch = 0;
    InitData.SysMemSlicePitch = 0;
    V_RETURN( pDeviceObj->pDevice->CreateBuffer( &vbdesc, 
      &InitData, &(pDeviceObj->pScreenQuadVB) ) );

    // Create our quad input layout
    const D3D10_INPUT_ELEMENT_DESC quadlayout[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 
          0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 
          0, 16, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    };
    V_RETURN( pDeviceObj->pTechQuad->GetPassByIndex( 0 )->GetDesc( &PassDesc ) );
    V_RETURN( pDeviceObj->pDevice->CreateInputLayout( quadlayout, 2, 
              PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, 
              &(pDeviceObj->pQuadLayout) ) );

    // Create the Depth and Blend state objects we need
    D3D10_DEPTH_STENCIL_DESC dsDescDepth;
    ZeroMemory( &dsDescDepth, sizeof( D3D10_DEPTH_STENCIL_DESC ) );
    dsDescDepth.DepthEnable = TRUE;
    dsDescDepth.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL;
    dsDescDepth.DepthFunc = D3D10_COMPARISON_LESS_EQUAL;
    dsDescDepth.StencilEnable = FALSE;

    V_RETURN( pDeviceObj->pDevice->CreateDepthStencilState( 
      &dsDescDepth, &pDeviceObj->pDepthStencilStateDepthEnable ) );

    dsDescDepth.DepthEnable = FALSE;
    V_RETURN( pDeviceObj->pDevice->CreateDepthStencilState( 
      &dsDescDepth, &pDeviceObj->pDepthStencilStateDepthDisable ) );

    // Create a rasterizer state to disable culling
    D3D10_RASTERIZER_DESC rsDesc;
    rsDesc.FillMode = D3D10_FILL_SOLID;
    rsDesc.CullMode = D3D10_CULL_NONE;
    rsDesc.FrontCounterClockwise = TRUE;
    rsDesc.DepthBias = 0;
    rsDesc.DepthBiasClamp = 0;
    rsDesc.SlopeScaledDepthBias = 0;
    rsDesc.ScissorEnable = FALSE;
    rsDesc.MultisampleEnable = TRUE;
    rsDesc.AntialiasedLineEnable = FALSE;
    V_RETURN( pDeviceObj->pDevice->CreateRasterizerState( 
      &rsDesc, &pDeviceObj->pRasterizerStateNoCull ) );

    D3D10_BLEND_DESC    bsBlendDesc;
    ZeroMemory( &bsBlendDesc, sizeof( D3D10_BLEND_DESC ) );
    bsBlendDesc.BlendEnable[0] = FALSE;
    bsBlendDesc.AlphaToCoverageEnable = FALSE;
    bsBlendDesc.RenderTargetWriteMask[ 0 ] = D3D10_COLOR_WRITE_ENABLE_ALL;

    hr = pDeviceObj->pDevice->CreateBlendState( 
           &bsBlendDesc, &pDeviceObj->pBlendStateDisable );

    return hr;
}

//--------------------------------------------------------------
// Called to render a frame
//--------------------------------------------------------------
void OnD3D10FrameRender( WINDOW_OBJECT* pWindowObj, 
                         double fTime, float fElapsedTime )
{
    DEVICE_OBJECT* pDeviceObj = pWindowObj->pDeviceObj;
    ID3D10Device* pd3dDevice = pWindowObj->pDeviceObj->pDevice;
    float          screenRez[2] = 
      { (float)pWindowObj->Width, (float)pWindowObj->Height };

    // Clear?
    float ClearColor[4] = { 0.1f, 0.3f, 0.8f, 0.0f };
    pd3dDevice->ClearRenderTargetView( 
      pWindowObj->pRenderTargetView, ClearColor );
    pd3dDevice->ClearDepthStencilView( 
      pWindowObj->pDepthStencilView, D3D10_CLEAR_DEPTH, 1.0, 0 );

    // Update the Test Mode Parameters
    pDeviceObj->pEffect->GetVariableByName( 
      "g_colorRange" )->AsScalar()->SetFloat( 
      g_testModes[ g_iCurrentTestMode ].rangeScale ) ;
    pDeviceObj->pEffect->GetVariableByName( 
      "g_vColor" )->AsVector()->SetFloatVector( 
      (float*) &g_testModes[ g_iCurrentTestMode ].modelColor[0] );
    pDeviceObj->pEffect->GetVariableByName( 
      "g_vScreenRez" )->AsVector()->SetFloatVector( screenRez );
    g_bRenderModel = g_testModes[ g_iCurrentTestMode ].bRenderModel ;

    // Set state Objects
    pDeviceObj->pDevice->OMSetBlendState( 
      pDeviceObj->pBlendStateDisable, NULL, 0);
    pDeviceObj->pDevice->OMSetDepthStencilState( 
      pDeviceObj->pDepthStencilStateDepthDisable, 0 );
    pDeviceObj->pDevice->RSSetState( 
      pDeviceObj->pRasterizerStateNoCull );

    // Render the color test fullscreen Quad
    pDeviceObj->pTechQuad->GetPassByIndex( 0 )->Apply( 0 );
    DrawFullScreenQuad10( pDeviceObj, pDeviceObj->pTechQuad, 
                          pWindowObj->Width, pWindowObj->Height ) ;

    // Shade a Mesh
    if (g_bRenderModel)
    {
        pDeviceObj->pDevice->OMSetDepthStencilState( 
                      pDeviceObj->pDepthStencilStateDepthEnable, 0 );

        // Setup the effects
        D3DXMATRIX mWorld;
        D3DXMATRIX mView;
        D3DXMATRIX mProj;
        D3DXMatrixRotationY( &mWorld, ( float )fTime * D3DX_PI / 2.0f );
        D3DXVECTOR3 vEye( 0,4,-4 );
        D3DXVECTOR3 vLook( 0,0,0 );
        D3DXVECTOR3 vUp( 0,1,0 );
        D3DXMatrixLookAtLH( &mView, &vEye, &vLook, &vUp );
        float fAspect = pWindowObj->Width / ( float )pWindowObj->Height;
        D3DXMatrixPerspectiveFovLH( 
          &mProj, D3DX_PI / 3.0f, fAspect, 0.1f, 30.0f );
        D3DXMATRIX mWVP = mWorld * mView * mProj;

        pDeviceObj->pmWorldViewProjection->SetMatrix( ( float* )&mWVP );
        pDeviceObj->pmWorld->SetMatrix( ( float* )&mWorld );

        D3D10_TECHNIQUE_DESC techDesc;
        pDeviceObj->pRender->GetDesc( &techDesc );

        UINT NumSubsets;
        pDeviceObj->pMesh->GetAttributeTable( NULL, &NumSubsets );

        pd3dDevice->IASetInputLayout( pDeviceObj->pInputLayout );

        for( UINT p = 0; p < techDesc.Passes; p++ )
        {
            pDeviceObj->pRender->GetPassByIndex( p )->Apply( 0 );

            for( UINT s = 0; s < NumSubsets; s++ )
            {
                pDeviceObj->pMesh->DrawSubset( s );
            }
        }
    }

    RenderText( pWindowObj );
}


LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    switch( uMsg )
    {
        case WM_KEYDOWN:
        case WM_SYSKEYDOWN:
        {
            switch( wParam )
            { 
                case VK_ESCAPE:
                    PostQuitMessage( 0 );
                    return 0;
                    break;
                case VK_SPACE:
                    g_iCurrentTestMode++;
                    g_iCurrentTestMode = g_iCurrentTestMode % g_nTestModes;
                    break;
                case VK_F8:
                    g_dispFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
                    ResetSwapChains();
                    break;
                case VK_F10:
                    g_dispFormat = DXGI_FORMAT_R10G10B10A2_UNORM;
                    ResetSwapChains();
                    break;
            }
        }
        break;
        case WM_CLOSE:
            PostQuitMessage( 0 );
            return 0;
            break;
        case WM_SIZE:
            // Find the swapchain for this hwnd
            for( int i = 0; i < g_WindowObjects.GetSize(); i++ )
            {
                WINDOW_OBJECT* pObj = g_WindowObjects.GetAt( i );
                if( hWnd == pObj->hWnd )
                {
                    // Cleanup the views
                    SAFE_RELEASE( pObj->pRenderTargetView );
                    SAFE_RELEASE( pObj->pDepthStencilView );

                    RECT rcCurrentClient;
                    GetClientRect( hWnd, &rcCurrentClient );

                    pObj->Width = ( UINT )rcCurrentClient.right;
                    pObj->Height = ( UINT )rcCurrentClient.bottom;

                    // WM_SIZE messages can be sent during
                    // desctruction / re- creation of the swap chain
                    // ensure we have a swap chain to resize
                    if ( pObj->pSwapChain )
                    {
                        DXGI_SWAP_CHAIN_DESC Desc;
                        pObj->pSwapChain->GetDesc( &Desc );

                        pObj->pSwapChain->ResizeBuffers( Desc.BufferCount,
                             // passing in 0 here will automatically
                             // calculate the size from the client rect
                             ( UINT )rcCurrentClient.right,
                             // passing in 0 here will automatically
                             // calculate the size from the client rect
                             ( UINT )rcCurrentClient.bottom,
                             Desc.BufferDesc.Format,
                             0 );

                        // recreate the views
                        CreateViewsForWindowObject( pObj );
                    }

                    return 0;
                }
            }
            return 0;
    };

    return DefWindowProc( hWnd, uMsg, wParam, lParam );
}


//-------------------------------------------------------
// Initialize D3D10
//-------------------------------------------------------
HRESULT InitD3D10()
{
    HRESULT hr = S_OK;

    // Check for D3D10 dlls
    HMODULE hD3D10 = LoadLibrary( L"d3d10.dll" );
    HMODULE hD3DX10 = LoadLibrary( D3DX10_DLL );
    HMODULE hDXGI = LoadLibrary( L"dxgi.dll" );

    if( !hD3D10 || !hD3DX10 || !hDXGI )
        return E_FAIL;

    if( hD3D10 )
        FreeLibrary( hD3D10 );
    if( hD3DX10 )
        FreeLibrary( hD3DX10 );
    if( hDXGI )
        FreeLibrary( hDXGI );

    // Create the DXGI Factory
    hr = CreateDXGIFactory( IID_IDXGIFactory, 
             ( void** )&g_pDXGIFactory );
    if( FAILED( hr ) )
        return hr;

    return hr;
}

//----------------------------------------------------------
// Enumerate all D3D10 Adapters
//----------------------------------------------------------
HRESULT EnumerateAdapters()
{
    HRESULT hr = S_OK;

    for( UINT i = 0; ; i++ )
    {
        ADAPTER_OBJECT* pAdapterObj = new ADAPTER_OBJECT;
        if( !pAdapterObj )
            return E_OUTOFMEMORY;

        pAdapterObj->pDXGIAdapter = NULL;
        hr = g_pDXGIFactory->EnumAdapters( i, 
                  &pAdapterObj->pDXGIAdapter );
        if( DXGI_ERROR_NOT_FOUND == hr )
        {
            delete pAdapterObj;
            hr = S_OK;
            break;
        }
        if( FAILED( hr ) )
        {
            delete pAdapterObj;
            return hr;
        }

        // get the description of the adapter
        DXGI_ADAPTER_DESC AdapterDesc;
        hr = pAdapterObj->pDXGIAdapter->GetDesc( &AdapterDesc );
        if( FAILED( hr ) )
        {
            delete pAdapterObj;
            return hr;
        }

        // Enumerate outputs for this adapter
        hr = EnumerateOutputs( pAdapterObj );
        if( FAILED( hr ) )
        {
            delete pAdapterObj;
            return hr;
        }

        // add the adapter to the list
        if( pAdapterObj->DXGIOutputArray.GetSize() > 0 )
            g_AdapterArray.Add( pAdapterObj );
    }

    if( g_AdapterArray.GetSize() < 1 )
        return E_FAIL;

    return hr;
}


HRESULT EnumerateOutputs( ADAPTER_OBJECT* pAdapterObj )
{
    HRESULT hr = S_OK;

    for( UINT i = 0; ; i++ )
    {
        IDXGIOutput* pOutput;
        hr = pAdapterObj->pDXGIAdapter->EnumOutputs( i, &pOutput );
        if( DXGI_ERROR_NOT_FOUND == hr )
        {
            hr = S_OK;
            break;
        }
        if( FAILED( hr ) )
            return hr;

        // get the description
        DXGI_OUTPUT_DESC OutputDesc;
        hr = pOutput->GetDesc( &OutputDesc );
        if( FAILED( hr ) )
            return hr;

        

        pAdapterObj->DXGIOutputArray.Add( pOutput );
    }

    return hr;
}

//-----------------------------------------------------------
// Creates and register the window class
//-----------------------------------------------------------
HRESULT CreateWindowClass( HINSTANCE hInstance )
{
    WCHAR szExePath[MAX_PATH];
    GetModuleFileName( NULL, szExePath, MAX_PATH );
    HICON hIcon = ExtractIcon( hInstance, szExePath, 0 );

    ZeroMemory( &g_WindowClass, sizeof( WNDCLASS ) );
    g_WindowClass.hCursor = LoadCursor( NULL, IDC_ARROW );
    g_WindowClass.hIcon = hIcon;
    g_WindowClass.hbrBackground = 
      ( HBRUSH )GetStockObject( BLACK_BRUSH );
    g_WindowClass.style = CS_DBLCLKS;
    g_WindowClass.lpfnWndProc = MsgProc;
    g_WindowClass.hInstance = hInstance;
    g_WindowClass.lpszClassName = g_szWindowClass;

    ATOM ClassAtom = RegisterClass( &g_WindowClass );
    if( ClassAtom == 0 )
    {
        DWORD error = GetLastError();

        if( ERROR_CLASS_ALREADY_EXISTS != error )
        {
            return E_FAIL;
        }
    }

    return S_OK;
}

//---------------------------------------------------------
// Creates windows for all monitors
//---------------------------------------------------------
HRESULT CreateMonitorWindows()
{
    HRESULT hr = S_OK;

    for( int a = 0; a < g_AdapterArray.GetSize(); a++ )
    {
        ADAPTER_OBJECT* pAdapter = g_AdapterArray.GetAt( a );
        for( int o = 0; o < pAdapter->DXGIOutputArray.GetSize(); o++ )
        {
            IDXGIOutput* pOutput = pAdapter->DXGIOutputArray.GetAt( o );
            DXGI_OUTPUT_DESC OutputDesc;
            pOutput->GetDesc( &OutputDesc );
            int X = OutputDesc.DesktopCoordinates.left;
            int Y = OutputDesc.DesktopCoordinates.top;
            int Width = OutputDesc.DesktopCoordinates.right - X;
            int Height = OutputDesc.DesktopCoordinates.bottom - Y;

            WINDOW_OBJECT* pWindowObj = new WINDOW_OBJECT;
            ZeroMemory( pWindowObj, sizeof( WINDOW_OBJECT ) );

            if( g_bFullscreen )
            {
                pWindowObj->hWnd = CreateWindow( g_szWindowClass,
                                                 g_szWindowedName,
                                                 WS_POPUP,
                                                 X,
                                                 Y,
                                                 Width,
                                                 Height,
                                                 NULL,
                                                 0,
                                                 g_WindowClass.hInstance,
                                                 NULL );
            }
            else
            {
                X += 100;
                Y += 100;
                Width /= 2;
                Height /= 2;
                DWORD dwStyle = WS_OVERLAPPEDWINDOW & 
                  ~( WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME );
                pWindowObj->hWnd = CreateWindow( g_szWindowClass,
                                                 g_szWindowedName,
                                                 dwStyle,
                                                 X,
                                                 Y,
                                                 Width,
                                                 Height,
                                                 NULL,
                                                 0,
                                                 g_WindowClass.hInstance,
                                                 NULL );
            }

            if( NULL == pWindowObj->hWnd )
            {
                delete pWindowObj;
                return E_FAIL;
            }

            // show the window
            ShowWindow( pWindowObj->hWnd, SW_SHOWDEFAULT );

            // set width and height
            pWindowObj->Width = Width;
            pWindowObj->Height = Height;

            // add this to the window object array
            g_WindowObjects.Add( pWindowObj );

        }
    }

    return hr;
}

//-------------------------------------------------
// Set DXGI's window association
//-------------------------------------------------
HRESULT SetWindowAssociation()
{
    if( g_WindowObjects.GetSize() < 1 )
        return E_FAIL;

    HWND hWnd = g_WindowObjects.GetAt( 0 )->hWnd;

    // set window association
    return g_pDXGIFactory->MakeWindowAssociation( hWnd, 0 );
}

//-----------------------------------------------------
// Creates a device per adapter
//-----------------------------------------------------
HRESULT CreateDevicePerAdapter( D3D10_DRIVER_TYPE DriverType )
{
    HRESULT hr = S_OK;
    int iWindowObj = 0;
    for( int a = 0; a < g_AdapterArray.GetSize(); a++ )
    {
        ADAPTER_OBJECT* pAdapterObj = g_AdapterArray.GetAt( a );

        IDXGIAdapter* pAdapter = NULL;
        if( D3D10_DRIVER_TYPE_HARDWARE == DriverType )
            pAdapter = pAdapterObj->pDXGIAdapter;

        UINT CreateFlags = 0;

        // Create a device for this adapter
        ID3D10Device* pd3dDevice = NULL;
        hr = D3D10CreateDevice( pAdapter,
                                DriverType,
                                NULL,
                                CreateFlags,
                                D3D10_SDK_VERSION,
                                &pd3dDevice );
        if( FAILED( hr ) )
            return hr;

        DEVICE_OBJECT* pDeviceObj = new DEVICE_OBJECT;
        ZeroMemory( pDeviceObj, sizeof( DEVICE_OBJECT ) );
        pDeviceObj->pDevice = pd3dDevice;

        // add the device
        pDeviceObj->Ordinal = g_DeviceArray.GetSize();
        g_DeviceArray.Add( pDeviceObj );

        // Init stuff needed for the device
        OnD3D10CreateDevice( pDeviceObj );

        // go through the outputs and set the device, adapter, and output
        for( int o = 0; o < pAdapterObj->DXGIOutputArray.GetSize(); o++ )
        {
            IDXGIOutput* pOutput = pAdapterObj->DXGIOutputArray.GetAt( o );
            WINDOW_OBJECT* pWindowObj = g_WindowObjects.GetAt( iWindowObj );

            pWindowObj->pDeviceObj = pDeviceObj;
            pWindowObj->pAdapter = pAdapter;
            pWindowObj->pOutput = pOutput;
            iWindowObj ++;
        }
    }

    return hr;
}

HRESULT ResetSwapChains()
{
    HRESULT hr = S_OK;
    // Release Existing swapChains
    hr = ReleaseSwapChains();

    // Create a new swap chain for each output
    hr = CreateSwapChainPerOutput();
    if( FAILED( hr ) )
    {
        MessageBox( NULL, L"Could not Re-create Swap Chains for " 
          L"all outputs.  This application will now exit.", 
          L"Error", MB_OK );
        return hr;
    }

    // ReSet DXGI Window association for the first monitor window
    hr = SetWindowAssociation();
    if( FAILED( hr ) )
    {
        MessageBox( NULL, L"Could not Re-set DXGI window " 
          L"association.  This application will now exit.", L"Error",
          MB_OK );
        return hr;
    }
    // Ensure the window has focus - sometimes this
    // gets changed during the switch
    SetFocus( g_hWndPrimary );

    return hr;
}

//------------------------------------------------------
// Creates a swapchain per output
//------------------------------------------------------
HRESULT CreateSwapChainPerOutput()
{
    HRESULT hr = S_OK;

    for( int i = 0; i < g_WindowObjects.GetSize(); i++ )
    {
        WINDOW_OBJECT* pWindowObj = g_WindowObjects.GetAt( i );

        // get the dxgi device
        IDXGIDevice* pDXGIDevice = NULL;
        hr = pWindowObj->pDeviceObj->pDevice->QueryInterface( 
                 IID_IDXGIDevice, ( void** )&pDXGIDevice );
        if( FAILED( hr ) )
            return hr;

        // create a swap chain
        DXGI_SWAP_CHAIN_DESC SwapChainDesc;
        ZeroMemory( &SwapChainDesc, sizeof( DXGI_SWAP_CHAIN_DESC ) );
        SwapChainDesc.BufferDesc.Width = pWindowObj->Width;
        SwapChainDesc.BufferDesc.Height = pWindowObj->Height;
        SwapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
        SwapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
        SwapChainDesc.BufferDesc.Format = g_dispFormat; 
        SwapChainDesc.BufferDesc.ScanlineOrdering = 
                      DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
        SwapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
        SwapChainDesc.SampleDesc.Count = 1;
        SwapChainDesc.SampleDesc.Quality = 0;
        SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        SwapChainDesc.BufferCount = 3;
        SwapChainDesc.OutputWindow = pWindowObj->hWnd;
        SwapChainDesc.Windowed = ( g_bFullscreen == FALSE);
        SwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
        SwapChainDesc.Flags = 0;
        hr = g_pDXGIFactory->CreateSwapChain( pDXGIDevice, 
                &SwapChainDesc, &pWindowObj->pSwapChain );
        pDXGIDevice->Release();
        pDXGIDevice = NULL;
        if( FAILED( hr ) )
            return hr;

        hr = CreateViewsForWindowObject( pWindowObj );
        if( FAILED( hr ) )
            return hr;
    }

    return hr;
}

//--------------------------------------------------------------
// Creates a render target view and depth
// stencil surface/view per swapchain
//--------------------------------------------------------------
HRESULT CreateViewsForWindowObject( WINDOW_OBJECT* pWindowObj )
{
    HRESULT hr = S_OK;

    // get the backbuffer
    ID3D10Texture2D* pBackBuffer = NULL;
    hr = pWindowObj->pSwapChain->GetBuffer( 0, 
               IID_ID3D10Texture2D, ( void** )&pBackBuffer );
    if( FAILED( hr ) )
        return hr;

    // get the backbuffer desc
    D3D10_TEXTURE2D_DESC BBDesc;
    pBackBuffer->GetDesc( &BBDesc );

    // create the render target view
    D3D10_RENDER_TARGET_VIEW_DESC RTVDesc;
    RTVDesc.Format = BBDesc.Format;
    RTVDesc.ViewDimension = D3D10_RTV_DIMENSION_TEXTURE2D;
    RTVDesc.Texture2D.MipSlice = 0;
    hr = pWindowObj->pDeviceObj->pDevice->CreateRenderTargetView( 
              pBackBuffer, &RTVDesc,
              &pWindowObj->pRenderTargetView );
    pBackBuffer->Release();
    pBackBuffer = NULL;
    if( FAILED( hr ) )
        return hr;

    // Create depth stencil texture
    ID3D10Texture2D* pDepthStencil = NULL;
    D3D10_TEXTURE2D_DESC descDepth;
    descDepth.Width = pWindowObj->Width;
    descDepth.Height = pWindowObj->Height;
    descDepth.MipLevels = 1;
    descDepth.ArraySize = 1;
    descDepth.Format = DXGI_FORMAT_D16_UNORM;
    descDepth.SampleDesc.Count = 1;
    descDepth.SampleDesc.Quality = 0;
    descDepth.Usage = D3D10_USAGE_DEFAULT;
    descDepth.BindFlags = D3D10_BIND_DEPTH_STENCIL;
    descDepth.CPUAccessFlags = 0;
    descDepth.MiscFlags = 0;
    hr = pWindowObj->pDeviceObj->pDevice->CreateTexture2D( 
                      &descDepth, NULL, &pDepthStencil );
    if( FAILED( hr ) )
        return hr;

    // Create the depth stencil view
    D3D10_DEPTH_STENCIL_VIEW_DESC descDSV;
    descDSV.Format = descDepth.Format;
    descDSV.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D;
    descDSV.Texture2D.MipSlice = 0;
    hr = pWindowObj->pDeviceObj->pDevice->CreateDepthStencilView( 
                pDepthStencil, &descDSV,
                &pWindowObj->pDepthStencilView );
    SAFE_RELEASE( pDepthStencil );
    if( FAILED( hr ) )
        return hr;

    // get various information
    if( pWindowObj->pAdapter )
        pWindowObj->pAdapter->GetDesc( &pWindowObj->AdapterDesc );
    if( pWindowObj->pOutput )
        pWindowObj->pOutput->GetDesc( &pWindowObj->OutputDesc );

    return hr;
}

//--------------------------------------------------
// Setup Multi-mon based upon g_MultiMonType
//--------------------------------------------------
HRESULT SetupMultiMon()
{
    HRESULT hr = S_OK;

    // Create a window per monitor
    hr = CreateMonitorWindows();
    if( FAILED( hr ) )
    {
        MessageBox( NULL, L"Could not create monitor windows.  " 
             L"This application will now exit.", L"Error", MB_OK );
        return hr;
    }

    // Set DXGI Window association for the first monitor window
    hr = SetWindowAssociation();
    if( FAILED( hr ) )
    {
        MessageBox( NULL, L"Could not set DXGI window association.  " 
           L"This application will now exit.", L"Error", MB_OK );
        return hr;
    }

    // Create a device per adapter or device per output
    hr = CreateDevicePerAdapter( D3D10_DRIVER_TYPE_HARDWARE );
    if( FAILED( hr ) )
    {
        hr = CreateDevicePerAdapter( D3D10_DRIVER_TYPE_REFERENCE );
        if( FAILED( hr ) )
        {
            MessageBox( NULL, L"Could not create a compatible " 
              L"Direct3D10 device.  This application will now exit.",
              L"Error", MB_OK );
            return hr;
        }
    }

    // Create a swap chain for each output
    hr = CreateSwapChainPerOutput();
    if( FAILED( hr ) )
    {
        MessageBox( NULL, L"Could not create Swap Chains for " 
          L"all outputs.  This application will now exit.", 
          L"Error", MB_OK );
        return hr;
    }

    if( g_WindowObjects.GetSize() > 0 )
    {
        WINDOW_OBJECT* pWindowObj = g_WindowObjects.GetAt( 0 );
        g_hWndPrimary = pWindowObj->hWnd;
    }

    return hr;
}

//----------------------------------------------------------
// Cleanup the device and window based objects
//----------------------------------------------------------
HRESULT ReleaseSwapChains()
{
    HRESULT hr = S_OK;

    // cleanup window objects
    for( int w = 0; w < g_WindowObjects.GetSize(); w++ )
    {
        WINDOW_OBJECT* pWindowObj = g_WindowObjects.GetAt( w );
        SAFE_RELEASE( pWindowObj->pRenderTargetView );
        SAFE_RELEASE( pWindowObj->pDepthStencilView );

        // Force Windowed Mode
        pWindowObj->pSwapChain->SetFullscreenState( FALSE, NULL );
        SAFE_RELEASE( pWindowObj->pSwapChain );
    }
    return hr;
}

void DeviceCleanup()
{
    // Make sure we're in windowed mode,
    // since we cannot destroy a fullscreen swapchain
    for( int w = 0; w < g_WindowObjects.GetSize(); w++ )
    {
        WINDOW_OBJECT* pWindowObj = g_WindowObjects.GetAt( w );
        pWindowObj->pSwapChain->SetFullscreenState( FALSE, NULL );
    }

    // cleanup window objects
    for( int w = 0; w < g_WindowObjects.GetSize(); w++ )
    {
        WINDOW_OBJECT* pWindowObj = g_WindowObjects.GetAt( w );
        DestroyWindow( pWindowObj->hWnd );
        SAFE_RELEASE( pWindowObj->pRenderTargetView );
        SAFE_RELEASE( pWindowObj->pDepthStencilView );
        SAFE_RELEASE( pWindowObj->pSwapChain );
        SAFE_DELETE( pWindowObj );
    }
    g_WindowObjects.RemoveAll();

    // cleanup devices
    for( int d = 0; d < g_DeviceArray.GetSize(); d++ )
    {
        DEVICE_OBJECT* pDeviceObj = g_DeviceArray.GetAt( d );

        SAFE_RELEASE( pDeviceObj->pFont );
        SAFE_RELEASE( pDeviceObj->pSprite );
        SAFE_DELETE( pDeviceObj->pTxtHelper );
        SAFE_RELEASE( pDeviceObj->pEffect );
        SAFE_RELEASE( pDeviceObj->pMesh );
        SAFE_RELEASE( pDeviceObj->pInputLayout );

        SAFE_RELEASE( pDeviceObj->pDepthStencilStateDepthEnable );
        SAFE_RELEASE( pDeviceObj->pDepthStencilStateDepthDisable );
        SAFE_RELEASE( pDeviceObj->pBlendStateDisable );
        SAFE_RELEASE( pDeviceObj->pRasterizerStateNoCull );

        // Screen Quad Related Items
        SAFE_RELEASE( pDeviceObj->pScreenQuadVB );

        SAFE_RELEASE( pDeviceObj->pDevice );
        SAFE_DELETE( pDeviceObj );
    }
    g_DeviceArray.RemoveAll();
}

//-----------------------------------------------------
// Cleanup everything
//-----------------------------------------------------
void FullCleanup()
{
    // Destroy devices
    DeviceCleanup();

    // Remove enumerated objects
    for( int a = 0; a < g_AdapterArray.GetSize(); a++ )
    {
        ADAPTER_OBJECT* pAdapterObj = g_AdapterArray.GetAt( a );

        // go through the outputs
        for( int o = 0; o < pAdapterObj->DXGIOutputArray.GetSize(); o++ )
        {
            IDXGIOutput* pOutput = pAdapterObj->DXGIOutputArray.GetAt( o );
            SAFE_RELEASE( pOutput );
        }
        pAdapterObj->DXGIOutputArray.RemoveAll();

        SAFE_RELEASE( pAdapterObj->pDXGIAdapter );
        SAFE_DELETE( pAdapterObj );
    }
    g_AdapterArray.RemoveAll();
}


void RenderText( WINDOW_OBJECT* pWindowObj )
{
    WCHAR strTxt[MAX_PATH];

    pWindowObj->pDeviceObj->pTxtHelper->Begin();
    pWindowObj->pDeviceObj->pTxtHelper->SetInsertionPos( 5, 5 );
    pWindowObj->pDeviceObj->pTxtHelper->SetForegroundColor( 
                               D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );
  
// Acquire the device format
    ID3D10RenderTargetView *pBBRTV;
    D3D10_RENDER_TARGET_VIEW_DESC rtvDesc;
    pWindowObj->pDeviceObj->pDevice->OMGetRenderTargets( 1, &pBBRTV, NULL );
    pBBRTV->GetDesc( &rtvDesc);
    
   
    pBBRTV->Release();

    pWindowObj->pDeviceObj->pTxtHelper->SetForegroundColor( 
                               D3DXCOLOR( 0.0f, 1.0f, 0.0f, 1.0f ) );
    
    pWindowObj->pDeviceObj->pTxtHelper->End();
}

//-----------------------------------------------------------
// Render scene to all monitors
//-----------------------------------------------------------
void RenderToAllMonitors( double fTime, float fElapsedTime )
{
    // Clear them all
    for( int w = 0; w < g_WindowObjects.GetSize(); w++ )
    {
        WINDOW_OBJECT* pWindowObj = g_WindowObjects.GetAt( w );

        // set the render target
        pWindowObj->pDeviceObj->pDevice->OMSetRenderTargets( 1, 
                       &pWindowObj->pRenderTargetView,
                       pWindowObj->pDepthStencilView );
        // set the viewport
        D3D10_VIEWPORT Viewport;
        Viewport.TopLeftX = 0;
        Viewport.TopLeftY = 0;
        Viewport.Width = pWindowObj->Width;
        Viewport.Height = pWindowObj->Height;
        Viewport.MinDepth = 0.0f;
        Viewport.MaxDepth = 1.0f;
        pWindowObj->pDeviceObj->pDevice->RSSetViewports( 1, &Viewport );

        // Call the render function
        OnD3D10FrameRender( pWindowObj, fTime, fElapsedTime );
    }
}

//--------------------------------------------------------------
// Render scene to all monitors
//--------------------------------------------------------------
void PresentToAllMonitors()
{
    for( int w = 0; w < g_WindowObjects.GetSize(); w++ )
    {
        WINDOW_OBJECT* pWindowObj = g_WindowObjects.GetAt( w );
        pWindowObj->pSwapChain->Present( 0, 0 );
    }
}


void DrawFullScreenQuad10( DEVICE_OBJECT* pDeviceObj, 
         ID3D10EffectTechnique* pTech, UINT Width, UINT Height )
{
    // Save the Old viewport
    ID3D10Device* pd3dDevice = pDeviceObj->pDevice;
    D3D10_VIEWPORT vpOld[D3D10_VIEWPORT_AND_SCISSORRECT_MAX_INDEX];
    UINT nViewPorts = 1;
    pd3dDevice->RSGetViewports( &nViewPorts, vpOld );

    // Setup the viewport to match the backbuffer
    D3D10_VIEWPORT vp;
    vp.Width = Width;
    vp.Height = Height;
    vp.MinDepth = 0.0f;
    vp.MaxDepth = 1.0f;
    vp.TopLeftX = 0;
    vp.TopLeftY = 0;
    pd3dDevice->RSSetViewports( 1, &vp );

    UINT strides = sizeof( SCREEN_VERTEX );
    UINT offsets = 0;
    ID3D10Buffer* pBuffers[1] = { pDeviceObj->pScreenQuadVB };

    pd3dDevice->IASetInputLayout( pDeviceObj->pQuadLayout );
    pd3dDevice->IASetVertexBuffers( 0, 1, pBuffers, &strides, &offsets );
    pd3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );

    D3D10_TECHNIQUE_DESC techDesc;
    pTech->GetDesc( &techDesc );

    for( UINT uiPass = 0; uiPass < techDesc.Passes; uiPass++ )
    {
        pTech->GetPassByIndex( uiPass )->Apply( 0 );

        pd3dDevice->Draw( 4, 0 );
    }

    // Restore the Old viewport
    pd3dDevice->RSSetViewports( nViewPorts, vpOld );
}

这段代码,与其他任何 3D 游戏技术代码一样,看起来非常复杂。然而,这段代码的部分内容可以被分解,以阐明它的工作原理。在我们开始初始化 Direct3D 之前,让我们简要说明一下文件顶部的内容。在头文件下方是全局声明。在这些全局声明下方是函数原型。这些以及其他一些数据结构将被列出,直到我们到达程序入口点 WinMain() 函数。

初始化 Direct3D 的过程首先通过填写 DXGI_SWAP_CHAIN_DESC 结构实例来描述要创建的交换链的特性。这以及接下来的步骤列在下面。

  • 使用 D3D10CreateDeviceAndSwapChain 函数创建 ID3D10DeviceIDXGISwapChain 接口。
  • 为交换链的后缓冲创建一个渲染目标视图。
  • 创建深度/模板缓冲区及其关联的深度/模板视图。
  • 将渲染目标视图和深度/模板视图绑定到渲染管线的输出合并阶段,以便 Direct3D 可以使用它们。
  • 设置视口。

描述交换链

再次强调,初始化 Direct3D 从填写 DXGI_SWAP_CHAIN_DESC 结构实例开始,该结构描述了我们要创建的交换链的特性。这是一个通用的结构。

typedef struct DXGI_SWAP_CHAIN_DESC {
  DXGI_MODE_DESC BufferDesc;
  DXGI_SAMPLE_DESC SampleDesc;
  DXGI_USAGE BufferUsage;
  UINT BufferCount;
  HWND OutputWindow;
  BOOL Windowed;
  DXGI_SWAP_EFFECT SwapEffect;
  UINT Flags;
 } DXGI_SWAP_CHAIN_DESC;

DXGI_MODE_DESC 类型是另一个结构,定义为:

typedef struct DXGI_MODE_DESC
{
    UINT Width;                                 // desired back buffer width
    UINT Height;                                // desired back buffer height
    DXGI_RATIONAL RefreshRate;      // display mode refresh rate
    DXGI_FORMAT Format;                         // back buffer pixel format
    DXGI_MODE_SCANLINE_ORDER ScanlineOrdering; // display scanline mode
    DXGI_MODE_SCALING Scaling;                 // display scaling mode
} DXGI_MODE_DESC;

下面的代码取自我们的示例,展示了如何填充 DXGI_SWAP_CHAIN_DESC 结构。

// create a swap chain
DXGI_SWAP_CHAIN_DESC SwapChainDesc;
ZeroMemory( &SwapChainDesc, sizeof( DXGI_SWAP_CHAIN_DESC ) );
SwapChainDesc.BufferDesc.Width = pWindowObj->Width;
SwapChainDesc.BufferDesc.Height = pWindowObj->Height;
SwapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
SwapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
SwapChainDesc.BufferDesc.Format = g_dispFormat; 
SwapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
SwapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
SwapChainDesc.SampleDesc.Count = 1;
SwapChainDesc.SampleDesc.Quality = 0;
SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
SwapChainDesc.BufferCount = 3;
SwapChainDesc.OutputWindow = pWindowObj->hWnd;
SwapChainDesc.Windowed = ( g_bFullscreen == FALSE);
SwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
SwapChainDesc.Flags = 0;

创建设备和交换链

在我们通过填充 DXGI_SWAP_CHAIN_DESC 结构来描述要创建的交换链之后,我们就可以创建 Direct3D 10 设备 (ID3D10Device) 和交换链 (IDXGISwapChain) 了。ID3D10Device 接口是主要的 Direct3D 接口,可以看作是我们对物理图形设备硬件的软件控制器;也就是说,通过此接口,我们可以与硬件交互并指示它执行操作(例如清除后缓冲、将资源绑定到各种管线阶段以及绘制几何图形)。设备和交换链可以使用以下函数创建。

hr = g_pDXGIFactory->CreateSwapChain( pDXGIDevice, 
         &SwapChainDesc, &pWindowObj > pSwapChain );
pDXGIDevice->Release();
pDXGIDevice = NULL;

创建深度/模板缓冲区

我们将不详细介绍深度模板缓冲区。但目前,我们需要设置一个,以便能够正确渲染。重要的是要知道,深度缓冲区用于提供像素级的精确深度测试,这样当您将一个物体渲染在另一个物体前面时,它们就不会出现混乱。模板缓冲区用于高级效果,如体积阴影。要创建深度/模板缓冲区,我们首先创建一个与后缓冲分辨率相同的 2D 纹理。我们通过填写 D3D10_TEXTURE2D_DESC 结构来完成此操作,我们将这样填写:

D3D10_TEXTURE2D_DESC descDepth;
ZeroMemory(&descDepth, sizeof(descDepth));
descDepth.Width = m_rcScreenRect.right;
descDepth.Height = m_rcScreenRect.bottom;
descDepth.MipLevels = 1;
descDepth.ArraySize = 1;
descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
descDepth.SampleDesc.Count = 1;
descDepth.SampleDesc.Quality = 0;
descDepth.Usage = D3D10_USAGE_DEFAULT;
descDepth.BindFlags = D3D10_BIND_DEPTH_STENCIL;
descDepth.CPUAccessFlags = 0;
descDepth.MiscFlags = 0;

要点是宽度和高度与后缓冲的大小完全相同。另外请注意,格式设置为 DXGI_FORMAT_D24_UNORM_S8_UINT,用英语来说就是 32 位缓冲区,其中 24 位分配给深度缓冲区,8 位分配给模板缓冲区。该缓冲区存储无符号整数数据。填充完结构后,我们可以将其作为参数传递给 ID3D10Device::CreateTexture2D(),其原型如下。

HRESULT CreateTexture2D(
const D3D10_TEXTURE2D_DESC *pDesc,
const D3D10_SUBRESOURCE_DATA *pInitialData,
ID3D10Texture2D **ppTexture2D
);

第一个参数接受我们刚刚填写的 D3D10_TEXTURE2D_DESC 结构的地址。第二个参数接受用于加载纹理的初始数据,我们对此不感兴趣,因此可以将其设置为 NULL。第三个参数接受一个指向纹理的指针的地址,当纹理创建完成时,Direct3D 将填充此指针。请注意示例代码,它以“MiscFlags”结束,然后后跟调用。

//------------------------------------------------------
// Creates a render target view and depth stencil
// surface/view per swapchain
//------------------------------------------------------
HRESULT CreateViewsForWindowObject( WINDOW_OBJECT* pWindowObj )
{
    HRESULT hr = S_OK;

    // get the backbuffer
    ID3D10Texture2D* pBackBuffer = NULL;
    hr = pWindowObj->pSwapChain->GetBuffer( 0, IID_ID3D10Texture2D, 
                   ( void** )&pBackBuffer );
    if( FAILED( hr ) )
        return hr;

    // get the backbuffer desc
    D3D10_TEXTURE2D_DESC BBDesc;
    pBackBuffer->GetDesc( &BBDesc );

    // create the render target view
    D3D10_RENDER_TARGET_VIEW_DESC RTVDesc;
    RTVDesc.Format = BBDesc.Format;
    RTVDesc.ViewDimension = D3D10_RTV_DIMENSION_TEXTURE2D;
    RTVDesc.Texture2D.MipSlice = 0;
    hr = pWindowObj->pDeviceObj->pDevice->CreateRenderTargetView( 
              pBackBuffer, &RTVDesc,
              &pWindowObj->pRenderTargetView );
    pBackBuffer->Release();
    pBackBuffer = NULL;
    if( FAILED( hr ) )
        return hr;

    // Create depth stencil texture
    ID3D10Texture2D* pDepthStencil = NULL;
    D3D10_TEXTURE2D_DESC descDepth;
    descDepth.Width = pWindowObj->Width;
    descDepth.Height = pWindowObj->Height;
    descDepth.MipLevels = 1;
    descDepth.ArraySize = 1;
    descDepth.Format = DXGI_FORMAT_D16_UNORM;
    descDepth.SampleDesc.Count = 1;
    descDepth.SampleDesc.Quality = 0;
    descDepth.Usage = D3D10_USAGE_DEFAULT;
    descDepth.BindFlags = D3D10_BIND_DEPTH_STENCIL;
    descDepth.CPUAccessFlags = 0;
    descDepth.MiscFlags = 0;
    hr = pWindowObj->pDeviceObj->pDevice->CreateTexture2D( 
               &descDepth, NULL, &pDepthStencil );
    if( FAILED( hr ) )
        return hr;

    // Create the depth stencil view
    D3D10_DEPTH_STENCIL_VIEW_DESC descDSV;
    descDSV.Format = descDepth.Format;
    descDSV.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D;
    descDSV.Texture2D.MipSlice = 0;
    hr = pWindowObj->pDeviceObj->pDevice->CreateDepthStencilView( 
             pDepthStencil, &descDSV,
             &pWindowObj->pDepthStencilView );
    SAFE_RELEASE( pDepthStencil );
    if( FAILED( hr ) )
        return hr;

    // get various information
    if( pWindowObj->pAdapter )
        pWindowObj->pAdapter->GetDesc( &pWindowObj->AdapterDesc );
    if( pWindowObj->pOutput )
        pWindowObj->pOutput->GetDesc( &pWindowObj->OutputDesc );

    return hr;
}

创建视口

视口定义了您的后缓冲的哪个区域将被渲染。我们想渲染到整个缓冲区;但是,您可以轻松地更改这些设置以渲染到缓冲区的不同部分。视口还定义了将用于深度缓冲区的最小和最大深度。设置视口比创建深度模板缓冲区容易得多。第一步是填写一个 D3D10_VIEWPORT 结构,它看起来像这样。

typedef struct D3D10_VIEWPORT {
INT TopLeftX;
INT TopLeftY;
UINT Width;
UINT Height;
FLOAT MinDepth;
FLOAT MaxDepth;
} D3D10_VIEWPORT;

现在检查示例代码。

// set the viewport
D3D10_VIEWPORT Viewport;
Viewport.TopLeftX = 0;
Viewport.TopLeftY = 0;
Viewport.Width = pWindowObj->Width;
Viewport.Height = pWindowObj->Height;
Viewport.MinDepth = 0.0f;
Viewport.MaxDepth = 1.0f;
pWindowObj->pDeviceObj->pDevice->RSSetViewports( 1, &Viewport );

// Call the render function
OnD3D10FrameRender( pWindowObj, fTime, fElapsedTime );

源代码文件加载在 GPU 上执行的 FX 文件。让我们从加载和编译 FX 文件开始。要做到这一点,我们使用函数 D3DX10CreateEffectFromFile(),其原型如下。

HRESULT D3DX10CreateEffectFromFile(
LPCWSTR pFileName,
CONST D3D10_SHADER_MACRO *pDefines,
ID3D10Include *pInclude,
LPCSTR pProfile,
UINT HLSLFlags,
UINT FXFlags,
ID3D10Device *pDevice,
ID3D10EffectPool *pEffectPool,
ID3DX10ThreadPump *pPump,
ID3D10Effect **ppEffect,
ID3D10Blob **ppErrors,
HRESULT *pHResult
);

这是示例代码创建效果的技术。

// Load the effect file
WCHAR str[MAX_PATH];
V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"10BitScanout10.fx" ) );
DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;

#if defined( DEBUG ) || defined( _DEBUG )
    
dwShaderFlags |= D3D10_SHADER_DEBUG;
#endif
V_RETURN( D3DX10CreateEffectFromFile( str, NULL, NULL, "fx_4_0", 
          dwShaderFlags, 0, pDeviceObj->pDevice, NULL, NULL, 
          &pDeviceObj->pEffect, NULL, NULL ) );

pDeviceObj->pRender = pDeviceObj->pEffect->GetTechniqueByName( "Render" );
pDeviceObj->pTechQuad = pDeviceObj->pEffect->GetTechniqueByName( "RenderQuad" );

pDeviceObj->pmWorldViewProjection = 
  pDeviceObj->pEffect->GetVariableByName( "g_mWorldViewProjection" )->AsMatrix();
pDeviceObj->pmWorld = pDeviceObj->pEffect->GetVariableByName( "g_mWorld" )->AsMatrix(); 

// Create an input layout
const D3D10_INPUT_ELEMENT_DESC layout[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};
D3D10_PASS_DESC PassDesc;
pDeviceObj->pRender->GetPassByIndex( 0 )->GetDesc( &PassDesc );
V_RETURN( pDeviceObj->pDevice->CreateInputLayout( 
              layout, sizeof( layout ) / sizeof( layout[0] ),
              PassDesc.pIAInputSignature,
              PassDesc.IAInputSignatureSize, &pDeviceObj->pInputLayout ) );

// Create a shape
DXUTCreateTeapot( pDeviceObj->pDevice, &pDeviceObj->pMesh );

// Create a screen quad for all render to texture operations
SCREEN_VERTEX svQuad[4];
svQuad[0].pos = D3DXVECTOR4( -1.0f, 1.0f, 0.5f, 1.0f );
svQuad[0].tex = D3DXVECTOR2( 0.0f, 0.0f );
svQuad[1].pos = D3DXVECTOR4( 1.0f, 1.0f, 0.5f, 1.0f );
svQuad[1].tex = D3DXVECTOR2( 1.0f, 0.0f );
svQuad[2].pos = D3DXVECTOR4( -1.0f, -1.0f, 0.5f, 1.0f );
svQuad[2].tex = D3DXVECTOR2( 0.0f, 1.0f );
svQuad[3].pos = D3DXVECTOR4( 1.0f, -1.0f, 0.5f, 1.0f );
svQuad[3].tex = D3DXVECTOR2( 1.0f, 1.0f );

D3D10_BUFFER_DESC vbdesc =
{
    4 * sizeof( SCREEN_VERTEX ),
    D3D10_USAGE_DEFAULT,
    D3D10_BIND_VERTEX_BUFFER,
    0,
    0
};

D3D10_SUBRESOURCE_DATA InitData;
InitData.pSysMem = svQuad;
InitData.SysMemPitch = 0;
InitData.SysMemSlicePitch = 0;
V_RETURN( pDeviceObj->pDevice->CreateBuffer( &vbdesc, 
          &InitData, &(pDeviceObj->pScreenQuadVB) ) );

// Create our quad input layout
const D3D10_INPUT_ELEMENT_DESC quadlayout[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 16, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};
V_RETURN( pDeviceObj->pTechQuad->GetPassByIndex( 0 )->GetDesc( &PassDesc ) );
V_RETURN( pDeviceObj->pDevice->CreateInputLayout( quadlayout, 2, 
          PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, 
          &(pDeviceObj->pQuadLayout) ) );
... etc ...

这并没有涵盖所有的代码,但现在我们可以查看大量的 DirectX 代码并理解它的作用。重要的是要记住,Direct3D API 是 COM 库的集合。COM 编程实践使用大量指针,并在使用完设备(或对象)后始终清理并移除它。下一个示例称为延迟粒子(deferred particles),是 DirectX 代码的许多粒子系统示例之一。粒子系统可以创建烟雾、火焰、雨、血、水和其他下雨的元素。极其微小的粒子从源(或发射器)流出,要么聚集形成一股流,要么爆炸以产生光。

粒子系统

查看这个延迟粒子系统示例。

def.JPG

这是主源文件。

-----------------
#include "DXUT.h"
#include "DXUTcamera.h"
#include "DXUTgui.h"
#include "DXUTsettingsDlg.h"
#include "SDKmisc.h"
#include "SDKMesh.h"
#include "resource.h"
#include "ParticleSystem.h"
#include "BreakableWall.h"

//----------------------------------------------------------
// Global variables
//----------------------------------------------------------
// manager for shared resources of dialogs
CDXUTDialogResourceManager          g_DialogResourceManager;
// A model viewing camer
CModelViewerCamera                  g_Camera;
CDXUTDirectionWidget                g_LightControl;
// Device settings dialog
CD3DSettingsDlg                     g_D3DSettingsDlg;
CDXUTDialog                         g_HUD; // manages the 3D
// dialog for sample specific controls
CDXUTDialog                         g_SampleUI;

// Direct3D10 resources
CDXUTTextHelper*                    g_pTxtHelper = NULL;
ID3DX10Font*                        g_pFont10 = NULL;
ID3DX10Sprite*                      g_pSprite10 = NULL;
CDXUTSDKMesh                        g_WallMesh;
CDXUTSDKMesh g_ChunkMesh[NUM_CHUNKS];

#define MAX_BUILDINGS 5
CBuilding g_Building[MAX_BUILDINGS];
CGrowableArray <D3DXMATRIX>         g_BaseMeshMatrices;
CGrowableArray <D3DXMATRIX> g_ChunkMeshMatrices[NUM_CHUNKS];

ID3D10InputLayout*                  g_pVertexLayout = NULL;
ID3D10InputLayout*                  g_pScreenQuadLayout = NULL;
ID3D10InputLayout*                  g_pMeshLayout = NULL;

UINT                                g_NumParticles = 200;
float                               g_fSpread = 4.0f;
float                               g_fStartSize = 0.0f;
float                               g_fEndSize = 10.0f;
float                               g_fSizeExponent = 128.0f;

float                               g_fMushroomCloudLifeSpan = 10.0f;
float                               g_fGroundBurstLifeSpan = 9.0f;
float                               g_fPopperLifeSpan = 9.0f;


float                               g_fMushroomStartSpeed = 20.0f;
float                               g_fStalkStartSpeed = 50.0f;
float                               g_fGroundBurstStartSpeed = 100.0f;
float                               g_fLandMineStartSpeed = 250.0f;

float                               g_fEndSpeed = 4.0f;
float                               g_fSpeedExponent = 32.0f;
float                               g_fFadeExponent = 4.0f;
float                               g_fRollAmount = 0.2f;
float                               g_fWindFalloff = 20.0f;
D3DXVECTOR3                         g_vPosMul( 1,1,1 );
D3DXVECTOR3                         g_vDirMul( 1,1,1 );
D3DXVECTOR3                         g_vWindVel( -2.0f,10.0f,0 );
D3DXVECTOR3                         g_vGravity( 0,-9.8f,0.0f );

float                               g_fGroundPlane = 0.5f;
float                               g_fLightRaise = 1.0f;

float                               g_fWorldBounds = 100.0f;

#define MAX_FLASH_COLORS 4
D3DXVECTOR4 g_vFlashColor[MAX_FLASH_COLORS] =
{
    D3DXVECTOR4( 1.0f, 0.5f, 0.00f, 0.9f ),
    D3DXVECTOR4( 1.0f, 0.3f, 0.05f, 0.9f ),
    D3DXVECTOR4( 1.0f, 0.4f, 0.00f, 0.9f ),
    D3DXVECTOR4( 0.8f, 0.3f, 0.05f, 0.9f )
};

D3DXVECTOR4                         g_vFlashAttenuation( 0,0.0f,3.0f,0 );
D3DXVECTOR4                         g_vMeshLightAttenuation( 0,0,1.5f,0 );
float                               g_fFlashLife = 0.50f;
float                               g_fFlashIntensity = 1000.0f;

UINT                                g_NumParticlesToDraw = 0;
/*#define MAX_MUSHROOM_CLOUDS 16
   #define MAX_GROUND_BURSTS 46
   #define MAX_PARTICLE_SYSTEMS 60
   #define MAX_FLASH_LIGHTS 8
   #define MAX_INSTANCES 200*/
#define MAX_MUSHROOM_CLOUDS 8
#define MAX_GROUND_BURSTS 23
#define MAX_PARTICLE_SYSTEMS 30
#define MAX_FLASH_LIGHTS 8
#define MAX_INSTANCES 200

CParticleSystem**                   g_ppParticleSystem = NULL;
ID3D10Buffer*                       g_pParticleBuffer = NULL;
ID3D10Buffer*                       g_pScreenQuadVB = NULL;

ID3D10Texture2D*                    g_pOffscreenParticleTex = NULL;
ID3D10ShaderResourceView*           g_pOffscreenParticleSRV = NULL;
ID3D10RenderTargetView*             g_pOffscreenParticleRTV = NULL;
ID3D10Texture2D*                    g_pOffscreenParticleColorTex = NULL;
ID3D10ShaderResourceView*           g_pOffscreenParticleColorSRV = NULL;
ID3D10RenderTargetView*             g_pOffscreenParticleColorRTV = NULL;

ID3D10ShaderResourceView*           g_pParticleTextureSRV = NULL;

ID3D10Effect*                       g_pEffect10 = NULL;
ID3D10EffectTechnique*              g_pRenderParticlesToBuffer = NULL;
ID3D10EffectTechnique*              g_pRenderParticles = NULL;
ID3D10EffectTechnique*              g_pCompositeParticlesToScene = NULL;
ID3D10EffectTechnique*              g_pRenderMesh = NULL;
ID3D10EffectTechnique*              g_pRenderMeshInst = NULL;

ID3D10EffectShaderResourceVariable* g_ptxDiffuse = NULL;
ID3D10EffectShaderResourceVariable* g_ptxParticleColor = NULL;
ID3D10EffectVectorVariable*         g_pLightDir = NULL;
ID3D10EffectMatrixVariable*         g_pmWorldViewProjection = NULL;
ID3D10EffectMatrixVariable*         g_pmWorld = NULL;
ID3D10EffectMatrixVariable*         g_pmInvViewProj = NULL;
ID3D10EffectScalarVariable*         g_pfTime = NULL;
ID3D10EffectVectorVariable*         g_pvEyePt = NULL;
ID3D10EffectVectorVariable*         g_pvRight = NULL;
ID3D10EffectVectorVariable*         g_pvUp = NULL;
ID3D10EffectVectorVariable*         g_pvForward = NULL;

ID3D10EffectScalarVariable*         g_pNumGlowLights = NULL;
ID3D10EffectVectorVariable*         g_pvGlowLightPosIntensity = NULL;
ID3D10EffectVectorVariable*         g_pvGlowLightColor = NULL;
ID3D10EffectVectorVariable*         g_pvGlowLightAttenuation = NULL;
ID3D10EffectVectorVariable*         g_pvMeshLightAttenuation = NULL;

ID3D10EffectMatrixVariable*         g_pmViewProj = NULL;
ID3D10EffectMatrixVariable*         g_pmWorldInst = NULL;

bool                                g_bRenderDeferred = true;

//-----------------------------------------------------------
// UI control IDs
//-----------------------------------------------------------
#define IDC_TOGGLEFULLSCREEN    1
#define IDC_TOGGLEREF           3
#define IDC_CHANGEDEVICE        4
#define IDC_RESET                50
#define IDC_DEFERRED            51
#define IDC_TOGGLEWARP          5

//-----------------------------------------------------------
// Forward declarations 
//-----------------------------------------------------------
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, 
                                    void* pUserContext );
void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext );
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, 
                          LPARAM lParam, bool* pbNoFurtherProcessing,
                          void* pUserContext );
void CALLBACK OnKeyboard( UINT nChar, bool bKeyDown, 
                          bool bAltDown, void* pUserContext );
void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, 
                          CDXUTControl* pControl, void* pUserContext );

bool CALLBACK IsD3D10DeviceAcceptable( UINT Adapter, UINT Output, 
              D3D10_DRIVER_TYPE DeviceType,
              DXGI_FORMAT BackBufferFormat, 
              bool bWindowed, void* pUserContext );
HRESULT CALLBACK OnD3D10CreateDevice( ID3D10Device* pd3dDevice, 
       const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc,
       void* pUserContext );
HRESULT CALLBACK OnD3D10ResizedSwapChain( 
        ID3D10Device* pd3dDevice, IDXGISwapChain* pSwapChain,
        const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
void CALLBACK OnD3D10ReleasingSwapChain( void* pUserContext );
void CALLBACK OnD3D10DestroyDevice( void* pUserContext );
void CALLBACK OnD3D10FrameRender( ID3D10Device* pd3dDevice, 
     double fTime, float fElapsedTime, void* pUserContext );

void InitApp();
void RenderText();

void ResetBuildings();


//------------------------------------------------------
// Entry point to the program. Initializes everything
// and goes into a message processing 
// loop. Idle time is used to render the scene.
//------------------------------------------------------
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                     LPWSTR lpCmdLine, int nCmdShow )
{
    // Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

    // DXUT will create and use the best device (either D3D9 or D3D10) 
    // that is available on the system depending
    // on which D3D callbacks are set below

    DXUTGetD3D10Enumeration( false, true );
    

    // Set DXUT callbacks
    DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );
    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackKeyboard( OnKeyboard );
    DXUTSetCallbackFrameMove( OnFrameMove );

    DXUTSetCallbackD3D10DeviceAcceptable( IsD3D10DeviceAcceptable );
    DXUTSetCallbackD3D10DeviceCreated( OnD3D10CreateDevice );
    DXUTSetCallbackD3D10SwapChainResized( OnD3D10ResizedSwapChain );
    DXUTSetCallbackD3D10FrameRender( OnD3D10FrameRender );
    DXUTSetCallbackD3D10SwapChainReleasing( OnD3D10ReleasingSwapChain );
    DXUTSetCallbackD3D10DeviceDestroyed( OnD3D10DestroyDevice );

    InitApp();
    // Parse the command line, show msgboxes
    // on error, no extra command line params
    DXUTInit( true, true, NULL );
    // Show the cursor and clip it when in full screen
    DXUTSetCursorSettings( true, true );
    DXUTCreateWindow( L"DeferredParticles" );
    DXUTCreateDevice( true, 640, 480 );
    DXUTMainLoop(); // Enter into the DXUT render loop

    return DXUTGetExitCode();
}


//----------------------------------------------------------
// Initialize the app 
//----------------------------------------------------------
void InitApp()
{
    D3DXVECTOR3 vDir( 1,1,0 );
    D3DXVec3Normalize( &vDir, &vDir );
    g_LightControl.SetLightDirection( vDir );

    // Initialize dialogs
    g_D3DSettingsDlg.Init( &g_DialogResourceManager );
    g_HUD.Init( &g_DialogResourceManager );
    g_SampleUI.Init( &g_DialogResourceManager );

    g_HUD.SetCallback( OnGUIEvent ); int iY = 10;
    
    g_SampleUI.SetCallback( OnGUIEvent ); iY = 10;

    iY += 24;
    
    // Setup the camera's view parameters
    D3DXVECTOR3 vecEye( 0.0f, 150.0f, 336.0f );
    D3DXVECTOR3 vecAt ( 0.0f, 0.0f, 0.0f );
    g_Camera.SetViewParams( &vecEye, &vecAt );
}


//-----------------------------------------------------------
// Called right before creating a D3D9 or D3D10 device,
// allowing the app to modify the device settings as needed
//-----------------------------------------------------------
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* 
              pDeviceSettings, void* pUserContext )
{

    // Disable vsync
    pDeviceSettings->d3d10.SyncInterval = 0;
    g_D3DSettingsDlg.GetDialogControl()->GetComboBox( 
          DXUTSETTINGSDLG_PRESENT_INTERVAL )->SetEnabled( false );
    // Disable multisampling (not implemented for this sample)
    g_D3DSettingsDlg.GetDialogControl()->GetComboBox( 
          DXUTSETTINGSDLG_D3D10_MULTISAMPLE_COUNT )->SetEnabled( false );
    g_D3DSettingsDlg.GetDialogControl()->GetComboBox( 
          DXUTSETTINGSDLG_D3D10_MULTISAMPLE_QUALITY )->SetEnabled( false );

    // For the first device created if its a REF device,
    // optionally display a warning dialog box
    static bool s_bFirstTime = true;
    if( s_bFirstTime )
    {
        s_bFirstTime = false;
        if( ( DXUT_D3D9_DEVICE == pDeviceSettings->ver && 
              pDeviceSettings->d3d9.DeviceType == D3DDEVTYPE_REF ) ||
            ( DXUT_D3D10_DEVICE == pDeviceSettings->ver &&
              pDeviceSettings->d3d10.DriverType == D3D10_DRIVER_TYPE_REFERENCE ) )
            DXUTDisplaySwitchingToREFWarning( pDeviceSettings->ver );
    }

    return true;
}

void NewExplosion( D3DXVECTOR3 vCenter, float fSize )
{
    D3DXVECTOR3 vDirMul( 0.2f,1.0f,0.2f );
    float fMinPower = 5.0f;
    float fMaxPower = 30.0f;
    for( UINT i = 0; i < MAX_BUILDINGS; i++ )
    {
        g_Building[i].CreateExplosion( 
          vCenter, vDirMul, fSize, fMinPower, fMaxPower );
    }
}

//--------------------------------------------------
// Handle updates to the scene. This is called
// regardless of which D3D API is used
//--------------------------------------------------
void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext )
{
    // Update the camera's position based on user input 
    g_Camera.FrameMove( fElapsedTime );
    if (fElapsedTime  > 0.1f ) fElapsedTime = 0.1f;

    D3DXVECTOR3 vEye;
    D3DXMATRIX mView;
    vEye = *g_Camera.GetEyePt();
    mView = *g_Camera.GetViewMatrix();
    D3DXVECTOR3 vRight( mView._11, mView._21, mView._31 );
    D3DXVECTOR3 vUp( mView._12, mView._22, mView._32 );
    D3DXVECTOR3 vFoward( mView._13, mView._23, mView._33 );

    D3DXVec3Normalize( &vRight, &vRight );
    D3DXVec3Normalize( &vUp, &vUp );
    D3DXVec3Normalize( &vFoward, &vFoward );

    g_pvRight->SetFloatVector( ( float* )&vRight );
    g_pvUp->SetFloatVector( ( float* )&vUp );
    g_pvForward->SetFloatVector( ( float* )&vFoward );

    UINT NumActiveSystems = 0;
    D3DXVECTOR4 vGlowLightPosIntensity[MAX_PARTICLE_SYSTEMS];
    D3DXVECTOR4 vGlowLightColor[MAX_PARTICLE_SYSTEMS];

    // Advanced building pieces
    for( UINT i = 0; i < MAX_BUILDINGS; i++ )
    {
        g_Building[i].AdvancePieces( fElapsedTime, g_vGravity );
    }

    // Advance the system
    for( UINT i = 0; i < MAX_PARTICLE_SYSTEMS; i++ )
    {
        g_ppParticleSystem[i]->AdvanceSystem( ( float )fTime, 
                fElapsedTime, vRight, vUp, g_vWindVel, g_vGravity );
    }

    PARTICLE_VERTEX* pVerts = NULL;
    g_pParticleBuffer->Map( D3D10_MAP_WRITE_DISCARD, 0, ( void** )&pVerts );

    CopyParticlesToVertexBuffer( pVerts, vEye, vRight, vUp );

    g_pParticleBuffer->Unmap();

    for( UINT i = 0; i < MAX_MUSHROOM_CLOUDS; i += 2 )
    {
        float fCurrentTime = g_ppParticleSystem[i]->GetCurrentTime();
        float fLifeSpan = g_ppParticleSystem[i]->GetLifeSpan();
        if( fCurrentTime > fLifeSpan )
        {
            D3DXVECTOR3 vCenter;
            vCenter.x = RPercent() * g_fWorldBounds;
            vCenter.y = g_fGroundPlane;
            vCenter.z = RPercent() * g_fWorldBounds;
            float fStartTime = -fabs( RPercent() ) * 4.0f;
            D3DXVECTOR4 vFlashColor = g_vFlashColor[ rand() % MAX_FLASH_COLORS ];

            g_ppParticleSystem[i]->SetCenter( vCenter );
            g_ppParticleSystem[i]->SetStartTime( fStartTime );
            g_ppParticleSystem[i]->SetFlashColor( vFlashColor );
            g_ppParticleSystem[i]->Init();

            g_ppParticleSystem[i + 1]->SetCenter( vCenter );
            g_ppParticleSystem[i + 1]->SetStartTime( fStartTime );
            g_ppParticleSystem[i + 1]->SetFlashColor( vFlashColor );
            g_ppParticleSystem[i + 1]->Init();
        }
        else if( fCurrentTime > 0.0f && fCurrentTime < 
                   g_fFlashLife && NumActiveSystems < MAX_FLASH_LIGHTS )
        {
            D3DXVECTOR3 vCenter = g_ppParticleSystem[i]->GetCenter();
            D3DXVECTOR4 vFlashColor = g_ppParticleSystem[i]->GetFlashColor();

            float fIntensity = 
              g_fFlashIntensity * ( ( g_fFlashLife - fCurrentTime ) / g_fFlashLife );
            vGlowLightPosIntensity[NumActiveSystems] = 
               D3DXVECTOR4( vCenter.x, vCenter.y + g_fLightRaise, 
                            vCenter.z, fIntensity );
            vGlowLightColor[NumActiveSystems] = vFlashColor;
            NumActiveSystems ++;
        }
    }

    // Ground bursts
    for( UINT i = MAX_MUSHROOM_CLOUDS; i < MAX_GROUND_BURSTS; i++ )
    {
        float fCurrentTime = g_ppParticleSystem[i]->GetCurrentTime();
        float fLifeSpan = g_ppParticleSystem[i]->GetLifeSpan();
        if( fCurrentTime > fLifeSpan )
        {
            D3DXVECTOR3 vCenter;
            vCenter.x = RPercent() * g_fWorldBounds;
            vCenter.y = g_fGroundPlane;
            vCenter.z = RPercent() * g_fWorldBounds;
            float fStartTime = -fabs( RPercent() ) * 4.0f;
            D3DXVECTOR4 vFlashColor = g_vFlashColor[ rand() % MAX_FLASH_COLORS ];

            float fStartSpeed = g_fGroundBurstStartSpeed + RPercent() * 30.0f;
            g_ppParticleSystem[i]->SetCenter( vCenter );
            g_ppParticleSystem[i]->SetStartTime( fStartTime );
            g_ppParticleSystem[i]->SetStartSpeed( fStartSpeed );
            g_ppParticleSystem[i]->SetFlashColor( vFlashColor );
            g_ppParticleSystem[i]->Init();
        }
        else if( fCurrentTime > 0.0f && fCurrentTime < 
                 g_fFlashLife && NumActiveSystems < MAX_FLASH_LIGHTS )
        {
            D3DXVECTOR3 vCenter = g_ppParticleSystem[i]->GetCenter();
            D3DXVECTOR4 vFlashColor = g_ppParticleSystem[i]->GetFlashColor();

            float fIntensity = g_fFlashIntensity * 
               ( ( g_fFlashLife - fCurrentTime ) / g_fFlashLife );
            vGlowLightPosIntensity[NumActiveSystems] = 
               D3DXVECTOR4( vCenter.x, vCenter.y + g_fLightRaise, vCenter.z,
                            fIntensity );
            vGlowLightColor[NumActiveSystems] = vFlashColor;
            NumActiveSystems ++;
        }
    }

    // Land mines
    for( UINT i = MAX_GROUND_BURSTS; i < MAX_PARTICLE_SYSTEMS; i++ )
    {
        float fCurrentTime = g_ppParticleSystem[i]->GetCurrentTime();
        float fLifeSpan = g_ppParticleSystem[i]->GetLifeSpan();
        if( fCurrentTime > fLifeSpan )
        {
            D3DXVECTOR3 vCenter;
            vCenter.x = RPercent() * g_fWorldBounds;
            vCenter.y = g_fGroundPlane;
            vCenter.z = RPercent() * g_fWorldBounds;
            float fStartTime = -fabs( RPercent() ) * 4.0f;
            D3DXVECTOR4 vFlashColor = g_vFlashColor[ rand() % MAX_FLASH_COLORS ];

            float fStartSpeed = g_fLandMineStartSpeed + RPercent() * 100.0f;
            g_ppParticleSystem[i]->SetCenter( vCenter );
            g_ppParticleSystem[i]->SetStartTime( fStartTime );
            g_ppParticleSystem[i]->SetStartSpeed( fStartSpeed );
            g_ppParticleSystem[i]->SetFlashColor( vFlashColor );
            g_ppParticleSystem[i]->Init();
        }
        else if( fCurrentTime > 0.0f && fCurrentTime < 
                 g_fFlashLife && NumActiveSystems < MAX_FLASH_LIGHTS )
        {
            D3DXVECTOR3 vCenter = g_ppParticleSystem[i]->GetCenter();
            D3DXVECTOR4 vFlashColor = g_ppParticleSystem[i]->GetFlashColor();

            float fIntensity = 
              g_fFlashIntensity * ( ( g_fFlashLife - fCurrentTime ) / g_fFlashLife );
            vGlowLightPosIntensity[NumActiveSystems] = 
              D3DXVECTOR4( vCenter.x, vCenter.y + g_fLightRaise, 
                           vCenter.z, fIntensity );
            vGlowLightColor[NumActiveSystems] = vFlashColor;
            NumActiveSystems ++;
        }
    }

    // Setup light variables
    g_pNumGlowLights->SetInt( NumActiveSystems );
    g_pvGlowLightPosIntensity->SetFloatVectorArray( 
      ( float* )&vGlowLightPosIntensity, 0, NumActiveSystems );
    g_pvGlowLightColor->SetFloatVectorArray( 
      ( float* )&vGlowLightColor, 0, NumActiveSystems );
    g_pvGlowLightAttenuation->SetFloatVector( 
      ( float* )&g_vFlashAttenuation );
    g_pvMeshLightAttenuation->SetFloatVector( 
      ( float* )&g_vMeshLightAttenuation );
}


//---------------------------------------------------------------------
// Render the help and statistics text
//---------------------------------------------------------------------
void RenderText()
{
    g_pTxtHelper->Begin();
    g_pTxtHelper->SetInsertionPos( 2, 0 );
    g_pTxtHelper->SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );
    
    g_pTxtHelper->End();
}


//---------------------------------------------------------------------
// Handle messages to the application
//---------------------------------------------------------------------
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, 
                          LPARAM lParam, bool* pbNoFurtherProcessing,
                          void* pUserContext )
{
    // Pass messages to dialog resource manager calls
    // so GUI state is updated correctly
    *pbNoFurtherProcessing = g_DialogResourceManager.MsgProc( 
                                  hWnd, uMsg, wParam, lParam );
    if( *pbNoFurtherProcessing )
        return 0;

    // Pass messages to settings dialog if its active
    if( g_D3DSettingsDlg.IsActive() )
    {
        g_D3DSettingsDlg.MsgProc( hWnd, uMsg, wParam, lParam );
        return 0;
    }

    // Give the dialogs a chance to handle the message first
    *pbNoFurtherProcessing = g_HUD.MsgProc( hWnd, uMsg, wParam, lParam );
    if( *pbNoFurtherProcessing )
        return 0;
    *pbNoFurtherProcessing = g_SampleUI.MsgProc( hWnd, uMsg, wParam, lParam );
    if( *pbNoFurtherProcessing )
        return 0;

    g_LightControl.HandleMessages( hWnd, uMsg, wParam, lParam );

    // Pass all remaining windows messages to camera
    // so it can respond to user input
    g_Camera.HandleMessages( hWnd, uMsg, wParam, lParam );

    return 0;
}


//------------------------------------------------------
// Handle key presses
//------------------------------------------------------
void CALLBACK OnKeyboard( UINT nChar, bool bKeyDown, 
              bool bAltDown, void* pUserContext )
{
}


//----------------------------------------------------------
// Handles the GUI events
//----------------------------------------------------------
void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, 
              CDXUTControl* pControl, void* pUserContext )
{
    switch( nControlID )
    {
        case IDC_TOGGLEFULLSCREEN:
            DXUTToggleFullScreen(); break;
        case IDC_TOGGLEREF:
            DXUTToggleREF(); break;
        case IDC_CHANGEDEVICE:
            g_D3DSettingsDlg.SetActive( !g_D3DSettingsDlg.IsActive() ); break;
        case IDC_TOGGLEWARP:
            DXUTToggleWARP(); break;
        case IDC_RESET:
            ResetBuildings();
            break;
        case IDC_DEFERRED:
            g_bRenderDeferred = !g_bRenderDeferred;
            break;
    }

}


//--------------------------------------------------------------------
// Reject any D3D10 devices that aren't acceptable by returning false
//--------------------------------------------------------------------
bool CALLBACK IsD3D10DeviceAcceptable( UINT Adapter, UINT Output, 
              D3D10_DRIVER_TYPE DeviceType,
              DXGI_FORMAT BackBufferFormat, 
              bool bWindowed, void* pUserContext )
{
    return true;
}


void ResetBuildings()
{
    float f2Third = 0.6666f;
    D3DXVECTOR3 vChunkOffsets[NUM_CHUNKS] =
    {
        D3DXVECTOR3( f2Third, -f2Third, 0.0f ),
        D3DXVECTOR3( -f2Third, f2Third, 0.0f ),
        D3DXVECTOR3( f2Third, f2Third, 0.0f ),
        D3DXVECTOR3( -f2Third, -f2Third, 0.0f ),
        D3DXVECTOR3( f2Third, 0, 0.0f ),
        D3DXVECTOR3( 0, f2Third, 0.0f ),
        D3DXVECTOR3( -f2Third, 0, 0.0f ),
        D3DXVECTOR3( 0, -f2Third, 0.0f ),
        D3DXVECTOR3( 0, 0, 0.0f )
    };

    for( UINT i = 0; i < MAX_BUILDINGS; i++ )
    {
        g_Building[i].SetBaseMesh( &g_WallMesh );
        for( UINT c = 0; c < NUM_CHUNKS; c++ )
        {
            g_Building[i].SetChunkMesh( 
                  c, &g_ChunkMesh[c], vChunkOffsets[c] );
        }
    }
}

//--------------------------------------------------------------------
// Create any D3D10 resources that aren't dependant on the back buffer
//--------------------------------------------------------------------
HRESULT CALLBACK OnD3D10CreateDevice( ID3D10Device* pd3dDevice, 
        const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc,
        void* pUserContext )
{
    HRESULT hr;

    V_RETURN( g_DialogResourceManager.OnD3D10CreateDevice( pd3dDevice ) );
    V_RETURN( g_D3DSettingsDlg.OnD3D10CreateDevice( pd3dDevice ) );
    V_RETURN( D3DX10CreateFont( pd3dDevice, 15, 0, FW_BOLD, 1, 
              FALSE, DEFAULT_CHARSET,
              OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 
              DEFAULT_PITCH | FF_DONTCARE,
              L"Arial", &g_pFont10 ) );
    V_RETURN( D3DX10CreateSprite( pd3dDevice, 512, &g_pSprite10 ) );
    g_pTxtHelper = new CDXUTTextHelper( NULL, NULL, g_pFont10, g_pSprite10, 15 );

    V_RETURN( CDXUTDirectionWidget::StaticOnD3D10CreateDevice( pd3dDevice ) );

    DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
    // Set the D3D10_SHADER_DEBUG flag to embed debug information in the shaders.
    // Setting this flag improves the shader debugging experience, but still allows 
    // the shaders to be optimized and to run exactly the way they will run in 
    // the release configuration of this program.
    dwShaderFlags |= D3D10_SHADER_DEBUG;
    #endif

    // Read the D3DX effect file
    WCHAR str[MAX_PATH];
    char strMaxGlowLights[MAX_PATH];
    char strMaxInstances[MAX_PATH];
    sprintf_s( strMaxGlowLights, MAX_PATH, "%d", MAX_FLASH_LIGHTS );
    sprintf_s( strMaxInstances, MAX_PATH, "%d", MAX_INSTANCES );
    D3D10_SHADER_MACRO macros[3] =
    {
        { "MAX_GLOWLIGHTS", strMaxGlowLights },
        { "MAX_INSTANCES", strMaxInstances },
        { NULL, NULL }
    };

    V_RETURN( DXUTFindDXSDKMediaFileCch( str, 
              MAX_PATH, L"DeferredParticles.fx" ) );
    V_RETURN( D3DX10CreateEffectFromFile( str, macros, NULL, 
              "fx_4_0", dwShaderFlags, 0, pd3dDevice, NULL,
              NULL, &g_pEffect10, NULL, NULL ) );

    // Obtain technique objects
    g_pRenderParticlesToBuffer = 
      g_pEffect10->GetTechniqueByName( "RenderParticlesToBuffer" );
    g_pRenderParticles = g_pEffect10->GetTechniqueByName( "RenderParticles" );
    g_pCompositeParticlesToScene = 
      g_pEffect10->GetTechniqueByName( "CompositeParticlesToScene" );
    g_pRenderMesh = g_pEffect10->GetTechniqueByName( "RenderMesh" );
    g_pRenderMeshInst = g_pEffect10->GetTechniqueByName( "RenderMeshInst" );

    // Obtain variables
    g_ptxDiffuse = 
      g_pEffect10->GetVariableByName( "g_txMeshTexture" )->AsShaderResource();
    g_ptxParticleColor = 
      g_pEffect10->GetVariableByName( "g_txParticleColor" )->AsShaderResource();
    g_pLightDir = g_pEffect10->GetVariableByName( "g_LightDir" )->AsVector();
    g_pmWorldViewProjection = 
      g_pEffect10->GetVariableByName( "g_mWorldViewProjection" )->AsMatrix();
    g_pmWorld = g_pEffect10->GetVariableByName( "g_mWorld" )->AsMatrix();
    g_pmInvViewProj = g_pEffect10->GetVariableByName( "g_mInvViewProj" )->AsMatrix();
    g_pfTime = g_pEffect10->GetVariableByName( "g_fTime" )->AsScalar();
    g_pvEyePt = g_pEffect10->GetVariableByName( "g_vEyePt" )->AsVector();
    g_pvRight = g_pEffect10->GetVariableByName( "g_vRight" )->AsVector();
    g_pvUp = g_pEffect10->GetVariableByName( "g_vUp" )->AsVector();
    g_pvForward = g_pEffect10->GetVariableByName( "g_vForward" )->AsVector();

    g_pNumGlowLights = 
      g_pEffect10->GetVariableByName( "g_NumGlowLights" )->AsScalar();
    g_pvGlowLightPosIntensity = 
      g_pEffect10->GetVariableByName( "g_vGlowLightPosIntensity" )->AsVector();
    g_pvGlowLightColor = 
       g_pEffect10->GetVariableByName( "g_vGlowLightColor" )->AsVector();
    g_pvGlowLightAttenuation = 
      g_pEffect10->GetVariableByName( "g_vGlowLightAttenuation" )->AsVector();
    g_pvMeshLightAttenuation = 
      g_pEffect10->GetVariableByName( "g_vMeshLightAttenuation" )->AsVector();

    g_pmWorldInst = g_pEffect10->GetVariableByName( "g_mWorldInst" )->AsMatrix();
    g_pmViewProj = g_pEffect10->GetVariableByName( "g_mViewProj" )->AsMatrix();

    // Create our vertex input layout
    const D3D10_INPUT_ELEMENT_DESC layout[] =
    {
        { "POSITION",  0, DXGI_FORMAT_R32G32B32_FLOAT, 
          0, 0,  D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "TEXCOORD",  0, DXGI_FORMAT_R32G32_FLOAT,    0, 
          12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "LIFE",      0, DXGI_FORMAT_R32_FLOAT,       0, 
          20, D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "THETA",     0, DXGI_FORMAT_R32_FLOAT,       0, 
          24, D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "COLOR",     0, DXGI_FORMAT_R8G8B8A8_UNORM,  0, 
          28, D3D10_INPUT_PER_VERTEX_DATA, 0 }
    };

    D3D10_PASS_DESC PassDesc;
    V_RETURN( g_pRenderParticlesToBuffer->GetPassByIndex( 0 )->GetDesc( 
                                          &PassDesc ) );
    V_RETURN( pd3dDevice->CreateInputLayout( 
                  layout, 5, PassDesc.pIAInputSignature,
                  PassDesc.IAInputSignatureSize, &g_pVertexLayout ) );

    const D3D10_INPUT_ELEMENT_DESC screenlayout[] =
    {
        { "POSITION",  0, DXGI_FORMAT_R32G32B32_FLOAT, 
          0, 0,  D3D10_INPUT_PER_VERTEX_DATA, 0 },
    };
    V_RETURN( g_pCompositeParticlesToScene->GetPassByIndex( 0 )->GetDesc( 
                                            &PassDesc ) );
    V_RETURN( pd3dDevice->CreateInputLayout( screenlayout, 1, 
                PassDesc.pIAInputSignature,
                PassDesc.IAInputSignatureSize, &g_pScreenQuadLayout ) );

    const D3D10_INPUT_ELEMENT_DESC meshlayout[] =
    {
        { "POSITION",  0, DXGI_FORMAT_R32G32B32_FLOAT, 
          0, 0,  D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "NORMAL",    0, DXGI_FORMAT_R32G32B32_FLOAT, 
          0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "TEXCOORD",  0, DXGI_FORMAT_R32_FLOAT, 
          0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0 }
    };
    V_RETURN( g_pRenderMesh->GetPassByIndex( 0 )->GetDesc( &PassDesc ) );
    V_RETURN( pd3dDevice->CreateInputLayout( meshlayout, 3, 
              PassDesc.pIAInputSignature,
              PassDesc.IAInputSignatureSize, &g_pMeshLayout ) );

    // Load the meshes
    V_RETURN( g_WallMesh.Create( pd3dDevice, 
                L"DeferredParticles\\wallsegment.sdkmesh" ) );
    V_RETURN( g_ChunkMesh[0].Create( pd3dDevice, 
                L"DeferredParticles\\wallchunk0.sdkmesh" ) );
    V_RETURN( g_ChunkMesh[1].Create( pd3dDevice, 
                L"DeferredParticles\\wallchunk1.sdkmesh" ) );
    V_RETURN( g_ChunkMesh[2].Create( pd3dDevice, 
               L"DeferredParticles\\wallchunk2.sdkmesh" ) );
    V_RETURN( g_ChunkMesh[3].Create( pd3dDevice, 
               L"DeferredParticles\\wallchunk3.sdkmesh" ) );
    V_RETURN( g_ChunkMesh[4].Create( pd3dDevice, 
               L"DeferredParticles\\wallchunk4.sdkmesh" ) );
    V_RETURN( g_ChunkMesh[5].Create( pd3dDevice, 
               L"DeferredParticles\\wallchunk5.sdkmesh" ) );
    V_RETURN( g_ChunkMesh[6].Create( pd3dDevice, 
               L"DeferredParticles\\wallchunk6.sdkmesh" ) );
    V_RETURN( g_ChunkMesh[7].Create( pd3dDevice, 
               L"DeferredParticles\\wallchunk7.sdkmesh" ) );
    V_RETURN( g_ChunkMesh[8].Create( pd3dDevice, 
               L"DeferredParticles\\wallchunk8.sdkmesh" ) );

    // Buildings
    g_Building[0].CreateBuilding( D3DXVECTOR3( 0, 0, 0 ), 2.0f, 50, 0, 50 );

    float fBuildingRange = g_fWorldBounds - 20.0f;

    for( UINT i = 1; i < MAX_BUILDINGS; i++ )
    {
        D3DXVECTOR3 vCenter;
        vCenter.x = RPercent() * fBuildingRange;
        vCenter.y = 0;
        vCenter.z = RPercent() * fBuildingRange;

        UINT x = ( rand() % 2 ) + 2;
        UINT y = ( rand() % 2 ) + 3;
        UINT z = ( rand() % 2 ) + 2;
        g_Building[i].CreateBuilding( vCenter, 2.0f, x * 2, y * 2, z * 2 );
    }

    ResetBuildings();

    // Particle system
    UINT NumStalkParticles = 500;
    UINT NumGroundExpParticles = 345;
    UINT NumLandMineParticles = 125;
    UINT MaxParticles = MAX_MUSHROOM_CLOUDS * ( g_NumParticles + NumStalkParticles ) +
        ( MAX_GROUND_BURSTS - MAX_MUSHROOM_CLOUDS ) * NumGroundExpParticles +
        ( MAX_PARTICLE_SYSTEMS - MAX_GROUND_BURSTS ) * NumLandMineParticles;
    V_RETURN( CreateParticleArray( MaxParticles ) );

    D3DXVECTOR4 vColor0( 1.0f,1.0f,1.0f,1 );
    D3DXVECTOR4 vColor1( 0.6f,0.6f,0.6f,1 );

    srand( timeGetTime() );
    g_ppParticleSystem = new CParticleSystem*[MAX_PARTICLE_SYSTEMS];
    g_NumParticlesToDraw = 0;
    for( UINT i = 0; i < MAX_MUSHROOM_CLOUDS; i += 2 )
    {
        D3DXVECTOR3 vLocation;
        vLocation.x = RPercent() * 50.0f;
        vLocation.y = g_fGroundPlane;
        vLocation.z = RPercent() * 50.0f;

        g_ppParticleSystem[i] = new CMushroomParticleSystem();
        g_ppParticleSystem[i]->CreateParticleSystem( g_NumParticles );
        g_ppParticleSystem[i]->SetSystemAttributes( 
           vLocation,
           g_fSpread, g_fMushroomCloudLifeSpan, g_fFadeExponent,
           g_fStartSize, g_fEndSize, g_fSizeExponent,
           g_fMushroomStartSpeed, g_fEndSpeed, g_fSpeedExponent,
           g_fRollAmount, g_fWindFalloff,
           1, 0, D3DXVECTOR3( 0, 0, 0 ), D3DXVECTOR3( 0, 0, 0 ),
           vColor0, vColor1,
           g_vPosMul, g_vDirMul );

        g_NumParticlesToDraw += g_NumParticles;

        g_ppParticleSystem[i + 1] = new CStalkParticleSystem();
        g_ppParticleSystem[i + 1]->CreateParticleSystem( NumStalkParticles );
        g_ppParticleSystem[i + 1]->SetSystemAttributes( 
           vLocation,
           15.0f, g_fMushroomCloudLifeSpan, g_fFadeExponent * 2.0f,
           g_fStartSize * 0.5f, g_fEndSize * 0.5f, g_fSizeExponent,
           g_fStalkStartSpeed, -1.0f, g_fSpeedExponent,
           g_fRollAmount, g_fWindFalloff,
           1, 0, D3DXVECTOR3( 0, 0, 0 ), D3DXVECTOR3( 0, 0, 0 ),
           vColor0, vColor1,
           D3DXVECTOR3( 1, 0.1f, 1 ), D3DXVECTOR3( 1, 0.1f, 1 ) );

        g_NumParticlesToDraw += NumStalkParticles;
    }

    for( UINT i = MAX_MUSHROOM_CLOUDS; i < MAX_GROUND_BURSTS; i++ )
    {
        D3DXVECTOR3 vLocation;
        vLocation.x = RPercent() * 50.0f;
        vLocation.y = g_fGroundPlane;
        vLocation.z = RPercent() * 50.0f;

        g_ppParticleSystem[i] = new CGroundBurstParticleSystem();
        g_ppParticleSystem[i]->CreateParticleSystem( NumGroundExpParticles );
        g_ppParticleSystem[i]->SetSystemAttributes( 
           vLocation,
           1.0f, g_fGroundBurstLifeSpan, g_fFadeExponent,
           0.5f, 8.0f, 1.0f,
           g_fGroundBurstStartSpeed, g_fEndSpeed, 4.0f,
           g_fRollAmount, 1.0f,
           30, 100.0f, D3DXVECTOR3( 0, 0.5f, 0 ), 
           D3DXVECTOR3( 1.0f, 0.5f, 1.0f ),
           vColor0, vColor1,
           g_vPosMul, g_vDirMul );

        g_NumParticlesToDraw += NumGroundExpParticles;
    }

    for( UINT i = MAX_GROUND_BURSTS; i < MAX_PARTICLE_SYSTEMS; i++ )
    {
        D3DXVECTOR3 vLocation;
        vLocation.x = RPercent() * 50.0f;
        vLocation.y = g_fGroundPlane;
        vLocation.z = RPercent() * 50.0f;

        g_ppParticleSystem[i] = new CLandMineParticleSystem();
        g_ppParticleSystem[i]->CreateParticleSystem( NumLandMineParticles );
        g_ppParticleSystem[i]->SetSystemAttributes( vLocation,
                    1.5f, g_fPopperLifeSpan, g_fFadeExponent,
                    1.0f, 6.0f, 1.0f,
                    g_fLandMineStartSpeed, g_fEndSpeed, 2.0f,
                    g_fRollAmount, 4.0f,
                    0, 70.0f, D3DXVECTOR3( 0, 0.8f, 0 ), 
                    D3DXVECTOR3( 0.3f, 0.2f, 0.3f ),
                    vColor0, vColor1,
                    g_vPosMul, g_vDirMul );

        g_NumParticlesToDraw += NumGroundExpParticles;
    }

    D3D10_BUFFER_DESC BDesc;
    BDesc.ByteWidth = sizeof( PARTICLE_VERTEX ) * 6 * g_NumParticlesToDraw;
    BDesc.Usage = D3D10_USAGE_DYNAMIC;
    BDesc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
    BDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
    BDesc.MiscFlags = 0;
    V_RETURN( pd3dDevice->CreateBuffer( 
      &BDesc, NULL, &g_pParticleBuffer ) );

    V_RETURN( DXUTFindDXSDKMediaFileCch( 
      str, MAX_PATH, L"DeferredParticles\\DeferredParticle.dds" ) );
    V_RETURN( D3DX10CreateShaderResourceViewFromFile( 
      pd3dDevice, str, NULL, NULL, &g_pParticleTextureSRV, NULL ) );
    
    // Create the screen quad
    BDesc.ByteWidth = 4 * sizeof( D3DXVECTOR3 );
    BDesc.Usage = D3D10_USAGE_IMMUTABLE;
    BDesc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
    BDesc.CPUAccessFlags = 0;
    BDesc.MiscFlags = 0;

    D3DXVECTOR3 verts[4] =
    {
        D3DXVECTOR3( -1, -1, 0.5f ),
        D3DXVECTOR3( -1, 1, 0.5f ),
        D3DXVECTOR3( 1, -1, 0.5f ),
        D3DXVECTOR3( 1, 1, 0.5f )
    };
    D3D10_SUBRESOURCE_DATA InitData;
    InitData.pSysMem = verts;
    V_RETURN( pd3dDevice->CreateBuffer( 
      &BDesc, &InitData, &g_pScreenQuadVB ) );

    return S_OK;
}


//------------------------------------------------------------------
// Create any D3D10 resources that depend on the back buffer
//------------------------------------------------------------------
HRESULT CALLBACK OnD3D10ResizedSwapChain( ID3D10Device* pd3dDevice, 
        IDXGISwapChain* pSwapChain,
        const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, 
        void* pUserContext )
{
    HRESULT hr;

    V_RETURN( g_DialogResourceManager.OnD3D10ResizedSwapChain( 
              pd3dDevice, pBackBufferSurfaceDesc ) );
    V_RETURN( g_D3DSettingsDlg.OnD3D10ResizedSwapChain( 
              pd3dDevice, pBackBufferSurfaceDesc ) );

    // Setup the camera's projection parameters
    float fAspectRatio = pBackBufferSurfaceDesc->Width / 
            ( FLOAT )pBackBufferSurfaceDesc->Height;
    g_Camera.SetProjParams( D3DX_PI / 4, fAspectRatio, 2.0f, 4000.0f );
    g_Camera.SetWindow( 
      pBackBufferSurfaceDesc->Width, pBackBufferSurfaceDesc->Height );
    g_Camera.SetButtonMasks( 
      MOUSE_MIDDLE_BUTTON, MOUSE_WHEEL, MOUSE_LEFT_BUTTON );

    g_HUD.SetLocation( pBackBufferSurfaceDesc->Width - 170, 0 );
    g_HUD.SetSize( 170, 170 );
    g_SampleUI.SetLocation( pBackBufferSurfaceDesc->Width - 170, 
               pBackBufferSurfaceDesc->Height - 300 );
    g_SampleUI.SetSize( 170, 300 );

    // Create the offscreen particle buffer
    D3D10_TEXTURE2D_DESC Desc;
    Desc.Width = pBackBufferSurfaceDesc->Width;
    Desc.Height = pBackBufferSurfaceDesc->Height;
    Desc.MipLevels = 1;
    Desc.ArraySize = 1;
    Desc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
    Desc.SampleDesc.Count = 1;
    Desc.SampleDesc.Quality = 0;
    Desc.Usage = D3D10_USAGE_DEFAULT;
    Desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
    Desc.CPUAccessFlags = 0;
    Desc.MiscFlags = 0;

    V_RETURN( pd3dDevice->CreateTexture2D( 
       &Desc, NULL, &g_pOffscreenParticleTex ) );
    Desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    V_RETURN( pd3dDevice->CreateTexture2D( 
      &Desc, NULL, &g_pOffscreenParticleColorTex ) );

    D3D10_RENDER_TARGET_VIEW_DESC RTVDesc;
    RTVDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
    RTVDesc.ViewDimension = D3D10_RTV_DIMENSION_TEXTURE2D;
    RTVDesc.Texture2D.MipSlice = 0;

    V_RETURN( pd3dDevice->CreateRenderTargetView( 
      g_pOffscreenParticleTex, &RTVDesc, &g_pOffscreenParticleRTV ) );
    RTVDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    V_RETURN( pd3dDevice->CreateRenderTargetView( 
      g_pOffscreenParticleColorTex, &RTVDesc,
      &g_pOffscreenParticleColorRTV ) );

    D3D10_SHADER_RESOURCE_VIEW_DESC SRVDesc;
    SRVDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
    SRVDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
    SRVDesc.Texture2D.MostDetailedMip = 0;
    SRVDesc.Texture2D.MipLevels = Desc.MipLevels;

    V_RETURN( pd3dDevice->CreateShaderResourceView( 
      g_pOffscreenParticleTex, &SRVDesc, 
      &g_pOffscreenParticleSRV ) );
    SRVDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    V_RETURN( pd3dDevice->CreateShaderResourceView( 
      g_pOffscreenParticleColorTex, &SRVDesc,
      &g_pOffscreenParticleColorSRV ) );

    return S_OK;
}

//------------------------------------------------------------
// Render particles
//------------------------------------------------------------
void RenderParticles( ID3D10Device* pd3dDevice, 
     ID3D10EffectTechnique* pRenderTechnique )
{
    //IA setup
    pd3dDevice->IASetInputLayout( g_pVertexLayout );
    UINT Strides[1];
    UINT Offsets[1];
    ID3D10Buffer* pVB[1];
    pVB[0] = g_pParticleBuffer;
    Strides[0] = sizeof( PARTICLE_VERTEX );
    Offsets[0] = 0;
    pd3dDevice->IASetVertexBuffers( 0, 1, pVB, Strides, Offsets );
    pd3dDevice->IASetIndexBuffer( NULL, DXGI_FORMAT_R16_UINT, 0 );
    pd3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

    g_ptxDiffuse->SetResource( g_pParticleTextureSRV );

    //Render
    D3D10_TECHNIQUE_DESC techDesc;
    pRenderTechnique->GetDesc( &techDesc );

    g_NumParticlesToDraw = GetNumActiveParticles();
    for( UINT p = 0; p < techDesc.Passes; ++p )
    {
        pRenderTechnique->GetPassByIndex( p )->Apply( 0 );
        pd3dDevice->Draw( 6 * g_NumParticlesToDraw, 0 );
    }
}

//--------------------------------------------------------
// Render particles into the offscreen buffer
//--------------------------------------------------------
void RenderParticlesIntoBuffer( ID3D10Device* pd3dDevice )
{
    // Clear the new render target
    float color[4] =
    {
        0, 0, 0, 0
    };
    pd3dDevice->ClearRenderTargetView( g_pOffscreenParticleRTV, color );
    pd3dDevice->ClearRenderTargetView( g_pOffscreenParticleColorRTV, color );
    
    // get the old render targets
    ID3D10RenderTargetView* pOldRTV;
    ID3D10DepthStencilView* pOldDSV;
    pd3dDevice->OMGetRenderTargets( 1, &pOldRTV, &pOldDSV );

    // Set the new render targets
    ID3D10RenderTargetView* pViews[2];
    pViews[0] = g_pOffscreenParticleRTV;
    pViews[1] = g_pOffscreenParticleColorRTV;
    pd3dDevice->OMSetRenderTargets( 2, pViews, pOldDSV );

    // Render the particles
    RenderParticles( pd3dDevice, g_pRenderParticlesToBuffer );

    // restore the original render targets
    pViews[0] = pOldRTV;
    pViews[1] = NULL;
    pd3dDevice->OMSetRenderTargets( 2, pViews, pOldDSV );
    SAFE_RELEASE( pOldRTV );
    SAFE_RELEASE( pOldDSV );
}

//-----------------------------------------------------------
// Composite offscreen particle buffer back into the scene
//-----------------------------------------------------------
void CompositeParticlesIntoScene( ID3D10Device* pd3dDevice )
{
    // Render the particles
    ID3D10EffectTechnique* pRenderTechnique = g_pCompositeParticlesToScene;

    //IA setup
    pd3dDevice->IASetInputLayout( g_pScreenQuadLayout );
    UINT Strides[1];
    UINT Offsets[1];
    ID3D10Buffer* pVB[1];
    pVB[0] = g_pScreenQuadVB;
    Strides[0] = sizeof( D3DXVECTOR3 );
    Offsets[0] = 0;
    pd3dDevice->IASetVertexBuffers( 0, 1, pVB, Strides, Offsets );
    pd3dDevice->IASetIndexBuffer( NULL, DXGI_FORMAT_R16_UINT, 0 );
    pd3dDevice->IASetPrimitiveTopology( 
                   D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );

    g_ptxDiffuse->SetResource( g_pOffscreenParticleSRV );
    g_ptxParticleColor->SetResource( g_pOffscreenParticleColorSRV );

    //Render
    D3D10_TECHNIQUE_DESC techDesc;
    pRenderTechnique->GetDesc( &techDesc );

    for( UINT p = 0; p < techDesc.Passes; ++p )
    {
        pRenderTechnique->GetPassByIndex( p )->Apply( 0 );
        pd3dDevice->Draw( 4, 0 );
    }

    // Un-set this resource, as it's associated with an OM output
    g_ptxParticleColor->SetResource( NULL );
    for( UINT p = 0; p < techDesc.Passes; ++p )
    {
        pRenderTechnique->GetPassByIndex( p )->Apply( 0 );
    }
}

void RenderInstanced( ID3D10Device* pd3dDevice, 
     ID3D10EffectTechnique* pTechnique, CDXUTSDKMesh* pMesh,
     UINT NumInstances )
{
    ID3D10Buffer* pVB[1];
    UINT Strides[1];
    UINT Offsets[1] =
    {
        0
    };

    pVB[0] = pMesh->GetVB10( 0, 0 );
    Strides[0] = ( UINT )pMesh->GetVertexStride( 0, 0 );

    pd3dDevice->IASetVertexBuffers( 0, 1, pVB, Strides, Offsets );
    pd3dDevice->IASetIndexBuffer( pMesh->GetIB10( 0 ), 
                                  pMesh->GetIBFormat10( 0 ), 0 );

    D3D10_TECHNIQUE_DESC techDesc;
    pTechnique->GetDesc( &techDesc );
    SDKMESH_SUBSET* pSubset = NULL;
    D3D10_PRIMITIVE_TOPOLOGY PrimType;

    for( UINT p = 0; p < techDesc.Passes; ++p )
    {
        for( UINT subset = 0; subset < pMesh->GetNumSubsets( 0 ); ++subset )
        {
            pSubset = pMesh->GetSubset( 0, subset );

            PrimType = pMesh->GetPrimitiveType10( 
              ( SDKMESH_PRIMITIVE_TYPE )pSubset->PrimitiveType );
            pd3dDevice->IASetPrimitiveTopology( PrimType );

            pTechnique->GetPassByIndex( p )->Apply( 0 );
            pd3dDevice->DrawIndexedInstanced( 
               ( UINT )pSubset->IndexCount, NumInstances,
               0, ( UINT )pSubset->VertexStart, 0 );
        }
    }
}

//-----------------------------------------------
// Render the scene using the D3D10 device
//-----------------------------------------------
void CALLBACK OnD3D10FrameRender( ID3D10Device* pd3dDevice, 
     double fTime, float fElapsedTime, void* pUserContext )
{
    HRESULT hr;

    // If the settings dialog is being shown,
    // then render it instead of rendering the app's scene
    if( g_D3DSettingsDlg.IsActive() )
    {
        g_D3DSettingsDlg.OnRender( fElapsedTime );
        return;
    }

    // Clear the render target and depth stencil
    float ClearColor[4] =
    {
        0.0f, 0.0f, 0.0f, 1.0f
    };
    ID3D10RenderTargetView* pRTV = DXUTGetD3D10RenderTargetView();
    pd3dDevice->ClearRenderTargetView( pRTV, ClearColor );
    ID3D10DepthStencilView* pDSV = DXUTGetD3D10DepthStencilView();
    pd3dDevice->ClearDepthStencilView( pDSV, D3D10_CLEAR_DEPTH, 1.0, 0 );

    D3DXVECTOR3 vEyePt;
    D3DXMATRIX mWorldViewProjection;
    D3DXVECTOR4 vLightDir;
    D3DXMATRIX mWorld;
    D3DXMATRIX mView;
    D3DXMATRIX mProj;
    D3DXMATRIX mViewProj;
    D3DXMATRIX mInvViewProj;

    // Get the projection & view matrix from the camera class
    D3DXMatrixIdentity( &mWorld );
    vEyePt = *g_Camera.GetEyePt();
    mProj = *g_Camera.GetProjMatrix();
    mView = *g_Camera.GetViewMatrix();

    mWorldViewProjection = mView * mProj;
    mViewProj = mView * mProj;
    D3DXMatrixInverse( &mInvViewProj, NULL, &mViewProj );
    D3DXMATRIX mSceneWorld;
    D3DXMatrixScaling( &mSceneWorld, 20, 20, 20 );
    D3DXMATRIX mSceneWVP = mSceneWorld * mViewProj;
    vLightDir = D3DXVECTOR4( g_LightControl.GetLightDirection(), 1 );

    // Per frame variables
    V( g_pmWorldViewProjection->SetMatrix( ( float* )&mSceneWVP ) );
    V( g_pmWorld->SetMatrix( ( float* )&mSceneWorld ) );
    V( g_pLightDir->SetFloatVector( ( float* )vLightDir ) );
    V( g_pmInvViewProj->SetMatrix( ( float* )&mInvViewProj ) );
    V( g_pfTime->SetFloat( ( float )fTime ) );
    V( g_pvEyePt->SetFloatVector( ( float* )&vEyePt ) );
    V( g_pmViewProj->SetMatrix( ( float* )&mViewProj ) );

    // Gather up the instance matrices for the buildings and pieces
    g_BaseMeshMatrices.Reset();
    for( UINT i = 0; i < NUM_CHUNKS; i++ )
    {
        g_ChunkMeshMatrices[i].Reset();
    }

    // Get matrices
    for( UINT i = 0; i < MAX_BUILDINGS; i++ )
    {
        g_Building[i].CollectBaseMeshMatrices( &g_BaseMeshMatrices );
        for( UINT c = 0; c < NUM_CHUNKS; c++ )
        {
            g_Building[i].CollectChunkMeshMatrices( c, &g_ChunkMeshMatrices[c] );
        }
    }

    // Set our input layout
    pd3dDevice->IASetInputLayout( g_pMeshLayout );

    // Intact walls
    D3DXMATRIX* pmData = g_BaseMeshMatrices.GetData();
    UINT NumMeshes = g_BaseMeshMatrices.GetSize();
    UINT numrendered = 0;
    while( numrendered < NumMeshes )
    {
        UINT NumToRender = min( MAX_INSTANCES, NumMeshes - numrendered );

        g_pmWorldInst->SetMatrixArray( 
          ( float* )&pmData[numrendered], 0, NumToRender );

        RenderInstanced( pd3dDevice, g_pRenderMeshInst, 
                         &g_WallMesh, NumToRender );

        numrendered += NumToRender;
    }

    // Chunks
    for( UINT c = 0; c < NUM_CHUNKS; c++ )
    {
        pmData = g_ChunkMeshMatrices[c].GetData();
        NumMeshes = g_ChunkMeshMatrices[c].GetSize();
        numrendered = 0;
        while( numrendered < NumMeshes )
        {
            UINT NumToRender = min( MAX_INSTANCES, NumMeshes - numrendered );

            g_pmWorldInst->SetMatrixArray( 
              ( float* )&pmData[numrendered], 0, NumToRender );

            RenderInstanced( pd3dDevice, g_pRenderMeshInst, 
                             &g_ChunkMesh[c], NumToRender );

            numrendered += NumToRender;
        }
    }

    // Render particles
    V( g_pmWorldViewProjection->SetMatrix( ( float* )&mWorldViewProjection ) );
    V( g_pmWorld->SetMatrix( ( float* )&mWorld ) );

    if( g_bRenderDeferred )
    {
        RenderParticlesIntoBuffer( pd3dDevice );
        CompositeParticlesIntoScene( pd3dDevice );
    }
    else
    {
        RenderParticles( pd3dDevice, g_pRenderParticles );
    }

    DXUT_BeginPerfEvent( DXUT_PERFEVENTCOLOR, L"HUD / Stats" );
    g_HUD.OnRender( fElapsedTime );
    g_SampleUI.OnRender( fElapsedTime );
    RenderText();
    DXUT_EndPerfEvent();
}


//-------------------------------------------------------------
// Release D3D10 resources created in OnD3D10ResizedSwapChain 
//-------------------------------------------------------------
void CALLBACK OnD3D10ReleasingSwapChain( void* pUserContext )
{
    g_DialogResourceManager.OnD3D10ReleasingSwapChain();

    SAFE_RELEASE( g_pOffscreenParticleTex );
    SAFE_RELEASE( g_pOffscreenParticleSRV );
    SAFE_RELEASE( g_pOffscreenParticleRTV );
    SAFE_RELEASE( g_pOffscreenParticleColorTex );
    SAFE_RELEASE( g_pOffscreenParticleColorSRV );
    SAFE_RELEASE( g_pOffscreenParticleColorRTV );
}


//----------------------------------------------------------
// Release D3D10 resources created in OnD3D10CreateDevice 
//----------------------------------------------------------
void CALLBACK OnD3D10DestroyDevice( void* pUserContext )
{
    g_DialogResourceManager.OnD3D10DestroyDevice();
    g_D3DSettingsDlg.OnD3D10DestroyDevice();
    CDXUTDirectionWidget::StaticOnD3D10DestroyDevice();
    DXUTGetGlobalResourceCache().OnDestroyDevice();
    SAFE_DELETE( g_pTxtHelper );
    SAFE_RELEASE( g_pFont10 );
    SAFE_RELEASE( g_pSprite10 );
    SAFE_RELEASE( g_pEffect10 );
    SAFE_RELEASE( g_pVertexLayout );
    SAFE_RELEASE( g_pScreenQuadLayout );
    SAFE_RELEASE( g_pMeshLayout );
    g_WallMesh.Destroy();
    for( UINT i = 0; i < NUM_CHUNKS; i++ )
    {
        g_ChunkMesh[i].Destroy();
    }

    SAFE_RELEASE( g_pScreenQuadVB );
    for( UINT i = 0; i < MAX_PARTICLE_SYSTEMS; i++ )
    {
        SAFE_DELETE( g_ppParticleSystem[i] );
    }
    SAFE_DELETE_ARRAY( g_ppParticleSystem );
    SAFE_RELEASE( g_pParticleBuffer );
    SAFE_RELEASE( g_pParticleTextureSRV );

    DestroyParticleArray();
}

代表烟雾的粒子系统通常被渲染为一系列与屏幕对齐的精灵。任何光照信息都在粒子渲染时计算。由此产生的被照亮的粒子会与现有场景或粒子进行 alpha 混合。这导致粒子系统看起来很平坦。虽然系统旨在代表烟雾的体积,但粒子仍然像独立实体一样被照亮。以延迟方式渲染粒子允许我们在屏幕外缓冲区中累积法线和不透明度,然后在另一个通道中照亮累积的数据。这使得我们可以将粒子系统作为一个连续的实体而不是单个精灵来处理和照亮。粒子不仅被场景光(太阳)照亮,还被爆炸本身产生的内部光照亮。每个光照在每次爆炸的中心都有一个短暂但强烈的灯光。这些灯光也会影响邻近爆炸中的粒子;因此,活动灯光的数量存在上限。在顶点着色器中,每个顶点都会遍历场景中的活动灯光,并使用二次衰减计算该顶点处的光照强度。此光照强度也存储在屏幕外缓冲区中,并在光照通道中使用,为粒子添加额外的光照。由于这种内部光照不是定向的,因此它会简单地添加到粒子中,而不考虑粒子的法线方向(参考:Microsoft DirectX SDK)。

参考文献

  • MSDN 关于 DirectX 和 DirectX SDK (2010 年 6 月)
  • Peter Walsh 著,《3D 游戏编程导论》
© . All rights reserved.