Click here to Skip to main content
15,886,422 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 79.7K   2.3K   60  
DirectX based application to open and view Half Life 1 game files
//
//	CApplication.cpp
//
//	Copyright 2005 Paul Higinbotham
//

#include "CApplication.h"
#include "..\graphicsengine\GraphicsEngine.h"
#include "..\graphicsengine\collections\String.h"

#include "..\graphicsengine\renderer\directxrenderer\d3dsettings.h"

#include <cassert>
#include <TCHAR.H>
#include <stdio.h>


// Forward declarations
LRESULT CALLBACK WndProc(
	HWND hWnd,
	UINT uMsg,
	WPARAM wParam,
	LPARAM lParam);

// statics
static char szWindowClassName[] = "ZGraphicsApplication";
static char szWindowName[] = "HL Level Viewer";

static char szFileMenu[] = "&File";
static char szFileOpen[] = "&Open...";
static char szFileExit[] = "E&xit";

static char szOptionsMenu[] = "&Options";
static char szOptionsDisplay[] = "&Display Settings...";
static char szOptionsFullScreen[] = "Full &Screen";

// Default camera movement keys
static const UINT sWKey = DIK_W;
static const UINT sSKey = DIK_S;
static const UINT sAKey = DIK_A;
static const UINT sDKey = DIK_D;
static const UINT sFKey = DIK_F;
static const UINT sIKey = DIK_I;
static const UINT sXKey = DIK_X;
static const UINT sZKey = DIK_Z;
static const UINT sUpKey = DIK_UP;
static const UINT sDownKey = DIK_DOWN;
static const UINT sLeftKey = DIK_LEFT;
static const UINT sRightKey = DIK_RIGHT;
static const UINT sSpaceKey = DIK_SPACE;
static const UINT sLCtlKey = DIK_LCONTROL;
static const UINT sLShftKey = DIK_LSHIFT;
static const UINT sESCKey = DIK_ESCAPE;
static const UINT sTABKey = DIK_TAB;

// Movement constants
static const float s_fWalkVel = 250.0f;			//	(units / sec)
static const float s_fRunVel = 350.0f;			//	(units / sec)
static const float s_fYawRate = 0.006f;
static const float s_fPitchRate = 0.006f;

// View constants
static const float s_fFocalL = 1.2f;			//	Focal length, 1.0 -> 90 degree FOV, 1.3 -> 75 degree FOV
static const float s_fNearD = 1.2f;				//	Near clip distance
static const float s_fFarD = 7000.0f;			//	Far clip distance

// Camera bounding box dimensions (roughly human size, for Freeman)
//static const Vector3f s_vBB[2] = { Vector3f(-15.0f, -15.0f, -70.0f), Vector3f(15.0f, 15.0f, 15.0f) };
static const Vector3f s_vBB[2] = { Vector3f(-18.0f, -18.0f, -70.0f), Vector3f(18.0f, 18.0f, 15.0f) };
static const float s_fCrouchZ = 45.0f;

// Application settings constants
static const char szAppKey[] = "Software\\ZGraphics\\Application";
static const int s_nDefClientWidth = 640;
static const int s_nDefClientHeight = 480;

// Main window style
static const DWORD s_WinStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;


enum MenuItems
{
	mnuBarFile = 0,
	mnuBarOptions,
	mnuFileOpen,
	mnuFileSep1,
	mnuFileExit,
	mnuOptionsDisplay,
	mnuOptionsFullScreen
};

enum CameraActions
{
	eMoveForward,
	eMoveBackward,
	eMoveLeft,
	eMoveRight,
	eLevitate,
	ePitchUp,
	ePitchDown,
	eYawLeft,
	eYawRight,
	eJump,
	eCrouch,
	eRun,
	eQuit,
	eToggleScreen,
	eFlashlight
};


int CompareKey(const int & nKey1, const int & nKey2)
{
	return (nKey1 - nKey2);
}


CApplication * CApplication::ms_pApplication = NULL;

CApplication::~CApplication()
{
	// release any engine resources
	delete m_pRenderer;
	delete m_pScene;
	delete m_pCamera;
}

