DirectX10 Rendering Engine – Engine.cpp
/* Portions of this class derived from Programming a Multiplayer First Person Shooter in DirectX by Vaughan Young and Beginning DirectX 10 Game Programming by Wendy Jones */ #include "engine.h" #include "dx_utils.h" // much prettier color to clear with #define COLOR_CORNFLOWERBLUE D3DXCOLOR( 100.0f / 255.0f, 149.0f / 255.0f, 237.0f / 255.0f, 255.0f / 255.0f ) #define COLOR_GREY D3DXCOLOR( 0.1f, 0.1f, 0.1f, 1.0f ) LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Rendering Engine CEngine* Engine( void ) { static CEngine g_pEngine; return &g_pEngine; } CEngine::CEngine() { m_pD3DDevice = NULL; m_pSwapChain = NULL; m_pRenderTargetView = NULL; m_pDepthStencil = NULL; m_pDepthStencilView = NULL; m_pRasterState = NULL; m_pAlphaBlendState = NULL; m_pSetup = NULL; m_pMaterialManager = NULL; m_pTextureManager = NULL; m_pEffectManager = NULL; m_pSoundResManager = NULL; m_pInputSystem = NULL; m_pSoundManager = NULL; m_nFps = 0; m_bLoaded = false; } CEngine::~CEngine( void ) { if (m_bLoaded) ShutdownDirect3D(); SAFE_DELETE(m_pSetup); SAFE_DELETE(m_pMaterialManager); SAFE_DELETE(m_pTextureManager); SAFE_DELETE(m_pEffectManager); SAFE_DELETE(m_pSoundResManager); SAFE_DELETE(m_pInputSystem); SAFE_DELETE(m_pSoundManager); } /******************************************************************* * Init * Initialize the engine * Inputs - void * Outputs - True on success, false otherwise *******************************************************************/ bool CEngine::Init( EngineSetup* pSetup ) { // If no setup structure was passed in, then create a default one. // Otherwise, make a copy of the passed in structure. m_pSetup = new EngineSetup; if( pSetup != NULL ) memcpy( m_pSetup, pSetup, sizeof( EngineSetup ) ); m_nWindowWidth = m_pSetup->nWindowWidth; m_nWindowHeight = m_pSetup->nWindowHeight; if ( !InitWindow( m_pSetup->hInstance, m_nWindowWidth, m_nWindowHeight) ) return false; if ( !InitDirect3D( m_hWindow, m_nWindowWidth, m_nWindowHeight) ) return false; // initialize sub systems m_pInputSystem = new CInputSystem(); if ( !m_pInputSystem->Init() ) return false; m_pSoundManager = new CSoundManager(); if ( FAILED( m_pSoundManager->Initialize(m_hWindow, DSSCL_PRIORITY) ) ) return false; HRESULT hr; // set up our alpha blend state D3D10_BLEND_DESC blendDesc; ZeroMemory( &blendDesc, sizeof(D3D10_BLEND_DESC) ); blendDesc.AlphaToCoverageEnable = false; blendDesc.BlendEnable[0] = true; blendDesc.SrcBlend = D3D10_BLEND_SRC_ALPHA; blendDesc.DestBlend = D3D10_BLEND_INV_SRC_ALPHA; blendDesc.BlendOp = D3D10_BLEND_OP_ADD; blendDesc.SrcBlendAlpha = D3D10_BLEND_ZERO; blendDesc.DestBlendAlpha = D3D10_BLEND_ZERO; blendDesc.BlendOpAlpha = D3D10_BLEND_OP_ADD; blendDesc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL; hr = m_pD3DDevice->CreateBlendState( &blendDesc, &m_pAlphaBlendState ); // failed if ( hr != S_OK ) { m_pAlphaBlendState = NULL; return false; } // create resource managers m_pMaterialManager = new CResourceManager<CMaterial>( CMaterial::CreateMaterialResource, "LoadMaterial", CMaterial::Lua_SetMaterial ); m_pTextureManager = new CResourceManager<CTexture>( CTexture::CreateTextureResource ); m_pEffectManager = new CResourceManager<CEffect>( CEffect::CreateEffectResource ); m_pSoundResManager = new CResourceManager<CSoundRes>( CSoundRes::CreateSoundResource ); // Allow the application to perform any state setup now. if( m_pSetup->StateSetup != NULL ) m_pSetup->StateSetup(); // The engine is fully loaded and ready to go. m_bLoaded = true; return true; } void CEngine::Run( void ) { if (!m_bLoaded) return; // timing vars LARGE_INTEGER nTimeStart; LARGE_INTEGER nTimeEnd; LARGE_INTEGER nTimingFreq; float timeDelta = 0.0f; int nFrameCount = 0; float fTimeCount = 0.0f; QueryPerformanceFrequency( &nTimingFreq ); // Main message loop MSG msg = {0}; while (WM_QUIT != msg.message) { QueryPerformanceCounter( &nTimeStart ); // Process Windows messages first while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE) { TranslateMessage(&msg); DispatchMessage(&msg); } // Perform any needed updates Update( timeDelta ); // Render Render( timeDelta ); QueryPerformanceCounter( &nTimeEnd ); timeDelta = ( (float)nTimeEnd.QuadPart - (float)nTimeStart.QuadPart ) / nTimingFreq.QuadPart; // keep track of FPS fTimeCount += timeDelta; if (fTimeCount > 1.0f) { m_nFps = nFrameCount; nFrameCount = 0; fTimeCount = 0.0f; // set window title to include fps char newTitle[256]; memset(newTitle,NULL,256); sprintf_s(newTitle, 256, "%s - FPS: %i", m_pSetup->szName, m_nFps); SetWindowText(m_hWindow, newTitle); } else { nFrameCount++; } } // ensure all states are closed for ( std::list<CState*>::iterator it = m_States.begin(); it != m_States.end(); it++ ) { delete *it; } // Clean up the resources we allocated ShutdownDirect3D(); m_bLoaded = false; } void CEngine::Update( const float fdTime ) { // Used to retrieve details about the viewer from the application. ViewerSetup viewer; m_pInputSystem->Update(fdTime); // Request the viewer from the current state, if there is one. if( m_pCurrentState != NULL ) m_pCurrentState->RequestViewer( &viewer ); // TODO: Account for state changes m_bStateChanged = false; if( m_pCurrentState != NULL ) m_pCurrentState->Update( fdTime ); } /******************************************************************* * Render * All drawing happens in the Render function * Inputs - Time delta * Outputs - void *******************************************************************/ void CEngine::Render( const float fdTime ) { if ( m_pD3DDevice == NULL || !m_bLoaded ) return; // clear render target m_pD3DDevice->ClearRenderTargetView( m_pRenderTargetView, COLOR_GREY ); m_pD3DDevice->ClearDepthStencilView( m_pDepthStencilView, D3D10_CLEAR_DEPTH, 1.0f, 0 ); // render our state if ( m_pCurrentState != NULL ) m_pCurrentState->Render(); // display the next item in the swap chain m_pSwapChain->Present(0, 0); } //----------------------------------------------------------------------------- // Adds a state to the engine. //----------------------------------------------------------------------------- void CEngine::AddState( CState* pState, bool change ) { m_States.push_back(pState); if( change == false ) return; if( m_pCurrentState != NULL ) m_pCurrentState->Close(); m_pCurrentState = m_States.back(); m_pCurrentState->Load(); } //----------------------------------------------------------------------------- // Removes a state from the engine //----------------------------------------------------------------------------- void CEngine::RemoveState( CState* pState ) { m_States.remove(pState); } //----------------------------------------------------------------------------- // Changes processing to the state with the specified ID. //----------------------------------------------------------------------------- void CEngine::ChangeState( unsigned long id ) { CState* pState = NULL; // Iterate through the list of states and find the new state to change to. for (std::list<CState*>::iterator it = m_States.begin(); it != m_States.end(); it++) { pState = static_cast<CState*>(*it); if( pState->GetID() == id ) { // Close the old state. if( m_pCurrentState != NULL ) m_pCurrentState->Close(); // Set the new current state and load it. m_pCurrentState = pState; m_pCurrentState->Load(); /* Not entirely sure if this needs to be done in DX10, will require a bit of research // Swap the back buffers until the first one is in the front. while( m_nCurrentBackBuffer != 0 ) { m_pD3DDevice->Present( NULL, NULL, NULL, NULL ); if( ++m_currentBackBuffer == m_setup->totalBackBuffers + 1 ) m_currentBackBuffer = 0; } */ // Indicate that the state has changed. m_bStateChanged = true; break; } } } /******************************************************************* * InitWindow * Inits and creates and main app window * Inputs - application instance - HINSTANCE Window width - int Window height - int * Outputs - true if successful, false if failed - bool *******************************************************************/ bool CEngine::InitWindow(HINSTANCE hInstance, int width, int height) { // Register class WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = 0; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = TEXT("DirectXEngine"); wcex.hIconSm = 0; if(!RegisterClassEx(&wcex)) { return false; } // Create window RECT rect = { 0, 0, width, height }; AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, false); // create the window from the class above m_hWindow = CreateWindow(TEXT("DirectXEngine"), m_pSetup->szName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, hInstance, NULL); if(!m_hWindow) { return false; } RECT rc; int screenWidth = GetSystemMetrics(SM_CXSCREEN); int screenHeight = GetSystemMetrics(SM_CYSCREEN); GetWindowRect(m_hWindow, &rc); SetWindowPos(m_hWindow, 0, (screenWidth - (rc.right - rc.left) )/2, (screenHeight - (rc.bottom - rc.top) )/2, 0, 0, SWP_NOZORDER|SWP_NOSIZE); ShowWindow(m_hWindow, SW_SHOW); UpdateWindow(m_hWindow); return true; } /******************************************************************* * InitDirect3D * Initializes Direct3D * Inputs - Parent window handle - HWND, Window width - int Window height - int * Outputs - true if successful, false if failed - bool *******************************************************************/ bool CEngine::InitDirect3D(HWND hWnd, int width, int height) { // Create the clear the DXGI_SWAP_CHAIN_DESC structure DXGI_SWAP_CHAIN_DESC swapChainDesc; ZeroMemory(&swapChainDesc, sizeof(swapChainDesc)); // Fill in the needed values swapChainDesc.BufferCount = 2; swapChainDesc.BufferDesc.Width = width; swapChainDesc.BufferDesc.Height = height; swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swapChainDesc.BufferDesc.RefreshRate.Numerator = 60; swapChainDesc.BufferDesc.RefreshRate.Denominator = 1; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.OutputWindow = hWnd; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.Windowed = TRUE; // Create the D3D device and the swap chain HRESULT hr = D3D10CreateDeviceAndSwapChain(NULL, D3D10_DRIVER_TYPE_HARDWARE, NULL, 0, D3D10_SDK_VERSION, &swapChainDesc, &m_pSwapChain, &m_pD3DDevice); // Error checking. Make sure the device was created if (FAILED(hr)) { MessageBox(hWnd, TEXT("A DX10 Compliant Video Card is Required"), TEXT("ERROR"), MB_OK); return false; } // Get the back buffer from the swapchain ID3D10Texture2D *pBackBuffer; hr = m_pSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*)&pBackBuffer); if (FAILED(hr)) { return false; } // create the render target view hr = m_pD3DDevice->CreateRenderTargetView(pBackBuffer, NULL, &m_pRenderTargetView); // release the back buffer SAFE_RELEASE(pBackBuffer); // Make sure the render target view was created successfully if (FAILED(hr)) { return false; } //create depth stencil texture D3D10_TEXTURE2D_DESC descDepth; descDepth.Width = width; descDepth.Height = height; descDepth.MipLevels = 1; descDepth.ArraySize = 1; descDepth.Format = DXGI_FORMAT_D32_FLOAT; 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; if( FAILED( m_pD3DDevice->CreateTexture2D( &descDepth, NULL, &m_pDepthStencil ) ) ) return false; D3D10_DEPTH_STENCIL_DESC dsDesc; ZeroMemory( &dsDesc, sizeof(D3D10_DEPTH_STENCIL_DESC) ); // Depth test parameters dsDesc.DepthEnable = true; dsDesc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL; dsDesc.DepthFunc = D3D10_COMPARISON_LESS; // Stencil test parameters dsDesc.StencilEnable = true; dsDesc.StencilReadMask = D3D10_DEFAULT_STENCIL_READ_MASK; dsDesc.StencilWriteMask = D3D10_DEFAULT_STENCIL_WRITE_MASK; // Stencil operations if pixel is front-facing dsDesc.FrontFace.StencilFailOp = D3D10_STENCIL_OP_KEEP; dsDesc.FrontFace.StencilDepthFailOp = D3D10_STENCIL_OP_INCR; dsDesc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP; dsDesc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS; // Stencil operations if pixel is back-facing dsDesc.BackFace.StencilFailOp = D3D10_STENCIL_OP_KEEP; dsDesc.BackFace.StencilDepthFailOp = D3D10_STENCIL_OP_DECR; dsDesc.BackFace.StencilPassOp = D3D10_STENCIL_OP_KEEP; dsDesc.BackFace.StencilFunc = D3D10_COMPARISON_ALWAYS; // Create depth stencil state ID3D10DepthStencilState * pDSState; m_pD3DDevice->CreateDepthStencilState(&dsDesc, &pDSState); m_pD3DDevice->OMSetDepthStencilState(pDSState, 1); // Create the depth stencil view D3D10_DEPTH_STENCIL_VIEW_DESC descDSV; descDSV.Format = descDepth.Format; descDSV.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D; descDSV.Texture2D.MipSlice = 0; if( FAILED( m_pD3DDevice->CreateDepthStencilView( m_pDepthStencil, &descDSV, &m_pDepthStencilView ) ) ) return false; // set the render target m_pD3DDevice->OMSetRenderTargets(1, &m_pRenderTargetView, m_pDepthStencilView); // Setup the raster description which will determine how and what polygons will be drawn. D3D10_RASTERIZER_DESC rasterDesc; rasterDesc.AntialiasedLineEnable = false; rasterDesc.CullMode = D3D10_CULL_BACK; rasterDesc.DepthBias = 0; rasterDesc.DepthBiasClamp = 0.0f; rasterDesc.DepthClipEnable = true; rasterDesc.FillMode = D3D10_FILL_SOLID; rasterDesc.FrontCounterClockwise = false; rasterDesc.MultisampleEnable = false; rasterDesc.ScissorEnable = false; rasterDesc.SlopeScaledDepthBias = 0.0f; // Create the rasterizer state from the description we just filled out. if( FAILED( m_pD3DDevice->CreateRasterizerState(&rasterDesc, &m_pRasterState) ) ) return false; // Now set the rasterizer state. m_pD3DDevice->RSSetState(m_pRasterState); // create and set the viewport D3D10_VIEWPORT viewPort; viewPort.Width = width; viewPort.Height = height; viewPort.MinDepth = 0.0f; viewPort.MaxDepth = 1.0f; viewPort.TopLeftX = 0; viewPort.TopLeftY = 0; m_pD3DDevice->RSSetViewports(1, &viewPort); // Set up the projection matrix D3DXMatrixPerspectiveFovLH( &m_matProjection, (float)D3DX_PI * 0.25f, (float)width/(float)height, 0.1f / m_pSetup->fScale, 1000.0f / m_pSetup->fScale ); return true; } /******************************************************************* * ShutdownDirect3D * Closes down and releases the resources for Direct3D * Inputs - void * Outputs - void *******************************************************************/ void CEngine::ShutdownDirect3D() { SAFE_RELEASE(m_pAlphaBlendState); SAFE_RELEASE(m_pRasterState); SAFE_RELEASE(m_pDepthStencilView); SAFE_RELEASE(m_pDepthStencil); SAFE_RELEASE(m_pRenderTargetView); SAFE_RELEASE(m_pSwapChain); SAFE_RELEASE(m_pD3DDevice); } /******************************************************************* * WndProc * The main window procedure for the application * Inputs - application window handle - HWND message sent to the window - UINT wParam of the message being sent - WPARAM lParam of the message being sent - LPARAM * Outputs - LRESULT *******************************************************************/ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { // Allow the user to press the escape key to end the application case WM_KEYDOWN: switch(wParam) { // Check if the user hit the escape key case VK_ESCAPE: PostQuitMessage(0); break; } break; // The user hit the close button, close the application case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hWnd, message, wParam, lParam); }