//
// DXRenderer.cpp
//
// Wrapper class for DirectX 9.x rendering API
//
// Copyright 2005 Paul Higinbotham
//
#include "stdafx.h"
#include "DXRenderer.h"
#include "DXDefines.h"
#include "D3DSettingsHelper.h"
using namespace ZGraphics;
// Forward declarations
bool ConfirmDeviceHelper(D3DCAPS9* pCaps, VertexProcessingType vertexProcessingType, D3DFORMAT backBufferFormat);
// Static initializations
const DWORD HFVERTEX::FVF = (D3DFVF_XYZ | D3DFVF_TEX2);
DXRenderer::DXRenderer() : m_bIsWindowMode(true)
{
m_hWnd = 0;
m_pD3D = 0;
m_pD3Ddevice = 0;
m_bIsWindowMode = true;
}
DXRenderer::~DXRenderer()
{
_cleanUp();
}
bool DXRenderer::Initialize(HWND hWnd)
{
m_hWnd = hWnd;
// Create the Direct3D object
m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
if (m_pD3D == NULL)
return false;
// Build a list of Direct3D adapters, modes and devices. The
// ConfirmDevice() callback is used to confirm that only devices that
// meet the app's requirements are considered.
m_D3DEnumeration.AppUsesDepthBuffer = true;
m_D3DEnumeration.SetD3D(m_pD3D);
m_D3DEnumeration.ConfirmDeviceCallback = ConfirmDeviceHelper;
if ( FAILED(m_D3DEnumeration.Enumerate()) )
return false;
// Choose initial 3D adapter settings
RECT clientWindowRect;
GetClientRect(m_hWnd, &clientWindowRect);
D3DSettingsHelper d3dSettingsHelper(m_pD3D, m_D3DEnumeration, m_D3DSettings, clientWindowRect);
if ( FAILED(d3dSettingsHelper.ChooseInitialD3DSettings()) )
return false;
m_D3DSettings = d3dSettingsHelper.GetD3DSettings();
// Initialize for 3D rendering
if (!_initD3DEnvironment())
return false;
return true;
}
const CD3DSettings & DXRenderer::GetDisplaySettings() const
{
return m_D3DSettings;
}
void DXRenderer::SetWindowMode(bool bWindowed)
{
m_D3DSettings.IsWindowed = bWindowed;
}
void DXRenderer::SetWindowedSize(int nWidth, int nHeight)
{
m_D3DSettings.Windowed_Width = nWidth;
m_D3DSettings.Windowed_Height = nHeight;
}
int DXRenderer::LaunchSettingsDialog()
{
CD3DSettingsDialog d3dSettingsDialog(&m_D3DEnumeration, &m_D3DSettings);
// NOTE The D3D display settings dialog resources MUST be compiled into the application that uses
// this GraphicsEngine DLL (Application.rc, resource.h). Otherwise the display settings dialog cannot be created.
int nReturn = (int)(INT_PTR)d3dSettingsDialog.ShowDialog(m_hWnd);
if (nReturn == IDOK)
{
d3dSettingsDialog.GetFinalSettings(&m_D3DSettings);
}
else if (nReturn != IDCANCEL)
{
DWORD dwError = GetLastError();
throw "Error: Unable to create display settings dialog";
}
return nReturn;
}
void DXRenderer::ReCreateDevice()
{
SAFE_RELEASE(m_pD3Ddevice);
_initD3DEnvironment();
}
HRESULT DXRenderer::CreateVertexBuffer(const Array<HFVERTEX> * pVertices, IDirect3DVertexBuffer9 ** ppVBuffer)
{
HRESULT hr = S_OK;
// Create DX9 vertex buffer
int nVNum = (int)pVertices->GetArrayCount();
int nBufSize = nVNum * sizeof(HFVERTEX);
(*ppVBuffer) = NULL;
hr = m_pD3Ddevice->CreateVertexBuffer(nBufSize, 0, HFVERTEX::FVF, D3DPOOL_MANAGED, ppVBuffer, NULL);
if (SUCCEEDED(hr))
{
// Fill vertex buffer
HFVERTEX * pVBuf = NULL;
(*ppVBuffer)->Lock(0, 0, (void **)&pVBuf, 0);
assert(pVBuf);
memcpy(pVBuf, pVertices->GetArrayPtr(), nBufSize);
(*ppVBuffer)->Unlock();
}
return hr;
}
HRESULT DXRenderer::SetStreamSource(UINT uStream, IDirect3DVertexBuffer9 * pVBuffer, UINT uOffsetBytes)
{
// Set up stream
HRESULT hr = m_pD3Ddevice->SetFVF(HFVERTEX::FVF);
hr = m_pD3Ddevice->SetStreamSource(uStream, pVBuffer, uOffsetBytes, sizeof(HFVERTEX));
return hr;
}
HRESULT DXRenderer::DrawPrimitive(D3DPRIMITIVETYPE Type, int nStartIndex, int nNumPrimitives)
{
return m_pD3Ddevice->DrawPrimitive(Type, nStartIndex, nNumPrimitives);
}
HRESULT DXRenderer::DrawPrimitiveT(D3DPRIMITIVETYPE Type, const Array<HFVERTEX> * pVertices, int nNumPrimitives)
{
// Combines D3D vertex creation, setting D3D stream, and drawing primitives into one call.
// This method is for testing purposes only.
HRESULT hr = S_OK;
// Create DX9 vertex buffer
int nVNum = (int)pVertices->GetArrayCount();
int nBufSize = nVNum * sizeof(HFVERTEX);
IDirect3DVertexBuffer9 * pDX9VertexBuffer = NULL;
hr = m_pD3Ddevice->CreateVertexBuffer(nBufSize, 0, HFVERTEX::FVF, D3DPOOL_MANAGED, &pDX9VertexBuffer, NULL);
if (SUCCEEDED(hr))
{
// Fill vertex buffer
HFVERTEX * pVBuf = NULL;
pDX9VertexBuffer->Lock(0, 0, (void **)&pVBuf, 0);
assert(pVBuf);
memcpy(pVBuf, pVertices->GetArrayPtr(), nBufSize);
pDX9VertexBuffer->Unlock();
// Set up stream
hr = m_pD3Ddevice->SetStreamSource(0, pDX9VertexBuffer, 0, sizeof(HFVERTEX));
hr = m_pD3Ddevice->SetFVF(HFVERTEX::FVF);
if (SUCCEEDED(hr))
{
// Draw triangle primitives
hr = m_pD3Ddevice->DrawPrimitive(Type, 0, nNumPrimitives);
}
else
{
assert(0);
}
}
SAFE_RELEASE(pDX9VertexBuffer);
return hr;
}
HRESULT DXRenderer::SetMaterial(const D3DMATERIAL9 * pMtrl)
{
return m_pD3Ddevice->SetMaterial(pMtrl);
}
HRESULT DXRenderer::SetTexture(DWORD dwStage, IDirect3DBaseTexture9 * pTexture)
{
return m_pD3Ddevice->SetTexture(dwStage, pTexture);
}
// Creates a Direct3D9 texture from a given array of texture bytes
IDirect3DTexture9 * DXRenderer::GetTexture(const BYTE * pbTexture, UINT uLevels, DWORD dwUsage, D3DFORMAT format, int nWidth, int nHeight)
{
HRESULT hr;
IDirect3DTexture9 * pDXTexture;
// Currently only support D3DFMT_X8R8G8B8 or D3DFMT_A8R8G8B8 format for HL experiment
assert(format == D3DFMT_X8R8G8B8 || format == D3DFMT_A8R8G8B8);
assert(pbTexture);
hr = m_pD3Ddevice->CreateTexture(nWidth, nHeight, uLevels, dwUsage, format, D3DPOOL_MANAGED, &pDXTexture, NULL);
if (!SUCCEEDED(hr))
{
SAFE_RELEASE(pDXTexture);
return 0;
}
D3DLOCKED_RECT d3dlr;
pDXTexture->LockRect(0, &d3dlr, 0, 0);
BYTE * pDstRow = (BYTE *) d3dlr.pBits;
DWORD * pRGBTexture = (DWORD *)pbTexture;
for (int nY=0; nY<nHeight; nY++)
{
DWORD * pDstXRGB = (DWORD *)pDstRow;
int nYIndex = nY * nWidth;
for (int nX=0; nX<nWidth; nX++)
{
*pDstXRGB = pRGBTexture[nYIndex + nX];
pDstXRGB++;
}
pDstRow += d3dlr.Pitch;
}
pDXTexture->UnlockRect(0);
return pDXTexture;
}
IDirect3DTexture9 * DXRenderer::CreateTextureFromFile(LPCTSTR pSrcFile)
{
IDirect3DTexture9 * pDXTexture = NULL;
HRESULT hr = D3DXCreateTextureFromFileEx(m_pD3Ddevice, pSrcFile,
D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN,
D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0,
NULL, NULL, &pDXTexture);
return pDXTexture;
}
HRESULT DXRenderer::SetTextureState(DWORD dwStage, D3DTEXTURESTAGESTATETYPE Type, DWORD dwValue)
{
return m_pD3Ddevice->SetTextureStageState(dwStage, Type, dwValue);
}
HRESULT DXRenderer::SetSamplerState(DWORD dwSampler, D3DSAMPLERSTATETYPE Type, DWORD dwValue)
{
return m_pD3Ddevice->SetSamplerState(dwSampler, Type, dwValue);
}
HRESULT DXRenderer::SetRenderState(D3DRENDERSTATETYPE Type, DWORD dwValue)
{
return m_pD3Ddevice->SetRenderState(Type, dwValue);
}
HRESULT DXRenderer::SetTransform(D3DTRANSFORMSTATETYPE Type, const Matrix4f & Matrix)
{
return m_pD3Ddevice->SetTransform(Type, (D3DMATRIX *)(Matrix.GetPtr()));
}
HRESULT DXRenderer::GetTransform(D3DTRANSFORMSTATETYPE Type, Matrix4f & Matrix)
{
return m_pD3Ddevice->GetTransform(Type, (D3DMATRIX *)Matrix.GetPtr());
}
HRESULT DXRenderer::SetLight(DWORD dwIndex, const D3DLIGHT9 * pLight)
{
return m_pD3Ddevice->SetLight(dwIndex, pLight);
}
HRESULT DXRenderer::EnableLight(DWORD dwIndex, BOOL bEnable)
{
return m_pD3Ddevice->LightEnable(dwIndex, bEnable);
}
HRESULT DXRenderer::SetFVF(DWORD dwFVF)
{
return m_pD3Ddevice->SetFVF(dwFVF);
}
HRESULT DXRenderer::BeginScene()
{
return m_pD3Ddevice->BeginScene();
}
HRESULT DXRenderer::EndScene()
{
return m_pD3Ddevice->EndScene();
}
HRESULT DXRenderer::Present(const RECT * pSourceRect, const RECT * pDestRect, HWND hDestWindowOverride, const RGNDATA * pDirtyRegion)
{
return m_pD3Ddevice->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);
}
HRESULT DXRenderer::Clear(DWORD dwCount, const D3DRECT *pRects, DWORD dwFlags, D3DCOLOR Color, float fZ, DWORD Stencil)
{
return m_pD3Ddevice->Clear(dwCount, pRects, dwFlags, Color, fZ, Stencil);
}
HRESULT DXRenderer::CreateProjectionMatrixLH(Matrix4f & Matrix, float fFOV, float fAspect, float fNearD, float fFarD)
{
// Aspect ratio is defined as (width / height) instead of normal (height / width)
assert(fAspect != 0.0f);
if (fAspect != 0.0f)
{
D3DXMatrixPerspectiveFovLH((D3DXMATRIX *)(Matrix.GetPtr()), fFOV, 1.0f/fAspect, fNearD, fFarD);
return S_OK;
}
return E_FAIL;
}
HRESULT DXRenderer::SaveTextureToFile(LPCSTR pszFilename, IDirect3DBaseTexture9 * pTexture, D3DXIMAGE_FILEFORMAT DestFormat)
{
return D3DXSaveTextureToFile(pszFilename, DestFormat, pTexture, NULL);
}
const D3DCAPS9 * DXRenderer::GetD3DCaps() const
{
return &m_D3Dcaps;
}
HRESULT DXRenderer::CompileShader(LPCSTR pSrcData, UINT srcDataLen, CONST D3DXMACRO* pDefines, LPD3DXINCLUDE pInclude,
LPCSTR pFunctionName, LPCSTR pProfile, DWORD Flags, LPD3DXBUFFER* ppShader,
LPD3DXBUFFER* ppErrorMsgs, LPD3DXCONSTANTTABLE *ppConstantTable)
{
return D3DXCompileShader(pSrcData, srcDataLen, pDefines, pInclude, pFunctionName, pProfile, Flags, ppShader,
ppErrorMsgs, ppConstantTable);
}
HRESULT DXRenderer::CompileShaderFromFile(LPCSTR pSrcFile, CONST D3DXMACRO* pDefines, LPD3DXINCLUDE pInclude,
LPCSTR pFunctionName, LPCSTR pProfile, DWORD Flags, LPD3DXBUFFER* ppShader,
LPD3DXBUFFER *ppErrorMsgs, LPD3DXCONSTANTTABLE *ppConstantTable)
{
return D3DXCompileShaderFromFile(pSrcFile, pDefines, pInclude, pFunctionName, pProfile, Flags, ppShader, ppErrorMsgs,
ppConstantTable);
}
LPCSTR DXRenderer::GetVertexShaderProfile()
{
return D3DXGetVertexShaderProfile(m_pD3Ddevice);
}
HRESULT DXRenderer::CreateVertexShader(const DWORD * pFunction, IDirect3DVertexShader9 ** ppShader)
{
return m_pD3Ddevice->CreateVertexShader(pFunction, ppShader);
}
HRESULT DXRenderer::SetVertexShader(IDirect3DVertexShader9 * pShader)
{
return m_pD3Ddevice->SetVertexShader(pShader);
}
HRESULT DXRenderer::SetCTableMatrix(LPD3DXCONSTANTTABLE pConstantTable, LPCSTR pszConstantName, const Matrix4f & Matrix)
{
return pConstantTable->SetMatrix(m_pD3Ddevice, pszConstantName, (D3DXMATRIX *)Matrix.GetPtr());
}
HRESULT DXRenderer::SetCTableVector(LPD3DXCONSTANTTABLE pConstantTable, LPCSTR pszConstantName, const Vector4f & Vector)
{
return pConstantTable->SetVector(m_pD3Ddevice, pszConstantName, (D3DXVECTOR4 *)Vector.GetPtr());
}
HRESULT DXRenderer::SetCTableFloat(LPD3DXCONSTANTTABLE pConstantTable, LPCSTR pszConstantName, float fScalar)
{
return pConstantTable->SetFloat(m_pD3Ddevice, pszConstantName, fScalar);
}
//
// Internal methods
//
bool DXRenderer::_initD3DEnvironment()
{
// Note adaptor and device type in case it changes
m_CurrDevType = m_D3DSettings.DevType();
m_nCurrAdaptor = m_D3DSettings.AdapterOrdinal();
D3DAdapterInfo * pAdapterInfo = m_D3DSettings.PAdapterInfo();
D3DDeviceInfo * pDeviceInfo = m_D3DSettings.PDeviceInfo();
// Set up present parameters
_buildPresentParamsFromSettings();
if (pDeviceInfo->Caps.PrimitiveMiscCaps & D3DPMISCCAPS_NULLREFERENCE)
{
assert(0);
// Error: Selected device is a reduced-functionality reference device. Cannot render.
return false;
}
// Create device
HRESULT hr;
DWORD behaviorFlags;
if (m_D3DSettings.GetVertexProcessingType() == SOFTWARE_VP)
behaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
else if (m_D3DSettings.GetVertexProcessingType() == MIXED_VP)
behaviorFlags = D3DCREATE_MIXED_VERTEXPROCESSING;
else if (m_D3DSettings.GetVertexProcessingType() == HARDWARE_VP)
behaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING;
else if (m_D3DSettings.GetVertexProcessingType() == PURE_HARDWARE_VP)
behaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE;
else
{
assert(0);
// Error: Invalid selection for vertex processing
return false;
}
// Create the device
hr = m_pD3D->CreateDevice(m_D3DSettings.AdapterOrdinal(), pDeviceInfo->DevType, // D3DDEVTYPE_REF
m_hWnd, behaviorFlags, &m_D3Dpp,
&m_pD3Ddevice);
if (!SUCCEEDED(hr))
{
assert(0);
// Error: Unable to create the Direct3D device object
return false;
}
// Get current device caps and behaviors
m_pD3Ddevice->GetDeviceCaps(&m_D3Dcaps);
m_dwCreateFlags = behaviorFlags;
return true;
}
void DXRenderer::_buildPresentParamsFromSettings()
{
m_D3Dpp.Windowed = m_D3DSettings.IsWindowed;
m_D3Dpp.BackBufferCount = 1;
m_D3Dpp.MultiSampleType = m_D3DSettings.MultisampleType();
m_D3Dpp.MultiSampleQuality = m_D3DSettings.MultisampleQuality();
m_D3Dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
m_D3Dpp.EnableAutoDepthStencil = m_D3DEnumeration.AppUsesDepthBuffer;
m_D3Dpp.hDeviceWindow = m_hWnd;
if (m_D3DEnumeration.AppUsesDepthBuffer)
{
m_D3Dpp.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
m_D3Dpp.AutoDepthStencilFormat = m_D3DSettings.DepthStencilBufferFormat();
}
else
{
m_D3Dpp.Flags = 0;
}
if (m_D3DSettings.IsWindowed)
{
m_D3Dpp.BackBufferWidth = m_D3DSettings.Windowed_Width;
m_D3Dpp.BackBufferHeight = m_D3DSettings.Windowed_Height;
m_D3Dpp.BackBufferFormat = m_D3DSettings.PDeviceCombo()->BackBufferFormat;
m_D3Dpp.FullScreen_RefreshRateInHz = 0;
m_D3Dpp.PresentationInterval = m_D3DSettings.PresentInterval();
}
else
{
m_D3Dpp.BackBufferWidth = m_D3DSettings.DisplayMode().Width;
m_D3Dpp.BackBufferHeight = m_D3DSettings.DisplayMode().Height;
m_D3Dpp.BackBufferFormat = m_D3DSettings.PDeviceCombo()->BackBufferFormat;
m_D3Dpp.FullScreen_RefreshRateInHz = m_D3DSettings.Fullscreen_DisplayMode.RefreshRate;
m_D3Dpp.PresentationInterval = m_D3DSettings.PresentInterval();
}
}
void DXRenderer::_cleanUp()
{
SAFE_RELEASE(m_pD3Ddevice);
SAFE_RELEASE(m_pD3D);
}
// Needed for use with CD3DEnumeration class
bool ConfirmDeviceHelper(D3DCAPS9* pCaps, VertexProcessingType vertexProcessingType, D3DFORMAT backBufferFormatt)
{
return true;
}