//
// Register and creates a window for rendering.
// Creates and initializes the DX input object.
// Creates and initializes the camera, scene, and rendering objects.
//
bool CApplication::Initialize(HINSTANCE hInstance)
{
	// register the window class
	WNDCLASS wc;
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszClassName = szWindowClassName;
	wc.lpszMenuName = NULL;
	RegisterClass(&wc);

	// Read settings from registry to get client width/height
	_readSettings();

	// set window client area
	RECT windowRect = {0, 0, m_nClientWidth, m_nClientHeight};
	AdjustWindowRect(&windowRect, s_WinStyle, true);

	_centerWindowInShell(windowRect);

	// create menu for application window
	HMENU hMenu = _createSimpleMenu();

	// create the application window
	m_hWnd = CreateWindow(
		szWindowClassName, szWindowName,
		s_WinStyle,
		windowRect.left, windowRect.top, (windowRect.right - windowRect.left), (windowRect.bottom - windowRect.top),
		(HWND)0, hMenu, hInstance, NULL);

	if (!m_hWnd)
		return false;

	// Initialize DX input object
	if (!m_Input.Init(m_hWnd, hInstance))
		return false;
	m_Input.AcquireKeyboard();
	m_Input.AcquireMouse();

	// Create graphic engine objects for rendering
	if (!_initializeEngineObjects())
		return false;

	_initializeKeyMap();

	// Get high performance count frequency (if supported)
	QueryPerformanceFrequency(&m_liCountFrequency);

	// display window
	ShowWindow(m_hWnd, SW_SHOW);
	UpdateWindow(m_hWnd);

	return true;
}

//
// Main application rendering loop and window message pump.
// For windowed mode Windows messages are processed before scene rendering occurs.
// For full-screen mode no messages are processed and only scene rendering occurs.
// Note that the application can exit only from windowed mode.
//
void CApplication::Run()
{
	// Get base time.
	_resetBaseTime();

	// Start Windows message pump.  Application always starts in windowed mode.
	while (1)
	{
		MSG sMsg;
		BOOL bHaveMessage = false;

		if (m_bWindowedMode)
		{
			// Check message queue.
			if (_isActive())
				bHaveMessage = ::PeekMessage(&sMsg, (HWND)0, 0, 0, PM_REMOVE);	// Returns immmediately
			else
				bHaveMessage = ::GetMessage(&sMsg, (HWND)0, 0, 0);				// Saves CPU cycles

			// We quit only when in windowed mode.
			if (sMsg.message == WM_QUIT)
				goto _quit;

			if (bHaveMessage)
			{
				// Take care of any messages in the Windows message queue.
				HACCEL hAccel = (HACCEL)0;
				if (!TranslateAccelerator(m_hWnd, hAccel, &sMsg))
				{
					TranslateMessage(&sMsg);
					DispatchMessage(&sMsg);
				}
			}
		}

		// Update and render scene for both windowed and full-screen modes.
		if (m_bActive && !bHaveMessage)
		{
			// Update player motion and render scene.
			_updateAndRender();
		}
	}

_quit:
	_resetBaseTime();
}


//
//	Internal methods
//

// Uses Windows commong open file dialog to allow user to browse for a scene file
void CApplication::_openFile()
{
	OPENFILENAME	openFileName;
	memset(&openFileName, 0, sizeof(openFileName));
	openFileName.lStructSize = sizeof(openFileName);
	openFileName.hwndOwner = m_hWnd;
	openFileName.lpstrFilter = _T("HF BSP Files\0*.BSP\0");	// Look only for bsp files.

	TCHAR strFilePath[2048] = "\0";
	openFileName.lpstrFile = strFilePath;
	openFileName.nMaxFile = 2048;
	openFileName.lpstrTitle = _T("Open BSP Scene File");

	if ( GetOpenFileName(&openFileName) )
	{
		// Load scene file
		m_bSceneLoaded = m_pScene->LoadScene(openFileName.lpstrFile);
		if (m_bSceneLoaded)
		{
			// Update window title
			cString szTitle = szWindowName;
			cString szFilename(openFileName.lpstrFile);
			int nPos = szFilename.FindLastChar('\\');
			if (nPos == -1)
				nPos = szFilename.FindLastChar('/');
			if (nPos > -1)
				szFilename = szFilename.GetPtr(nPos+1);
			szTitle.AppendString(" - ");
			szTitle.AppendString(szFilename);
			SetWindowText(m_hWnd, szTitle.GetPtr());

			// Enable Options menu items
			EnableMenuItem(m_hOptionsSubMenu, mnuOptionsDisplay, MF_ENABLED);
			EnableMenuItem(m_hOptionsSubMenu, mnuOptionsFullScreen, MF_ENABLED);

			m_bActive = true;
			_resetBaseTime();
		}
	}
}
	
