Preface
Nearly everything you do in Direct3D is done to manipulate the graphics hardware itself. This is why DirectX is not so much a game platform as it is a hardware interface. The most important piece of hardware that we are concerned with is called the GPU, or Graphics Processing Unit. The GPU is a separate integrated circuit chip that executes "shader" files. Think of shaders as mini programs that are executed on the GPU: they are written in HLSL, and loaded as FX (effect) files, having a .fx file extension. While the CPU (microprocessor) performs calculations that can direct the entire system, the GPU performs calculations on graphics, directing graphics output to the monitor. This is why it is a good idea to ascertain the capabilities of your machine's graphics hardware. Identify your GPU (and its capabilities) by going to the "download latest driver updates" section of http://www.nvidia.com.
Making any Direct3D program requires one to first create a window, a standard window identified by a window handle. You can use the normal Win32 API functions or other technologies like MFC. The purpose of creating a main window is to receive messages and provide a canvas upon which Direct3D will render drawings or images. Direct3D will draw all of its data onto this window. Upon creating the Direct3D object, we then create the Direct3D device. The Direct3D device is the most important interface in Direct3D.
The DirectX Graphics Infrastructure: DXGI
The DXGI is a component that lies at the base of all of the most recent versions of Direct3D. It handles the fundamental tasks involved with Direct3D, such as verifying the appropriate resolution rate and displaying images on the screen. DXGI is not actually a part of the DirectX API, but rather underlies it and other graphic components. That is, it acts as an interface between Direct3D and the hardware.
Since all our rendering is done with a Direct3D device, the first thing we need to do is construct a device. This also involves constructing a swap chain, consisting of a back buffer and primary surface. Doing this requires us to fill out a structure called DXGI_SWAP_CHAIN_DESC
and then call the D3D10CreateDeviceAndSwapChain(
) function. These structures will be discussed after our first example.
Understanding the Swap Chain
The GPU contains in its memory a pointer to a buffer of pixels that contains the image currently being displayed on the screen. When you need to render something, such as a 3D model or image, the GPU updates this array and sends the information to the monitor to display. The monitor then redraws the screen from top to bottom, replacing the old image with the new. The problem lies in the fact that the monitor does not refresh as fast as needed for real-time rendering. If another model were rendered to the GPU while the monitor was refreshing, the image displayed would be cut in two, the top portion containing the old image and the bottom portion containing the new. This effect is called tearing, and to prevent this, DXGI implements a feature called swapping.
According to the literature contained in the DirectX browser, in Direct3D 10, the device object is used to perform both rendering and resource creation. In Direct3D 11, the immediate context is used by the application to perform rendering onto a buffer, and the device contains methods to create resources. The swap chain is responsible for taking the buffer to which the device renders and displaying the content on the actual monitor screen. The swap chain contains two or more buffers, mainly the front and the back. These are textures to which the device renders in order to display on the monitor. The front buffer is what is being presented currently to the user. This buffer is read-only and cannot be modified. The back buffer is the render target to which the device will draw. Once it finishes the drawing operation, the swap chain will present the backbuffer by swapping the two buffers. The back buffer becomes the front buffer, and vice versa.
Instead of rendering new images directly to the monitor, DXGI draws your images onto a secondary buffer of pixels, called the back buffer. The front buffer would be the buffer currently being displayed. You draw all your images onto the back buffer, and when you are done, DXGI will update the front buffer with the contents of the back buffer, discarding the old image. But tearing could still occur should the image still be in transfer during the refresh. This is why DXGI uses a pointer for each buffer (both front and back) and simply switches their values. The back buffer then becomes the front buffer (and vice versa), and thus there is no tearing.
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:
#include "DXUT.h"
#include "DXUTmisc.h"
#include "SDKmisc.h"
#include "resource.h"
#include "DXUTShapes.h"
WNDCLASS g_WindowClass;
BOOL g_bFullscreen = FALSE; 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;
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];
};
static const TEST_MODE g_testModes[] = {
{ 0.25f, false, 1.0f, 0.0f, 0.0f, 1.0f },
{ 0.25f, true, 1.0f, 1.0f, 1.0f, 1.0f },
{ 0.25f, true, 1.0f, 0.0f, 0.0f, 1.0f },
{ 0.25f, true, 0.0f, 1.0f, 0.0f, 1.0f },
{ 0.25f, true, 0.0f, 0.0f, 1.0f, 1.0f },
{ 0.5f, false, 0.0f, 0.0f, 1.0f, 1.0f },
{ 0.5f, true, 1.0f, 1.0f, 1.0f, 1.0f },
{ 0.5f, true, 1.0f, 0.0f, 0.0f, 1.0f },
{ 0.5f, true, 0.0f, 1.0f, 0.0f, 1.0f },
{ 0.5f, true, 0.0f, 0.0f, 1.0f, 1.0f },
{ 1.0f, false, 1.0f, 0.0f, 0.0f, 1.0f },
{ 1.0f, true, 1.0f, 1.0f, 1.0f, 1.0f },
{ 1.0f, true, 1.0f, 0.0f, 0.0f, 1.0f },
{ 1.0f, true, 0.0f, 1.0f, 0.0f, 1.0f },
{ 1.0f, true, 0.0f, 0.0f, 1.0f, 1.0f },
};
const UINT g_nTestModes = ARRAYSIZE(g_testModes);
UINT g_iCurrentTestMode = 0;
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
if( FAILED( InitD3D10() ) )
{
MessageBox( NULL, L"This application requires Direct3D10 and "
L"Vista to run. This application will now exit.", L"Error", MB_OK );
FullCleanup();
return 1;
}
if( FAILED( EnumerateAdapters() ) )
{
MessageBox( NULL, L"Could not enumerate adapters. "
L"This application will now exit.", L"Error", MB_OK );
FullCleanup();
return 1;
}
if( FAILED( CreateWindowClass( hInstance ) ) )
{
MessageBox( NULL, L"Could not create window class. "
L"This application will now exit.", L"Error", MB_OK );
FullCleanup();
return 1;
}
if( FAILED( SetupMultiMon() ) )
{
FullCleanup();
return 1;
}
CDXUTTimer Timer;
float fElapsedTime = 0.0f;
double fTime = 0.0f;
Timer.Start();
fTime = Timer.GetTime();
bool bGotMsg;
MSG msg;
msg.message = WM_NULL;
PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );
while( WM_QUIT != msg.message )
{
bGotMsg = ( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) != 0 );
if( bGotMsg )
{
if( g_hWndPrimary == NULL ||
0 == TranslateAccelerator( g_hWndPrimary, NULL, &msg ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
else
{
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 );
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) ) );
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 ) );
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;
}
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 };
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 );
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 ;
pDeviceObj->pDevice->OMSetBlendState(
pDeviceObj->pBlendStateDisable, NULL, 0);
pDeviceObj->pDevice->OMSetDepthStencilState(
pDeviceObj->pDepthStencilStateDepthDisable, 0 );
pDeviceObj->pDevice->RSSetState(
pDeviceObj->pRasterizerStateNoCull );
pDeviceObj->pTechQuad->GetPassByIndex( 0 )->Apply( 0 );
DrawFullScreenQuad10( pDeviceObj, pDeviceObj->pTechQuad,
pWindowObj->Width, pWindowObj->Height ) ;
if (g_bRenderModel)
{
pDeviceObj->pDevice->OMSetDepthStencilState(
pDeviceObj->pDepthStencilStateDepthEnable, 0 );
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:
for( int i = 0; i < g_WindowObjects.GetSize(); i++ )
{
WINDOW_OBJECT* pObj = g_WindowObjects.GetAt( i );
if( hWnd == pObj->hWnd )
{
SAFE_RELEASE( pObj->pRenderTargetView );
SAFE_RELEASE( pObj->pDepthStencilView );
RECT rcCurrentClient;
GetClientRect( hWnd, &rcCurrentClient );
pObj->Width = ( UINT )rcCurrentClient.right;
pObj->Height = ( UINT )rcCurrentClient.bottom;
if ( pObj->pSwapChain )
{
DXGI_SWAP_CHAIN_DESC Desc;
pObj->pSwapChain->GetDesc( &Desc );
pObj->pSwapChain->ResizeBuffers( Desc.BufferCount,
( UINT )rcCurrentClient.right,
( UINT )rcCurrentClient.bottom,
Desc.BufferDesc.Format,
0 );
CreateViewsForWindowObject( pObj );
}
return 0;
}
}
return 0;
};
return DefWindowProc( hWnd, uMsg, wParam, lParam );
}
HRESULT InitD3D10()
{
HRESULT hr = S_OK;
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 );
hr = CreateDXGIFactory( IID_IDXGIFactory,
( void** )&g_pDXGIFactory );
if( FAILED( hr ) )
return hr;
return hr;
}
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;
}
DXGI_ADAPTER_DESC AdapterDesc;
hr = pAdapterObj->pDXGIAdapter->GetDesc( &AdapterDesc );
if( FAILED( hr ) )
{
delete pAdapterObj;
return hr;
}
hr = EnumerateOutputs( pAdapterObj );
if( FAILED( hr ) )
{
delete pAdapterObj;
return hr;
}
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;
DXGI_OUTPUT_DESC OutputDesc;
hr = pOutput->GetDesc( &OutputDesc );
if( FAILED( hr ) )
return hr;
pAdapterObj->DXGIOutputArray.Add( pOutput );
}
return hr;
}
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;
}
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;
}
ShowWindow( pWindowObj->hWnd, SW_SHOWDEFAULT );
pWindowObj->Width = Width;
pWindowObj->Height = Height;
g_WindowObjects.Add( pWindowObj );
}
}
return hr;
}
HRESULT SetWindowAssociation()
{
if( g_WindowObjects.GetSize() < 1 )
return E_FAIL;
HWND hWnd = g_WindowObjects.GetAt( 0 )->hWnd;
return g_pDXGIFactory->MakeWindowAssociation( hWnd, 0 );
}
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;
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;
pDeviceObj->Ordinal = g_DeviceArray.GetSize();
g_DeviceArray.Add( pDeviceObj );
OnD3D10CreateDevice( pDeviceObj );
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;
hr = ReleaseSwapChains();
hr = CreateSwapChainPerOutput();
if( FAILED( hr ) )
{
MessageBox( NULL, L"Could not Re-create Swap Chains for "
L"all outputs. This application will now exit.",
L"Error", MB_OK );
return hr;
}
hr = SetWindowAssociation();
if( FAILED( hr ) )
{
MessageBox( NULL, L"Could not Re-set DXGI window "
L"association. This application will now exit.", L"Error",
MB_OK );
return hr;
}
SetFocus( g_hWndPrimary );
return hr;
}
HRESULT CreateSwapChainPerOutput()
{
HRESULT hr = S_OK;
for( int i = 0; i < g_WindowObjects.GetSize(); i++ )
{
WINDOW_OBJECT* pWindowObj = g_WindowObjects.GetAt( i );
IDXGIDevice* pDXGIDevice = NULL;
hr = pWindowObj->pDeviceObj->pDevice->QueryInterface(
IID_IDXGIDevice, ( void** )&pDXGIDevice );
if( FAILED( hr ) )
return hr;
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;
}
HRESULT CreateViewsForWindowObject( WINDOW_OBJECT* pWindowObj )
{
HRESULT hr = S_OK;
ID3D10Texture2D* pBackBuffer = NULL;
hr = pWindowObj->pSwapChain->GetBuffer( 0,
IID_ID3D10Texture2D, ( void** )&pBackBuffer );
if( FAILED( hr ) )
return hr;
D3D10_TEXTURE2D_DESC BBDesc;
pBackBuffer->GetDesc( &BBDesc );
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;
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;
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;
if( pWindowObj->pAdapter )
pWindowObj->pAdapter->GetDesc( &pWindowObj->AdapterDesc );
if( pWindowObj->pOutput )
pWindowObj->pOutput->GetDesc( &pWindowObj->OutputDesc );
return hr;
}
HRESULT SetupMultiMon()
{
HRESULT hr = S_OK;
hr = CreateMonitorWindows();
if( FAILED( hr ) )
{
MessageBox( NULL, L"Could not create monitor windows. "
L"This application will now exit.", L"Error", MB_OK );
return hr;
}
hr = SetWindowAssociation();
if( FAILED( hr ) )
{
MessageBox( NULL, L"Could not set DXGI window association. "
L"This application will now exit.", L"Error", MB_OK );
return hr;
}
hr = CreateDevicePerAdapter( D3D10_DRIVER_TYPE_HARDWARE );
if( FAILED( hr ) )
{
hr = CreateDevicePerAdapter( D3D10_DRIVER_TYPE_REFERENCE );
if( FAILED( hr ) )
{
MessageBox( NULL, L"Could not create a compatible "
L"Direct3D10 device. This application will now exit.",
L"Error", MB_OK );
return hr;
}
}
hr = CreateSwapChainPerOutput();
if( FAILED( hr ) )
{
MessageBox( NULL, L"Could not create Swap Chains for "
L"all outputs. This application will now exit.",
L"Error", MB_OK );
return hr;
}
if( g_WindowObjects.GetSize() > 0 )
{
WINDOW_OBJECT* pWindowObj = g_WindowObjects.GetAt( 0 );
g_hWndPrimary = pWindowObj->hWnd;
}
return hr;
}
HRESULT ReleaseSwapChains()
{
HRESULT hr = S_OK;
for( int w = 0; w < g_WindowObjects.GetSize(); w++ )
{
WINDOW_OBJECT* pWindowObj = g_WindowObjects.GetAt( w );
SAFE_RELEASE( pWindowObj->pRenderTargetView );
SAFE_RELEASE( pWindowObj->pDepthStencilView );
pWindowObj->pSwapChain->SetFullscreenState( FALSE, NULL );
SAFE_RELEASE( pWindowObj->pSwapChain );
}
return hr;
}
void DeviceCleanup()
{
for( int w = 0; w < g_WindowObjects.GetSize(); w++ )
{
WINDOW_OBJECT* pWindowObj = g_WindowObjects.GetAt( w );
pWindowObj->pSwapChain->SetFullscreenState( FALSE, NULL );
}
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();
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 );
SAFE_RELEASE( pDeviceObj->pScreenQuadVB );
SAFE_RELEASE( pDeviceObj->pDevice );
SAFE_DELETE( pDeviceObj );
}
g_DeviceArray.RemoveAll();
}
void FullCleanup()
{
DeviceCleanup();
for( int a = 0; a < g_AdapterArray.GetSize(); a++ )
{
ADAPTER_OBJECT* pAdapterObj = g_AdapterArray.GetAt( a );
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 ) );
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();
}
void RenderToAllMonitors( double fTime, float fElapsedTime )
{
for( int w = 0; w < g_WindowObjects.GetSize(); w++ )
{
WINDOW_OBJECT* pWindowObj = g_WindowObjects.GetAt( w );
pWindowObj->pDeviceObj->pDevice->OMSetRenderTargets( 1,
&pWindowObj->pRenderTargetView,
pWindowObj->pDepthStencilView );
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 );
}
}
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 )
{
ID3D10Device* pd3dDevice = pDeviceObj->pDevice;
D3D10_VIEWPORT vpOld[D3D10_VIEWPORT_AND_SCISSORRECT_MAX_INDEX];
UINT nViewPorts = 1;
pd3dDevice->RSGetViewports( &nViewPorts, vpOld );
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 );
}
pd3dDevice->RSSetViewports( nViewPorts, vpOld );
}
This code, like any other 3D gaming technology code, appears very involved. Sections of this code can be broken down, however, to clarify how it works. Before we start with initializing Direct3D, let's take a brief note about the top of the file. Below the header files are global declarations. Below those global declarations are function prototypes. Those and some other data structures are itemized, until we get to the program entry point, the WinMain()
function.
The process of initializing Direct3D begins by describing the characteristics of the swap chain to be created by filling out an instance of the DXGI_SWAP_CHAIN_DESC
structure. This and the following steps are listed below:
- Create the
ID3D10Device
and IDXGISwapChain
interfaces using the D3D10CreateDeviceAndSwapChain
function.
- Create a render target view to the swap chain's back buffer.
- Create the depth/stencil buffer and its associated depth/stencil view.
- Bind the render target view and depth/stencil view to the output merger stage of the rendering pipeline so that they can be used by Direct3D.
- Set the viewport.
Describe the Swap Chain
To repeat, initializing Direct3D begins by filling out an instance of the DXGI_SWAP_CHAIN_DESC
structure, which describes the characteristics of the swap chain we are going to create. Here is a generic structure:
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:
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:
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:
hr = g_pDXGIFactory->CreateSwapChain( pDXGIDevice,
&SwapChainDesc, &pWindowObj > pSwapChain );
pDXGIDevice->Release();
pDXGIDevice = NULL;
Creating a Depth/Stencil Buffer
Wel will not go into the particulars of a depth stencil buffer. For now, though, we need to set one up in order to be able to render properly. What is important to know is that a depth buffer is used to provide pixel accurate depth testing so that when you render an object in front of another object, they don't come out all mangled up. Stencil buffers are used for advanced effects like volume shadowing. To create a depth/stencil buffer, we start by creating a 2D texture with the same resolution as our back buffer. We do this by filling out a D3D10_TEXTURE2D_DESC
structure, and we will fill it out like this:
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:
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:
HRESULT CreateViewsForWindowObject( WINDOW_OBJECT* pWindowObj )
{
HRESULT hr = S_OK;
ID3D10Texture2D* pBackBuffer = NULL;
hr = pWindowObj->pSwapChain->GetBuffer( 0, IID_ID3D10Texture2D,
( void** )&pBackBuffer );
if( FAILED( hr ) )
return hr;
D3D10_TEXTURE2D_DESC BBDesc;
pBackBuffer->GetDesc( &BBDesc );
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;
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;
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;
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:
typedef struct D3D10_VIEWPORT {
INT TopLeftX;
INT TopLeftY;
UINT Width;
UINT Height;
FLOAT MinDepth;
FLOAT MaxDepth;
} D3D10_VIEWPORT;
Now examine the sample 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:
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:
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 has been used. This next example is called deferred particles, and is one of the many particle system examples of DirectX code. Particle systems create smoke, fire, rain, blood, water, and other rainy elements. Extremely tiny particles stream (or emit) from a source, and either accumulate to form a stream or detonate to exhibit light.
Particle Systems
Examine this example of a deferred particle system:
Here is the main source file:
-----------------
#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"
CDXUTDialogResourceManager g_DialogResourceManager;
CModelViewerCamera g_Camera;
CDXUTDirectionWidget g_LightControl;
CD3DSettingsDlg g_D3DSettingsDlg;
CDXUTDialog g_HUD; CDXUTDialog g_SampleUI;
CDXUTTextHelper* g_pTxtHelper = NULL;
ID3DX10Font* g_pFont10 = NULL;
ID3DX10Sprite* g_pSprite10 = NULL;
CDXUTSDKMesh g_WallMesh;
CDXUTSDKMesh g_ChunkMesh[NUM_CHUNKS];
#define MAX_BUILDINGS 5
CBuilding g_Building[MAX_BUILDINGS];
CGrowableArray <D3DXMATRIX> g_BaseMeshMatrices;
CGrowableArray <D3DXMATRIX> g_ChunkMeshMatrices[NUM_CHUNKS];
ID3D10InputLayout* g_pVertexLayout = NULL;
ID3D10InputLayout* g_pScreenQuadLayout = NULL;
ID3D10InputLayout* g_pMeshLayout = NULL;
UINT g_NumParticles = 200;
float g_fSpread = 4.0f;
float g_fStartSize = 0.0f;
float g_fEndSize = 10.0f;
float g_fSizeExponent = 128.0f;
float g_fMushroomCloudLifeSpan = 10.0f;
float g_fGroundBurstLifeSpan = 9.0f;
float g_fPopperLifeSpan = 9.0f;
float g_fMushroomStartSpeed = 20.0f;
float g_fStalkStartSpeed = 50.0f;
float g_fGroundBurstStartSpeed = 100.0f;
float g_fLandMineStartSpeed = 250.0f;
float g_fEndSpeed = 4.0f;
float g_fSpeedExponent = 32.0f;
float g_fFadeExponent = 4.0f;
float g_fRollAmount = 0.2f;
float g_fWindFalloff = 20.0f;
D3DXVECTOR3 g_vPosMul( 1,1,1 );
D3DXVECTOR3 g_vDirMul( 1,1,1 );
D3DXVECTOR3 g_vWindVel( -2.0f,10.0f,0 );
D3DXVECTOR3 g_vGravity( 0,-9.8f,0.0f );
float g_fGroundPlane = 0.5f;
float g_fLightRaise = 1.0f;
float g_fWorldBounds = 100.0f;
#define MAX_FLASH_COLORS 4
D3DXVECTOR4 g_vFlashColor[MAX_FLASH_COLORS] =
{
D3DXVECTOR4( 1.0f, 0.5f, 0.00f, 0.9f ),
D3DXVECTOR4( 1.0f, 0.3f, 0.05f, 0.9f ),
D3DXVECTOR4( 1.0f, 0.4f, 0.00f, 0.9f ),
D3DXVECTOR4( 0.8f, 0.3f, 0.05f, 0.9f )
};
D3DXVECTOR4 g_vFlashAttenuation( 0,0.0f,3.0f,0 );
D3DXVECTOR4 g_vMeshLightAttenuation( 0,0,1.5f,0 );
float g_fFlashLife = 0.50f;
float g_fFlashIntensity = 1000.0f;
UINT g_NumParticlesToDraw = 0;
#define MAX_MUSHROOM_CLOUDS 8
#define MAX_GROUND_BURSTS 23
#define MAX_PARTICLE_SYSTEMS 30
#define MAX_FLASH_LIGHTS 8
#define MAX_INSTANCES 200
CParticleSystem** g_ppParticleSystem = NULL;
ID3D10Buffer* g_pParticleBuffer = NULL;
ID3D10Buffer* g_pScreenQuadVB = NULL;
ID3D10Texture2D* g_pOffscreenParticleTex = NULL;
ID3D10ShaderResourceView* g_pOffscreenParticleSRV = NULL;
ID3D10RenderTargetView* g_pOffscreenParticleRTV = NULL;
ID3D10Texture2D* g_pOffscreenParticleColorTex = NULL;
ID3D10ShaderResourceView* g_pOffscreenParticleColorSRV = NULL;
ID3D10RenderTargetView* g_pOffscreenParticleColorRTV = NULL;
ID3D10ShaderResourceView* g_pParticleTextureSRV = NULL;
ID3D10Effect* g_pEffect10 = NULL;
ID3D10EffectTechnique* g_pRenderParticlesToBuffer = NULL;
ID3D10EffectTechnique* g_pRenderParticles = NULL;
ID3D10EffectTechnique* g_pCompositeParticlesToScene = NULL;
ID3D10EffectTechnique* g_pRenderMesh = NULL;
ID3D10EffectTechnique* g_pRenderMeshInst = NULL;
ID3D10EffectShaderResourceVariable* g_ptxDiffuse = NULL;
ID3D10EffectShaderResourceVariable* g_ptxParticleColor = NULL;
ID3D10EffectVectorVariable* g_pLightDir = NULL;
ID3D10EffectMatrixVariable* g_pmWorldViewProjection = NULL;
ID3D10EffectMatrixVariable* g_pmWorld = NULL;
ID3D10EffectMatrixVariable* g_pmInvViewProj = NULL;
ID3D10EffectScalarVariable* g_pfTime = NULL;
ID3D10EffectVectorVariable* g_pvEyePt = NULL;
ID3D10EffectVectorVariable* g_pvRight = NULL;
ID3D10EffectVectorVariable* g_pvUp = NULL;
ID3D10EffectVectorVariable* g_pvForward = NULL;
ID3D10EffectScalarVariable* g_pNumGlowLights = NULL;
ID3D10EffectVectorVariable* g_pvGlowLightPosIntensity = NULL;
ID3D10EffectVectorVariable* g_pvGlowLightColor = NULL;
ID3D10EffectVectorVariable* g_pvGlowLightAttenuation = NULL;
ID3D10EffectVectorVariable* g_pvMeshLightAttenuation = NULL;
ID3D10EffectMatrixVariable* g_pmViewProj = NULL;
ID3D10EffectMatrixVariable* g_pmWorldInst = NULL;
bool g_bRenderDeferred = true;
#define IDC_TOGGLEFULLSCREEN 1
#define IDC_TOGGLEREF 3
#define IDC_CHANGEDEVICE 4
#define IDC_RESET 50
#define IDC_DEFERRED 51
#define IDC_TOGGLEWARP 5
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();
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
DXUTGetD3D10Enumeration( false, true );
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 );
DXUTSetCursorSettings( true, true );
DXUTCreateWindow( L"DeferredParticles" );
DXUTCreateDevice( true, 640, 480 );
DXUTMainLoop();
return DXUTGetExitCode();
}
void InitApp()
{
D3DXVECTOR3 vDir( 1,1,0 );
D3DXVec3Normalize( &vDir, &vDir );
g_LightControl.SetLightDirection( vDir );
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;
D3DXVECTOR3 vecEye( 0.0f, 150.0f, 336.0f );
D3DXVECTOR3 vecAt ( 0.0f, 0.0f, 0.0f );
g_Camera.SetViewParams( &vecEye, &vecAt );
}
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings*
pDeviceSettings, void* pUserContext )
{
pDeviceSettings->d3d10.SyncInterval = 0;
g_D3DSettingsDlg.GetDialogControl()->GetComboBox(
DXUTSETTINGSDLG_PRESENT_INTERVAL )->SetEnabled( false );
g_D3DSettingsDlg.GetDialogControl()->GetComboBox(
DXUTSETTINGSDLG_D3D10_MULTISAMPLE_COUNT )->SetEnabled( false );
g_D3DSettingsDlg.GetDialogControl()->GetComboBox(
DXUTSETTINGSDLG_D3D10_MULTISAMPLE_QUALITY )->SetEnabled( false );
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 );
}
}
void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext )
{
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];
for( UINT i = 0; i < MAX_BUILDINGS; i++ )
{
g_Building[i].AdvancePieces( fElapsedTime, g_vGravity );
}
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 ++;
}
}
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 ++;
}
}
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 ++;
}
}
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 );
}
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();
}
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, bool* pbNoFurtherProcessing,
void* pUserContext )
{
*pbNoFurtherProcessing = g_DialogResourceManager.MsgProc(
hWnd, uMsg, wParam, lParam );
if( *pbNoFurtherProcessing )
return 0;
if( g_D3DSettingsDlg.IsActive() )
{
g_D3DSettingsDlg.MsgProc( hWnd, uMsg, wParam, lParam );
return 0;
}
*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 );
g_Camera.HandleMessages( hWnd, uMsg, wParam, lParam );
return 0;
}
void CALLBACK OnKeyboard( UINT nChar, bool bKeyDown,
bool bAltDown, void* pUserContext )
{
}
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;
}
}
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] );
}
}
}
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 )
dwShaderFlags |= D3D10_SHADER_DEBUG;
#endif
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 ) );
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" );
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();
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 ) );
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" ) );
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();
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 ) );
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;
}
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 ) );
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 );
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;
}
void RenderParticles( ID3D10Device* pd3dDevice,
ID3D10EffectTechnique* pRenderTechnique )
{
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 );
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 );
}
}
void RenderParticlesIntoBuffer( ID3D10Device* pd3dDevice )
{
float color[4] =
{
0, 0, 0, 0
};
pd3dDevice->ClearRenderTargetView( g_pOffscreenParticleRTV, color );
pd3dDevice->ClearRenderTargetView( g_pOffscreenParticleColorRTV, color );
ID3D10RenderTargetView* pOldRTV;
ID3D10DepthStencilView* pOldDSV;
pd3dDevice->OMGetRenderTargets( 1, &pOldRTV, &pOldDSV );
ID3D10RenderTargetView* pViews[2];
pViews[0] = g_pOffscreenParticleRTV;
pViews[1] = g_pOffscreenParticleColorRTV;
pd3dDevice->OMSetRenderTargets( 2, pViews, pOldDSV );
RenderParticles( pd3dDevice, g_pRenderParticlesToBuffer );
pViews[0] = pOldRTV;
pViews[1] = NULL;
pd3dDevice->OMSetRenderTargets( 2, pViews, pOldDSV );
SAFE_RELEASE( pOldRTV );
SAFE_RELEASE( pOldDSV );
}
void CompositeParticlesIntoScene( ID3D10Device* pd3dDevice )
{
ID3D10EffectTechnique* pRenderTechnique = g_pCompositeParticlesToScene;
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 );
D3D10_TECHNIQUE_DESC techDesc;
pRenderTechnique->GetDesc( &techDesc );
for( UINT p = 0; p < techDesc.Passes; ++p )
{
pRenderTechnique->GetPassByIndex( p )->Apply( 0 );
pd3dDevice->Draw( 4, 0 );
}
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 );
}
}
}
void CALLBACK OnD3D10FrameRender( ID3D10Device* pd3dDevice,
double fTime, float fElapsedTime, void* pUserContext )
{
HRESULT hr;
if( g_D3DSettingsDlg.IsActive() )
{
g_D3DSettingsDlg.OnRender( fElapsedTime );
return;
}
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;
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 );
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 ) );
g_BaseMeshMatrices.Reset();
for( UINT i = 0; i < NUM_CHUNKS; i++ )
{
g_ChunkMeshMatrices[i].Reset();
}
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] );
}
}
pd3dDevice->IASetInputLayout( g_pMeshLayout );
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;
}
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;
}
}
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();
}
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 );
}
void CALLBACK OnD3D10DestroyDevice( void* pUserContext )
{
g_DialogResourceManager.OnD3D10DestroyDevice();
g_D3DSettingsDlg.OnD3D10DestroyDevice();
CDXUTDirectionWidget::StaticOnD3D10DestroyDevice();
DXUTGetGlobalResourceCache().OnDestroyDevice();
SAFE_DELETE( g_pTxtHelper );
SAFE_RELEASE( g_pFont10 );
SAFE_RELEASE( g_pSprite10 );
SAFE_RELEASE( g_pEffect10 );
SAFE_RELEASE( g_pVertexLayout );
SAFE_RELEASE( g_pScreenQuadLayout );
SAFE_RELEASE( g_pMeshLayout );
g_WallMesh.Destroy();
for( UINT i = 0; i < NUM_CHUNKS; i++ )
{
g_ChunkMesh[i].Destroy();
}
SAFE_RELEASE( g_pScreenQuadVB );
for( UINT i = 0; i < MAX_PARTICLE_SYSTEMS; i++ )
{
SAFE_DELETE( g_ppParticleSystem[i] );
}
SAFE_DELETE_ARRAY( g_ppParticleSystem );
SAFE_RELEASE( g_pParticleBuffer );
SAFE_RELEASE( g_pParticleTextureSRV );
DestroyParticleArray();
}
Particle systems representing smoke are often rendered as a series of screen-aligned sprites. Any lighting information is computed when the particle is rendered. The resulting lit particle is alpha-blended over the top of existing scenery or particles. This results in a flat-looking particle system. Whereas the system is supposed to represent a volume of smoke, the particles are still light as if they are individual entities. Rendering the particles in a deferred manner allows us to accumulate normals and opacity in an offscreen buffer and then light the accumulated data in another pass. This allows us to treat and light the particle system as one continuous entity rather than individual sprites. The particles are lit not only by the scene light (the sun), but also by internal lights generated by the explosions themselves. Each light has a short but intense light at the center of each explosion. These lights can affect particles in neighboring explosions as well; therefore, there is a limit on the maximum number of lights that can be active at any one time. In the vertex shader, each vertex loops over the active lights in the scene, and calculates the intensity of the light at that vertex using a quadratic falloff. This light intensity is also stored in an offscreen buffer and used in the lighting pass to add extra light to the particles. Since this interior light is not directional, it is simply added to the particles without regard for the particles' orientation of the normals (Reference: Microsoft DirectX SDK).
References
- The MSDN on DirectX and the DirectX SDK (June 2010)
- Introduction to #D Game Programming, written by Peter Walsh