topical media & game development
graphic-directx-game-16-Skinned-Mesh-Demo-d3dApp.cpp / cpp
//=============================================================================
// d3dApp.h by Frank Luna (C) 2005 All Rights Reserved.
//=============================================================================
include <d3dApp.h>
D3DApp* gd3dApp = 0;
IDirect3DDevice9* gd3dDevice = 0;
LRESULT CALLBACK
MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Don't start processing messages until the application has been created.
if( gd3dApp != 0 )
return gd3dApp->msgProc(msg, wParam, lParam);
else
return DefWindowProc(hwnd, msg, wParam, lParam);
}
D3DApp::D3DApp(HINSTANCE hInstance, std::string winCaption, D3DDEVTYPE devType, DWORD requestedVP)
{
mMainWndCaption = winCaption;
mDevType = devType;
mRequestedVP = requestedVP;
mhAppInst = hInstance;
mhMainWnd = 0;
md3dObject = 0;
mAppPaused = false;
ZeroMemory(&md3dPP, sizeof(md3dPP));
initMainWindow();
initDirect3D();
}
D3DApp::~D3DApp()
{
ReleaseCOM(md3dObject);
ReleaseCOM(gd3dDevice);
}
HINSTANCE D3DApp::getAppInst()
{
return mhAppInst;
}
HWND D3DApp::getMainWnd()
{
return mhMainWnd;
}
void D3DApp::initMainWindow()
{
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = MainWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = mhAppInst;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = "D3DWndClassName";
if( !RegisterClass(&wc) )
{
MessageBox(0, "RegisterClass FAILED", 0, 0);
PostQuitMessage(0);
}
// Default to a window with a client area rectangle of 800x600.
RECT R = {0, 0, 800, 600};
AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, false);
mhMainWnd = CreateWindow("D3DWndClassName", mMainWndCaption.c_str(),
WS_OVERLAPPEDWINDOW, 100, 100, R.right, R.bottom,
0, 0, mhAppInst, 0);
if( !mhMainWnd )
{
MessageBox(0, "CreateWindow FAILED", 0, 0);
PostQuitMessage(0);
}
ShowWindow(mhMainWnd, SW_SHOW);
UpdateWindow(mhMainWnd);
}
void D3DApp::initDirect3D()
{
// Step 1: Create the IDirect3D9 object.
md3dObject = Direct3DCreate9(D3D_SDK_VERSION);
if( !md3dObject )
{
MessageBox(0, "Direct3DCreate9 FAILED", 0, 0);
PostQuitMessage(0);
}
// Step 2: Verify hardware support for specified formats in windowed and full screen modes.
D3DDISPLAYMODE mode;
md3dObject->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &mode);
HR(md3dObject->CheckDeviceType(D3DADAPTER_DEFAULT, mDevType, mode.Format, mode.Format, true));
HR(md3dObject->CheckDeviceType(D3DADAPTER_DEFAULT, mDevType, D3DFMT_X8R8G8B8, D3DFMT_X8R8G8B8, false));
// Step 3: Check for requested vertex processing and pure device.
D3DCAPS9 caps;
HR(md3dObject->GetDeviceCaps(D3DADAPTER_DEFAULT, mDevType, &caps));
DWORD devBehaviorFlags = 0;
if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
devBehaviorFlags |= mRequestedVP;
else
devBehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;
// If pure device and HW T&L supported
if( caps.DevCaps & D3DDEVCAPS_PUREDEVICE &&
devBehaviorFlags & D3DCREATE_HARDWARE_VERTEXPROCESSING)
devBehaviorFlags |= D3DCREATE_PUREDEVICE;
// Step 4: Fill out the D3DPRESENT_PARAMETERS structure.
md3dPP.BackBufferWidth = 0;
md3dPP.BackBufferHeight = 0;
md3dPP.BackBufferFormat = D3DFMT_UNKNOWN;
md3dPP.BackBufferCount = 1;
md3dPP.MultiSampleType = D3DMULTISAMPLE_NONE;
md3dPP.MultiSampleQuality = 0;
md3dPP.SwapEffect = D3DSWAPEFFECT_DISCARD;
md3dPP.hDeviceWindow = mhMainWnd;
md3dPP.Windowed = true;
md3dPP.EnableAutoDepthStencil = true;
md3dPP.AutoDepthStencilFormat = D3DFMT_D24S8;
md3dPP.Flags = 0;
md3dPP.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
md3dPP.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
// Step 5: Create the device.
HR(md3dObject->CreateDevice(
D3DADAPTER_DEFAULT, // primary adapter
mDevType, // device type
mhMainWnd, // window associated with device
devBehaviorFlags, // vertex processing
&md3dPP, // present parameters
&gd3dDevice)); // return created device
}
int D3DApp::run()
{
MSG msg;
msg.message = WM_NULL;
__int64 cntsPerSec = 0;
QueryPerformanceFrequency((LARGE_INTEGER*)&cntsPerSec);
float secsPerCnt = 1.0f / (float)cntsPerSec;
__int64 prevTimeStamp = 0;
QueryPerformanceCounter((LARGE_INTEGER*)&prevTimeStamp);
while(msg.message != WM_QUIT)
{
// If there are Window messages then process them.
if(PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
// Otherwise, do animation/game stuff.
else
{
// If the application is paused then free some CPU cycles to other
// applications and then continue on to the next frame.
if( mAppPaused )
{
Sleep(20);
continue;
}
if( !isDeviceLost() )
{
__int64 currTimeStamp = 0;
QueryPerformanceCounter((LARGE_INTEGER*)&currTimeStamp);
float dt = (currTimeStamp - prevTimeStamp)*secsPerCnt;
updateScene(dt);
drawScene();
// Prepare for next iteration: The current time stamp becomes
// the previous time stamp for the next iteration.
prevTimeStamp = currTimeStamp;
}
}
}
return (int)msg.wParam;
}
LRESULT D3DApp::msgProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
// Is the application in a minimized or maximized state?
static bool minOrMaxed = false;
RECT clientRect = {0, 0, 0, 0};
switch( msg )
{
// WM_ACTIVE is sent when the window is activated or deactivated.
// We pause the game when the main window is deactivated and
// unpause it when it becomes active.
case WM_ACTIVATE:
if( LOWORD(wParam) == WA_INACTIVE )
mAppPaused = true;
else
mAppPaused = false;
return 0;
// WM_SIZE is sent when the user resizes the window.
case WM_SIZE:
if( gd3dDevice )
{
md3dPP.BackBufferWidth = LOWORD(lParam);
md3dPP.BackBufferHeight = HIWORD(lParam);
if( wParam == SIZE_MINIMIZED )
{
mAppPaused = true;
minOrMaxed = true;
}
else if( wParam == SIZE_MAXIMIZED )
{
mAppPaused = false;
minOrMaxed = true;
onLostDevice();
HR(gd3dDevice->Reset(&md3dPP));
onResetDevice();
}
// Restored is any resize that is not a minimize or maximize.
// For example, restoring the window to its default size
// after a minimize or maximize, or from dragging the resize
// bars.
else if( wParam == SIZE_RESTORED )
{
mAppPaused = false;
// Are we restoring from a mimimized or maximized state,
// and are in windowed mode? Do not execute this code if
// we are restoring to full screen mode.
if( minOrMaxed && md3dPP.Windowed )
{
onLostDevice();
HR(gd3dDevice->Reset(&md3dPP));
onResetDevice();
}
else
{
// No, which implies the user is resizing by dragging
// the resize bars. However, we do not reset the device
// here because as the user continuously drags the resize
// bars, a stream of WM_SIZE messages is sent to the window,
// and it would be pointless (and slow) to reset for each
// WM_SIZE message received from dragging the resize bars.
// So instead, we reset after the user is done resizing the
// window and releases the resize bars, which sends a
// WM_EXITSIZEMOVE message.
}
minOrMaxed = false;
}
}
return 0;
// WM_EXITSIZEMOVE is sent when the user releases the resize bars.
// Here we reset everything based on the new window dimensions.
case WM_EXITSIZEMOVE:
GetClientRect(mhMainWnd, &clientRect);
md3dPP.BackBufferWidth = clientRect.right;
md3dPP.BackBufferHeight = clientRect.bottom;
onLostDevice();
HR(gd3dDevice->Reset(&md3dPP));
onResetDevice();
return 0;
// WM_CLOSE is sent when the user presses the 'X' button in the
// caption bar menu.
case WM_CLOSE:
DestroyWindow(mhMainWnd);
return 0;
// WM_DESTROY is sent when the window is being destroyed.
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_KEYDOWN:
if( wParam == VK_ESCAPE )
enableFullScreenMode(false);
else if( wParam == 'F' )
enableFullScreenMode(true);
return 0;
}
return DefWindowProc(mhMainWnd, msg, wParam, lParam);
}
void D3DApp::enableFullScreenMode(bool enable)
{
// Switch to fullscreen mode.
if( enable )
{
// Are we already in fullscreen mode?
if( !md3dPP.Windowed )
return;
int width = GetSystemMetrics(SM_CXSCREEN);
int height = GetSystemMetrics(SM_CYSCREEN);
md3dPP.BackBufferFormat = D3DFMT_X8R8G8B8;
md3dPP.BackBufferWidth = width;
md3dPP.BackBufferHeight = height;
md3dPP.Windowed = false;
// Change the window style to a more fullscreen friendly style.
SetWindowLongPtr(mhMainWnd, GWL_STYLE, WS_POPUP);
// If we call SetWindowLongPtr, MSDN states that we need to call
// SetWindowPos for the change to take effect. In addition, we
// need to call this function anyway to update the window dimensions.
SetWindowPos(mhMainWnd, HWND_TOP, 0, 0, width, height, SWP_NOZORDER | SWP_SHOWWINDOW);
}
// Switch to windowed mode.
else
{
// Are we already in windowed mode?
if( md3dPP.Windowed )
return;
RECT R = {0, 0, 800, 600};
AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, false);
md3dPP.BackBufferFormat = D3DFMT_UNKNOWN;
md3dPP.BackBufferWidth = 800;
md3dPP.BackBufferHeight = 600;
md3dPP.Windowed = true;
// Change the window style to a more windowed friendly style.
SetWindowLongPtr(mhMainWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
// If we call SetWindowLongPtr, MSDN states that we need to call
// SetWindowPos for the change to take effect. In addition, we
// need to call this function anyway to update the window dimensions.
SetWindowPos(mhMainWnd, HWND_TOP, 100, 100, R.right, R.bottom, SWP_NOZORDER | SWP_SHOWWINDOW);
}
// Reset the device with the changes.
onLostDevice();
HR(gd3dDevice->Reset(&md3dPP));
onResetDevice();
}
bool D3DApp::isDeviceLost()
{
// Get the state of the graphics device.
HRESULT hr = gd3dDevice->TestCooperativeLevel();
// If the device is lost and cannot be reset yet then
// sleep for a bit and we'll try again on the next
// message loop cycle.
if( hr == D3DERR_DEVICELOST )
{
Sleep(20);
return true;
}
// Driver error, exit.
else if( hr == D3DERR_DRIVERINTERNALERROR )
{
MessageBox(0, "Internal Driver Error...Exiting", 0, 0);
PostQuitMessage(0);
return true;
}
// The device is lost but we can reset and restore it.
else if( hr == D3DERR_DEVICENOTRESET )
{
onLostDevice();
HR(gd3dDevice->Reset(&md3dPP));
onResetDevice();
return false;
}
else
return false;
}
(C) Æliens
20/2/2008
You may not copy or print any of this material without explicit permission of the author or the publisher.
In case of other copyright issues, contact the author.