// Launches the display settings dialog
void CApplication::_displaySettings()
{
	int nReturn = m_pRenderer->LaunchSettingsDialog();

	if (nReturn == IDOK)
	{
		_pause();

		// Check for window mode change
		CD3DSettings d3dSettings = m_pRenderer->GetDisplaySettings();
		D3DDEVTYPE devType = d3dSettings.DevType();
		if (m_bWindowedMode != d3dSettings.IsWindowed)
		{
			_changeWindowedMode(d3dSettings.IsWindowed);
		}

		// Reset render object
		m_pScene->ReleaseRenderObjects();
		m_pRenderer->ReCreateDevice();
		m_pScene->RestoreRenderObjects(m_pRenderer);

		_resume();
	}
}

// Toggles application back and forth from windowed mode and fullscreen mode.
void CApplication::_toggleFullScreen()
{
	if (!m_bSceneLoaded)
		return;

	_pause();

	CD3DSettings d3dSettings = m_pRenderer->GetDisplaySettings();
	_changeWindowedMode(!d3dSettings.IsWindowed);

	// Reset render object
	m_pRenderer->SetWindowMode(m_bWindowedMode);
	m_pScene->ReleaseRenderObjects();
	m_pRenderer->ReCreateDevice();

	// Reset view information in case screen aspect changes.
	_setViewInfo();
	m_pScene->RestoreRenderObjects(m_pRenderer);

	if (m_bWindowedMode)
	{
		SetCursor(LoadCursor(NULL, IDC_ARROW));
	}
	else
	{
		SetCursor(NULL);
	}

	_resume();
}

void CApplication::_closeApplication()
{
	PostMessage(m_hWnd, WM_CLOSE, 0, 0);
}

inline const unsigned char CApplication::_getKey(int eAction) const
{
	const int *pnKey = m_pkeyMap->IsInMap(eAction);
	if (pnKey)
		return (unsigned char) (*pnKey);
	else
		return 0;
}

