Click here to Skip to main content
15,884,628 members
Articles / Multimedia / DirectX

Understanding Direct3D 10 Application Code

Rate me:
Please Sign up or sign in to vote.
4.33/5 (11 votes)
29 Dec 2010CPOL11 min read 50.8K   1.8K   36   5
An article that describes some of the important parts of Direct3D program code.

Preface

Nearly everything you do in Direct3D is done to manipulate the graphics hardware itself. This is why DirectX is not so much a game platform as it is a hardware interface. The most important piece of hardware that we are concerned with is called the GPU, or Graphics Processing Unit. The GPU is a separate integrated circuit chip that executes "shader" files. Think of shaders as mini programs that are executed on the GPU: they are written in HLSL, and loaded as FX (effect) files, having a .fx file extension. While the CPU (microprocessor) performs calculations that can direct the entire system, the GPU performs calculations on graphics, directing graphics output to the monitor. This is why it is a good idea to ascertain the capabilities of your machine's graphics hardware. Identify your GPU (and its capabilities) by going to the "download latest driver updates" section of http://www.nvidia.com.

Making any Direct3D program requires one to first create a window, a standard window identified by a window handle. You can use the normal Win32 API functions or other technologies like MFC. The purpose of creating a main window is to receive messages and provide a canvas upon which Direct3D will render drawings or images. Direct3D will draw all of its data onto this window. Upon creating the Direct3D object, we then create the Direct3D device. The Direct3D device is the most important interface in Direct3D.

The DirectX Graphics Infrastructure: DXGI

The DXGI is a component that lies at the base of all of the most recent versions of Direct3D. It handles the fundamental tasks involved with Direct3D, such as verifying the appropriate resolution rate and displaying images on the screen. DXGI is not actually a part of the DirectX API, but rather underlies it and other graphic components. That is, it acts as an interface between Direct3D and the hardware.

2.jpg

Since all our rendering is done with a Direct3D device, the first thing we need to do is construct a device. This also involves constructing a swap chain, consisting of a back buffer and primary surface. Doing this requires us to fill out a structure called DXGI_SWAP_CHAIN_DESC and then call the D3D10CreateDeviceAndSwapChain() function. These structures will be discussed after our first example.

Understanding the Swap Chain

The GPU contains in its memory a pointer to a buffer of pixels that contains the image currently being displayed on the screen. When you need to render something, such as a 3D model or image, the GPU updates this array and sends the information to the monitor to display. The monitor then redraws the screen from top to bottom, replacing the old image with the new. The problem lies in the fact that the monitor does not refresh as fast as needed for real-time rendering. If another model were rendered to the GPU while the monitor was refreshing, the image displayed would be cut in two, the top portion containing the old image and the bottom portion containing the new. This effect is called tearing, and to prevent this, DXGI implements a feature called swapping.

According to the literature contained in the DirectX browser, in Direct3D 10, the device object is used to perform both rendering and resource creation. In Direct3D 11, the immediate context is used by the application to perform rendering onto a buffer, and the device contains methods to create resources. The swap chain is responsible for taking the buffer to which the device renders and displaying the content on the actual monitor screen. The swap chain contains two or more buffers, mainly the front and the back. These are textures to which the device renders in order to display on the monitor. The front buffer is what is being presented currently to the user. This buffer is read-only and cannot be modified. The back buffer is the render target to which the device will draw. Once it finishes the drawing operation, the swap chain will present the backbuffer by swapping the two buffers. The back buffer becomes the front buffer, and vice versa.

Instead of rendering new images directly to the monitor, DXGI draws your images onto a secondary buffer of pixels, called the back buffer. The front buffer would be the buffer currently being displayed. You draw all your images onto the back buffer, and when you are done, DXGI will update the front buffer with the contents of the back buffer, discarding the old image. But tearing could still occur should the image still be in transfer during the refresh. This is why DXGI uses a pointer for each buffer (both front and back) and simply switches their values. The back buffer then becomes the front buffer (and vice versa), and thus there is no tearing.

3.jpg

Examples

The images shown below are the output of an application that initially renders a blank screen. The user hits the space bar, and a colored, spinning tea pot appears. The user hits the space bar again and the color of the tea pot changes. If the user presses Alt-Enter, the output goes into full screen mode. Now, we must step through the code, or at least the main source code file. This file will illustrate that the first thing it had to do was to construct a Direct3D device, as all rendering is done through a Direct3D device. But from the top down, there are a lot of other things we need to understand in order to write our own Direct3D applications.

space.JPG

spacegreen.JPG

OK. So a spinning teapot is displayed. The user hits the spacebar and the teapot changes colors. If the user hits the Alt-Enter key, the spinning images will go into full screen mode. Here is the main source code of the application. It will provide a good framework to step through some central concepts in coding a Direct3D application:

C++
#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 );
}

This code, like any other 3D gaming technology code, appears very involved. Sections of this code can be broken down, however, to clarify how it works. Before we start with initializing Direct3D, let's take a brief note about the top of the file. Below the header files are global declarations. Below those global declarations are function prototypes. Those and some other data structures are itemized, until we get to the program entry point, the WinMain() function.

The process of initializing Direct3D begins by describing the characteristics of the swap chain to be created by filling out an instance of the DXGI_SWAP_CHAIN_DESC structure. This and the following steps are listed below:

  • Create the ID3D10Device and IDXGISwapChain interfaces using the D3D10CreateDeviceAndSwapChain function.
  • Create a render target view to the swap chain's back buffer.
  • Create the depth/stencil buffer and its associated depth/stencil view.
  • Bind the render target view and depth/stencil view to the output merger stage of the rendering pipeline so that they can be used by Direct3D.
  • Set the viewport.

