Understanding Direct3D 10 Application 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 an .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 good idea to ascertain the capabilities of your machine’s graphics hardware. Identify your GPU (and its capabilities) by going to the “download latest drivers update” 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
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. 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.
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 was 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.
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.
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:
Collapse | Copy Code
#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 Definitionsstatic 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 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. 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. 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 bool bGotMsg; MSG msg; msg.message = WM_NULL; PeekMessage( &msg, NULL, 0U, 0U, PM_NO while( WM_QUIT != msg.message ) { // Use PeekMessage() so we can use idle time to render the scene. bGotMsg = ( PeekMessage( &msg, NULL, 0U, 0U, PM_ 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, ( UINT )rcCurrentClient.right, // passing in 0 here will automatically calculate the size from the client rect ( UINT )rcCurrentClient.bottom, // passing in 0 here will automatically calculate the size from the client rect 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//--------------------------------------------------------------------------------------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 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 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. 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. 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 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 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 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 );}</window_object*></adapter_object*></device_object*></idxgioutput*>
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; this structure is defined as follows:
Collapse | Copy Code
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:
Collapse | Copy Code
typedef struct DXGI_MODE_DESC{ UINT Width; UINT Height; DXGI_RATIONAL RefreshRate; DXGI_FORMAT Format; DXGI_MODE_SCANLINE_ORDER ScanlineOrdering; DXGI_MODE_SCALING Scaling; } DXGI_MODE_DESC;
The following code is taken from our sample and shows how we fill out the DXGI_SWAP_CHAIN_DESC structure:
Collapse | Copy Code
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:
Collapse | Copy Code
hr = g_pDXGIFactory->CreateSwapChain( pDXGIDevice, &SwapChainDesc, &pWindowObj > pSwapChain ); pDXGIDevice->Release(); pDXGIDevice = NULL;
: Creating a Depth/Stencil Buffer
We’ll 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:
Collapse | Copy Code
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:
Collapse | Copy Code
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:
Collapse | Copy Code
//--------------------------------------------------------------------------------------// 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:
Collapse | Copy Code
typedef struct D3D10_VIEWPORT {INT TopLeftX;INT TopLeftY;UINT Width;UINT Height;FLOAT MinDepth;FLOAT MaxDepth;} D3D10_VIEWPORT;
Now examine the sample code:
Collapse | Copy Code
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 ); 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:
Collapse | Copy Code
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:
Collapse | Copy Code
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(); 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 ) ); DXUTCreateTeapot( pDeviceObj->pDevice, &pDeviceObj->pMesh ); 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) ) ); 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 hs been used. This next example is called deferred particles, and is one of the many particle systems 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:
Here is the main source file:
Collapse | Copy Code
-----------------#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//--------------------------------------------------------------------------------------CDXUTDialogResourceManager g_DialogResourceManager; // manager for shared resources of dialogsCModelViewerCamera g_Camera; // A model viewing camerCDXUTDirectionWidget g_LightControl;CD3DSettingsDlg g_D3DSettingsDlg; // Device settings dialogCDXUTDialog g_HUD; // manages the 3D CDXUTDialog g_SampleUI; // dialog for sample specific controls// Direct3D10 resourcesCDXUTTextHelper* g_pTxtHelper = NULL;ID3DX10Font* g_pFont10 = NULL;ID3DX10Sprite* g_pSprite10 = NULL;CDXUTSDKMesh g_WallMesh;CDXUTSDKMesh g_ChunkMesh[NUM_CHUNKS];#define MAX_BUILDINGS 5CBuilding 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 4D3DXVECTOR4 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 200CParticleSystem** 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(); DXUTInit( true, true, NULL ); // Parse the command line, show msgboxes on error, no extra command line params DXUTSetCursorSettings( true, true ); // Show the cursor and clip it when in full screen 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 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 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//--------------------------------------------------------------------------------------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//--------------------------------------------------------------------------------------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 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 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 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 lite 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