// Method to check current keyboard state and perform keyboard function as defined
// in the keyboard map object.
void CApplication::_handleKeyboard(DWORD ulTimeDelUSec)
{
	float fRunVel = s_fRunVel;
	float fWalkVel = s_fWalkVel;

	// Crouch key
	if (m_Input.IsKeydown(_getKey(eCrouch)))
	{
		// Set camera to crouch mode
		m_pCamera->SetCrouched(true);
	}
	else if (m_pCamera->IsCrouched())
	{
		// Command camera to return to upright height.  
		m_pCamera->SetCrouched(false);
	}
	if (m_pCamera->IsCrouched())
	{
		// Reduce the player walk/run velocity when crouched.
		fRunVel /= 2.0f;
		fWalkVel /= 2.0f;
	}

	// Run key
	m_bRun = m_Input.IsKeydown(_getKey(eRun));

	// Forward/Backward keys
	if (m_Input.IsKeydown(_getKey(eMoveForward)))
	{
		// Set camera forward velocity.
		m_pCamera->Walk(m_bRun ? fRunVel : fWalkVel);
	}
	else if (m_Input.IsKeydown(_getKey(eMoveBackward)))
	{
		// Set camera backward velocity by changing sign.
		m_pCamera->Walk(m_bRun ? -fRunVel : -fWalkVel);
	}
	else
	{
		// Set camera forward/backward velocity to zero.
		m_pCamera->Walk(0.0f);
	}

	// Strafe (right/left) movement keys
	if (m_Input.IsKeydown(_getKey(eMoveRight)))
	{
		// Set camera right strafing velocity.
		m_pCamera->Strafe(m_bRun ? -fRunVel : -fWalkVel);
	}
	else if (m_Input.IsKeydown(_getKey(eMoveLeft)))
	{
		// Set camera left strafing velocity.
		m_pCamera->Strafe(m_bRun ? fRunVel : fWalkVel);
	}
	else
	{
		// Set camera strafing velocity to zero.
		m_pCamera->Strafe(0.0f);
	}

	if (_isWindowed())
	{
		// Levitate key
		if (m_Input.IsKeydown(_getKey(eLevitate)))
		{
			// Tell camera to levitate.
			m_pCamera->StartLevitate();
			m_bLevitate = true;
		}
		else if (m_bLevitate)
		{
			// Tell camera to stop levitating.
			m_pCamera->StopLevitate();
			m_bLevitate = false;
		}
	}

	// Pitch up/down keys
	if (m_Input.IsKeydown(_getKey(ePitchUp)))
	{
		// Set camera pitch up angle increment.
		m_pCamera->Pitch(0.000003f * ulTimeDelUSec);
	}
	else if (m_Input.IsKeydown(_getKey(ePitchDown)))
	{
		// Set camera pitch down angle increment.
		m_pCamera->Pitch(-0.000003f * ulTimeDelUSec);
	}

	// Yaw right/left keys
	if (m_Input.IsKeydown(_getKey(eYawLeft)))
	{
		// Set camera yaw left angle increment.
		m_pCamera->Yaw(0.000003f * ulTimeDelUSec);
	}
	else if (m_Input.IsKeydown(_getKey(eYawRight)))
	{
		// Set camera yaw right angle increment.
		m_pCamera->Yaw(-0.000003f * ulTimeDelUSec);
	}

	// Jump key
	if (m_Input.IsKeydown(_getKey(eJump)))
	{
		// Start jump motion only if camera is currently on the floor.
		// Player can jump only if from a ground surface.
		if (!m_bJump && m_pCamera->IsCamOnFloor())
		{
			// Adjust jump initial velocity and "gravity" acceleration based on what feels right.
			// Note that windowed mode and full-screen mode time updating behave differently.
			if (_isWindowed())
			{
				m_pCamera->Jump(2.2f, -0.08f);
			}
			else
			{
				m_pCamera->Jump(25.0f, -10.0f);
			}
			m_bJump = true;
		}
	}
	else if (m_bJump)
	{
		m_bJump = false;
	}

	// Quit key
	if (m_Input.IsKeydown(_getKey(eQuit)))
	{
		_closeApplication();
	}

	// Toggle fullscreen key
	if (m_Input.IsKeydown(_getKey(eToggleScreen)))
	{
		_toggleFullScreen();
	}

	// Flashlight key
	// Add time delay to flashlight key change do "de-bounce" fast key repeats.
	if (m_Input.IsKeydown(_getKey(eFlashlight)) && (_getTime() - m_dwFLToggleTime) > 250)
	{
		m_pScene->ToggleFlashlight();
		m_dwFLToggleTime = _getTime();
	}
}