Describe the Swap Chain

To repeat, initializing Direct3D begins by filling out an instance of the DXGI_SWAP_CHAIN_DESC structure, which describes the characteristics of the swap chain we are going to create. Here is a generic structure:

C++
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;

The DXGI_MODE_DESC type is another structure, defined as:

C++
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;

The following code is taken from our sample and shows how we fill out the DXGI_SWAP_CHAIN_DESC structure:

C++
// 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;

Create the Device and Swap Chain

After we describe the swap chain we want to create by filling out a DXGI_SWAP_CHAIN_DESC structure, we are ready to create the Direct3D 10 device (ID3D10Device) and swap chain (IDXGISwapChain). The ID3D10Device interface is the chief Direct3D interface, and can be thought of as our software controller of the physical graphics device hardware; that is, through this interface, we can interact with the hardware and instruct it to do things (such as clear the back buffer, bind resources to the various pipeline stages, and draw geometry). The device and swap chain can be created with the following function:

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

Creating a Depth/Stencil Buffer

Wel will not go into the particulars of a depth stencil buffer. For now, though, we need to set one up in order to be able to render properly. What is important to know is that a depth buffer is used to provide pixel accurate depth testing so that when you render an object in front of another object, they don't come out all mangled up. Stencil buffers are used for advanced effects like volume shadowing. To create a depth/stencil buffer, we start by creating a 2D texture with the same resolution as our back buffer. We do this by filling out a D3D10_TEXTURE2D_DESC structure, and we will fill it out like this:

C++
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;

The main points to note are that the width and height are exactly the same size as the back buffer. Also notice, the format is set to DXGI_FORMAT_D24_UNORM_S8_UINT, which in English means a 32-bit buffer, with 24 bits allocated to the depth buffer and 8 bits allocated to the stencil buffer. The buffer holds unsigned integer data. When the structure is all filled out, we can pass it as a parameter to ID3D10Device::CreateTexture2D(), which has the following prototype:

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

The first parameter takes the address of our D3D10_TEXTURE2D_DESC structure that we just filled out. The second parameter takes initial data to load the texture with, which we are not interested in and can therefore set to NULL. The third parameter takes the address of a pointer to a texture, which will be filled in by Direct3D when the texture is created. Notice the sample code, ending with "MiscFlags", and then is followed by the call:

C++
//------------------------------------------------------
// 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;
}

Creating a Viewport

A viewport defines which area of your back buffer is rendered to. We want to render to the entire buffer; however, you could easily change these settings to render to a different portion of the buffer. The viewport also defines the minimum and maximum depth that will be used for your depth buffer. Setting up the viewport is much easier than creating the depth stencil buffer. The first step is to fill in a D3D10_VIEWPORT structure, which looks like this:

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

Now examine the sample code:

C++
// 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 );

The source code file loads the FX file that executes on the GPU. Let's start with loading and compiling the FX file. To do that, we use the function D3DX10CreateEffectFromFile(), which has the following prototype:

C++
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
);

Here is the sample code's technique to create the effect:

C++
// 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 ...

That did not cover all of the code, but we can now look at large segments of DirectX code and understand what it is doing. It is important to keep in mind that the Direct3D API is a collection of COM libraries. COM programming practices use a lot of pointers, and always clean up and remove a device (or an object) once it has been used. This next example is called deferred particles, and is one of the many particle system examples of DirectX code. Particle systems create smoke, fire, rain, blood, water, and other rainy elements. Extremely tiny particles stream (or emit) from a source, and either accumulate to form a stream or detonate to exhibit light.

Particle Systems

Examine this example of a deferred particle system:

def.JPG

Here is the main source file:

C++
-----------------
#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();
}

Particle systems representing smoke are often rendered as a series of screen-aligned sprites. Any lighting information is computed when the particle is rendered. The resulting lit particle is alpha-blended over the top of existing scenery or particles. This results in a flat-looking particle system. Whereas the system is supposed to represent a volume of smoke, the particles are still light as if they are individual entities. Rendering the particles in a deferred manner allows us to accumulate normals and opacity in an offscreen buffer and then light the accumulated data in another pass. This allows us to treat and light the particle system as one continuous entity rather than individual sprites. The particles are lit not only by the scene light (the sun), but also by internal lights generated by the explosions themselves. Each light has a short but intense light at the center of each explosion. These lights can affect particles in neighboring explosions as well; therefore, there is a limit on the maximum number of lights that can be active at any one time. In the vertex shader, each vertex loops over the active lights in the scene, and calculates the intensity of the light at that vertex using a quadratic falloff. This light intensity is also stored in an offscreen buffer and used in the lighting pass to add extra light to the particles. Since this interior light is not directional, it is simply added to the particles without regard for the particles' orientation of the normals (Reference: Microsoft DirectX SDK).

References

  • The MSDN on DirectX and the DirectX SDK (June 2010)
  • Introduction to #D Game Programming, written by Peter Walsh

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Monroe Community
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 3 Pin
Bartlomiej Filipek30-Sep-13 8:03
Bartlomiej Filipek30-Sep-13 8:03 
GeneralAverage Vote of 3 Pin
Sharjith4-Jan-11 7:06
professionalSharjith4-Jan-11 7:06 
Generalnice Pin
Pranay Rana30-Dec-10 16:50
professionalPranay Rana30-Dec-10 16:50 
GeneralVery Nice Article Pin
LaxmikantYadav29-Dec-10 17:12
LaxmikantYadav29-Dec-10 17:12 
GeneralMy vote of 3 Pin
Ajay Vijayvargiya29-Dec-10 16:10
Ajay Vijayvargiya29-Dec-10 16:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.