Click here to Skip to main content
15,896,111 members
Articles / Desktop Programming / Win32

Half Life Game Level Viewer

Rate me:
Please Sign up or sign in to vote.
4.61/5 (23 votes)
7 Feb 2009CPOL26 min read 80K   2.3K   60  
DirectX based application to open and view Half Life 1 game files
//
//	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;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
I am a senior software developer currently doing contract work for Microsoft. My educational background is in electrical engineering and I hold a masters degree from the University of Washington. I have experience in hardware and systems design but have done primarily software development for the last two decades. I have worked for various small companies as well as start-up companies, and have worked as a full time employee SDE at Microsoft Corporation.

Comments and Discussions