// Method to check current mouse state and perform appropriate mouse based functions.
void CApplication::_handleMouse()
{
	// Mouse input is only used in full-screen mode.  For windowed mode the mouse works
	// normally for Windows applications (selecting menus, etc.).
	if (_isWindowed())
		return;

	// Retrieve mouse left/right movement, as well as mouse button states.
	LONG lXDel, lYDel;
	bool bLeftBtn, bRightBtn;
	m_Input.GetMouseInfo(lXDel, lYDel, bLeftBtn, bRightBtn);

	if (bRightBtn)
	{
		// Do camera levitation movement if right mouse button is pressed.
		m_pCamera->StartLevitate();
		SetCursor(NULL);
		m_bLevitate = true;
	}
	else if (m_bLevitate)
	{
		// Otherwise terminate any levitation movement (let gravity pull camera back to ground).
		m_pCamera->StopLevitate();
		m_bLevitate = false;
	}

	if (lXDel == 0 && lYDel == 0)
		return;

	// Mouse movement adjustment, sort of based on screen aspect.
	// Make adjustments based on what feels right.
	// @todo - Think about mouse motion smoothing.
	float fXAdj = (float)lXDel / 4.0f;
	float fYAdj = (float)lYDel / 3.0f;

	// Update camera yaw and pitch angles based on mouse movement and static rate values.
	// Adjust static rate values based on what feels right.
	float fYawDelAngle = -fXAdj * s_fYawRate;
	float fPitchDelAngle = -fYAdj * s_fPitchRate;

	// Set camera yaw and pitch angle changes.
	m_pCamera->Pitch(fPitchDelAngle);
	m_pCamera->Yaw(fYawDelAngle);
}

void CApplication::_handleClose()
{
	_saveSettings();
}

// Create and initialize all objects needed to load and render HL levels.
bool CApplication::_initializeEngineObjects()
{
	// Create render object
	m_pRenderer = ObjectFactory::CreateDirectXRenderer();
	assert(m_pRenderer);
	if (!m_pRenderer)
		return false;

	// Initialize render object
	if (!m_pRenderer->Initialize(m_hWnd))
	{
		MessageBox(NULL, "Unable to initialize the D3D rendering system.  Make sure your video hardware meets the requirements and that you have correct drivers.", "Renderer Initialization Error", MB_ICONERROR | MB_OK);
		return false;
	}

	// Create camera object
	m_pCamera = ObjectFactory::CreateFPSCamera();
	assert(m_pCamera);
	if (!m_pCamera)
		return false;

	// Set up default view information for camera
	_setViewInfo();

	// Set camera bounding boxes
	m_pCamera->SetBB(s_vBB, s_fCrouchZ);

	// Create scene object
	m_pScene = ObjectFactory::CreateHFSceneGraph();
	assert(m_pScene);
	if (!m_pScene)
		return false;

	// Provide the scene object with the camera and renderer objects
	m_pScene->SetCamera(m_pCamera);
	m_pScene->SetRenderer(m_pRenderer);

	return true;
}

// Update mouse and keyboard actions based on provide time change (time delta).
void CApplication::_updateInput(DWORD ulTimeDelUSec)
{
	// Retrieve current input state.
	m_Input.Update();

	// Update keyboard state
	_handleKeyboard(ulTimeDelUSec);

	// Update mouse view
	_handleMouse();
}

// Update input handling, camera position/orientation, and render level scene based on current
// camera.
void CApplication::_updateAndRender()
{
	// Ignore if a scene has not been loaded yet.
	if (m_bSceneLoaded)
	{
		// Get time delta in uSec
		DWORD ulTimeDelUSec = _getTimeDelta();

		// Don't bother updating if no significant time has passed.
		if (ulTimeDelUSec > 0)
		{
			_updateInput(ulTimeDelUSec);			// Update input time and handle mouse/keyboard actions.
			m_pScene->UpdateCamera(ulTimeDelUSec);	// Update camera position.
			m_pScene->Render();						// Render scene for current camera position/orientation.
		}
	}
}

void CApplication::_resetBaseTime()
{
	if (m_liCountFrequency.QuadPart != 0)
	{
		// High performance counter supported
		QueryPerformanceCounter(&m_liBaseTime);
	}
	else
	{
		// Low resolution counter
		m_liBaseTime.LowPart = timeGetTime();
	}
}

DWORD CApplication::_getTimeDelta(bool bUpdateBase)
{
	DWORD	dwTimeDel;

	if (m_liCountFrequency.QuadPart != 0)
	{
		// High performance counter supported
		LARGE_INTEGER liStopCount;
		LARGE_INTEGER liCountDel;
		QueryPerformanceCounter(&liStopCount);
		liCountDel.QuadPart = liStopCount.QuadPart - m_liBaseTime.QuadPart;
		float fRenderTime = (float)liCountDel.QuadPart;
		fRenderTime /= (float)m_liCountFrequency.QuadPart;		// Time delta in Sec
		dwTimeDel = (DWORD)(fRenderTime * 1000000);				// Time delta in uSec
		if (bUpdateBase)
			m_liBaseTime.QuadPart = liStopCount.QuadPart;
	}
	else
	{
		// Low resolution counter
		DWORD dwTime = timeGetTime();
		dwTimeDel = (dwTime - m_liBaseTime.LowPart) * 1000;		// Time delta in uSec
		if (bUpdateBase)
			m_liBaseTime.LowPart = dwTime;
	}

	return dwTimeDel;		// uSec
}

DWORD CApplication::_getTime()
{
	DWORD	dwTime;

	if (m_liCountFrequency.QuadPart != 0)
	{
		// High performance counter supported
		LARGE_INTEGER liTime;
		QueryPerformanceCounter(&liTime);
		float fTime = (float)liTime.QuadPart;
		fTime /= (float)m_liCountFrequency.QuadPart;	// Time in Sec
		dwTime = (DWORD)(fTime * 1000);					// Time in mSec
	}
	else
	{
		// Low resolution counter
		dwTime = timeGetTime();							// Time in mSec
	}

	return dwTime;		// mSec
}

void CApplication::_initializeKeyMap()
{
	// Create Keymap with default settings
	delete m_pkeyMap;

	m_pkeyMap = new Map<int, int, CompareIntType>(20, CompareKey);
	assert(m_pkeyMap);

	// @todo - Add UI to allow user to modify key controls.
	m_pkeyMap->Add(eMoveForward, sWKey);
	m_pkeyMap->Add(eMoveBackward, sSKey);
	m_pkeyMap->Add(eMoveLeft, sAKey);
	m_pkeyMap->Add(eMoveRight, sDKey);
	m_pkeyMap->Add(eLevitate, sXKey);
	m_pkeyMap->Add(ePitchUp, sUpKey);
	m_pkeyMap->Add(ePitchDown, sDownKey);
	m_pkeyMap->Add(eYawLeft, sLeftKey);
	m_pkeyMap->Add(eYawRight, sRightKey);
	m_pkeyMap->Add(eJump, sSpaceKey);
	m_pkeyMap->Add(eCrouch, sLCtlKey);
	m_pkeyMap->Add(eRun, sLShftKey);
	m_pkeyMap->Add(eQuit, sESCKey);
	m_pkeyMap->Add(eToggleScreen, sTABKey);
	m_pkeyMap->Add(eFlashlight, sFKey);
}

HMENU CApplication::_createSimpleMenu()
{
	// Create menu bar
	m_hMenubar = CreateMenu();

	// Add simple file and options menus
	// File
	//   ->Open
	//   ->Exit
	//
	// Options
	//    ->Render Options
	//	  ->Full Screen
	m_hFileSubMenu = CreateMenu();
	AppendMenu(m_hFileSubMenu, MF_STRING, mnuFileOpen, szFileOpen);
	AppendMenu(m_hFileSubMenu, MF_SEPARATOR, mnuFileSep1, NULL);
	AppendMenu(m_hFileSubMenu, MF_STRING, mnuFileExit, szFileExit);

	m_hOptionsSubMenu = CreateMenu();
	AppendMenu(m_hOptionsSubMenu, MF_STRING, mnuOptionsDisplay, szOptionsDisplay);
	AppendMenu(m_hOptionsSubMenu, MF_STRING, mnuOptionsFullScreen, szOptionsFullScreen);

	AppendMenu(m_hMenubar, MF_STRING|MF_POPUP, (UINT_PTR)m_hFileSubMenu, szFileMenu);
	AppendMenu(m_hMenubar, MF_STRING|MF_POPUP, (UINT_PTR)m_hOptionsSubMenu, szOptionsMenu);

	if (!m_bSceneLoaded)
	{
		EnableMenuItem(m_hOptionsSubMenu, mnuOptionsDisplay, MF_GRAYED);
		EnableMenuItem(m_hOptionsSubMenu, mnuOptionsFullScreen, MF_GRAYED);
	}
	
	return m_hMenubar;
}

inline void CApplication::_centerWindowInShell(RECT & windowRect)
{
	RECT screenRect;
	GetWindowRect(GetShellWindow(), &screenRect);
	UINT uWidth = (windowRect.right - windowRect.left);
	UINT uHeight = (windowRect.bottom - windowRect.top);
	windowRect.left = ((screenRect.right - screenRect.left) - uWidth) / 2;
	windowRect.top = ((screenRect.bottom - screenRect.top) - uHeight) / 2;
	windowRect.left = (windowRect.left < 0) ? 0 : windowRect.left;
	windowRect.top = (windowRect.top < 0) ? 0 : windowRect.top;
	windowRect.right = windowRect.left + uWidth;
	windowRect.bottom = windowRect.top + uHeight;
}

// Sets the view information of the camera object based on static settings and the aspect
// ratio of the display window.
void CApplication::_setViewInfo()
{
	assert(m_pCamera);
	assert(m_pRenderer);

	// Use the window aspect ratio to set the view aspect correctly.
	UINT uHeight;
	UINT uWidth;
	const CD3DSettings & d3dSettings = m_pRenderer->GetDisplaySettings();
	if (d3dSettings.IsWindowed)
	{
		uHeight = m_nClientHeight;
		uWidth = m_nClientWidth;
	}
	else
	{
		uHeight = d3dSettings.Fullscreen_DisplayMode.Height;
		uWidth = d3dSettings.Fullscreen_DisplayMode.Width;
	}

	float fAspect = ((float)uHeight) / ((float)uWidth);
	m_pCamera->SetView(fAspect, s_fFocalL, s_fNearD, s_fFarD);
}

// Helper method that performs all functions needed to change between windowed mode and
// full screen mode display.
void CApplication::_changeWindowedMode(bool bIsWindowed)
{
	m_bWindowedMode = bIsWindowed;

	if (m_bWindowedMode)
	{
		// Set windowed-mode style
		SetWindowLong(m_hWnd, GWL_STYLE, s_WinStyle);

		HMENU hMenu = _createSimpleMenu();
		if (hMenu != NULL)
			SetMenu(m_hWnd, hMenu);

		RECT windowRect = {0, 0, m_nClientWidth-1, m_nClientHeight-1};
		AdjustWindowRect(&windowRect, s_WinStyle, true);

		_centerWindowInShell(windowRect);

		SetWindowPos(m_hWnd, HWND_NOTOPMOST, 
					 windowRect.left, windowRect.top,
					 (windowRect.right - windowRect.left ),
					 (windowRect.bottom - windowRect.top),
					 SWP_SHOWWINDOW);
	}
	else
	{
		// Set fullscreen-mode style
		SetWindowLong(m_hWnd, GWL_STYLE, WS_POPUP|WS_SYSMENU|WS_VISIBLE);
		SetMenu(m_hWnd, NULL);
	}
}

void CApplication::_saveSettings()
{
	if (!m_bWindowedMode)
		return;

	HKEY hKey;
	RECT rcClient;

	GetClientRect(m_hWnd, &rcClient);
	m_nClientWidth = rcClient.right - rcClient.left;
	m_nClientHeight = rcClient.bottom - rcClient.top;

	if (m_nClientWidth < 640 || m_nClientHeight < 480)
	{
		m_nClientWidth = 640;
		m_nClientHeight = 480;
	}

	// Save and restore only window client size for now
	if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER, szAppKey, 
		0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL) )
    {
		RegSetValueEx(hKey, "Width", 0, REG_DWORD, (BYTE *)&m_nClientWidth, sizeof(DWORD));
		RegSetValueEx(hKey, "Height", 0, REG_DWORD, (BYTE *)&m_nClientHeight, sizeof(DWORD));

		RegCloseKey(hKey);
	}
}

void CApplication::_readSettings()
{
	HKEY hKey;

	// Save and restore only window client size for now
	if (ERROR_SUCCESS == RegCreateKeyEx( HKEY_CURRENT_USER, szAppKey, 
		0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL) )
	{

		DWORD dwType;
		DWORD dwLength = sizeof(DWORD);
		DWORD dwDest;

		if ( ERROR_SUCCESS == RegQueryValueEx(hKey, "Width", 0, &dwType, (BYTE*)(&dwDest), &dwLength) &&
			 dwType == REG_DWORD )
		{
			m_nClientWidth = (int)dwDest;
		}
		else
		{
			m_nClientWidth = s_nDefClientWidth;
		}

		if ( ERROR_SUCCESS == RegQueryValueEx(hKey, "Height", 0, &dwType, (BYTE*)(&dwDest), &dwLength) &&
			 dwType == REG_DWORD )
		{
			m_nClientHeight = (int)dwDest;
		}
		else
		{
			m_nClientHeight = s_nDefClientHeight;
		}

		RegCloseKey(hKey);

		m_nClientWidth = (m_nClientWidth < 640) ? 640 : m_nClientWidth;
		m_nClientHeight = (m_nClientHeight < 480) ? 480 : m_nClientHeight;
	}
}


//
// CApplication window message proc handler
//

LRESULT CApplication::MsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_CLOSE:
		_handleClose();
		PostMessage(hWnd, WM_QUIT, 0, 0);
		break;

	case WM_COMMAND:
		if (HIWORD(wParam) == 0)		// Indicates a menu item command
		{
			UINT uMenuID = LOWORD(wParam);

			switch (uMenuID)
			{
			case mnuFileOpen:
				_openFile();
				break;

			case mnuFileExit:
				_closeApplication();
				break;

			case mnuOptionsDisplay:
				_displaySettings();
				break;

			case mnuOptionsFullScreen:
				_toggleFullScreen();
				break;

			default:
				break;
			}
		}
		break;

	case WM_ENTERMENULOOP:
		// Pause game
		_pause();
		break;

	case WM_EXITMENULOOP:
		// Resume game
		_resume();
		break;

	case WM_SIZE:
		switch (wParam)
		{
		case SIZE_MINIMIZED:
			_pause();
			break;

		case SIZE_MAXIMIZED:
			_resume();
			break;

		case SIZE_RESTORED:
			_resume();
			break;
		}
		break;

	case WM_MOUSEMOVE:
		// Normally hide cursor in client area, unless user has right button clicked
		if (wParam & MK_LBUTTON)
		{
			if (GetCursor() == NULL)
				SetCursor(LoadCursor(NULL, IDC_ARROW));
		}
		else if (m_bSceneLoaded)
		{
			if (!_isWindowed())
				SetCursor(NULL);
		}
		break;

	case WM_ACTIVATE:
		m_Input.AcquireMouse();
		m_Input.AcquireKeyboard();
		break;

	case WM_GETMINMAXINFO:
		((MINMAXINFO*)lParam)->ptMinTrackSize.x = s_nDefClientWidth;
		((MINMAXINFO*)lParam)->ptMinTrackSize.y = s_nDefClientHeight;
		break;

	case WM_SYSCOMMAND:
		// Prevent moving/sizing and power loss in fullscreen mode
		switch (wParam)
		{
			case SC_MOVE:
			case SC_SIZE:
			case SC_MAXIMIZE:
			case SC_KEYMENU:
			case SC_MONITORPOWER:
				if (!_isWindowed())
					return 1;
				break;
		}
		break;

	case WM_SETCURSOR:
		// Turn off Windows cursor in fullscreen mode
		if (_isActive() && !_isWindowed())
		{
			SetCursor(NULL);
			return true;	// prevent Windows from setting cursor to window class cursor
		}
		break;
	}

	return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}


//
//	Main window call back function
//
LRESULT CALLBACK WndProc(
	HWND hWnd,
	UINT uMsg,
	WPARAM wParam,
	LPARAM lParam)
{
	// Obtain pointer to window application class
	CApplication * pApplication = CApplication::GetApplicationInstance();

	return pApplication->MsgProc(hWnd, uMsg, wParam, lParam);
}

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