Was gehört in eine Headerdatei?

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Sinan
Beiträge: 4
Registriert: 10.12.2011, 02:54
Echter Name: Sinan Fankhauser

Was gehört in eine Headerdatei?

Beitrag von Sinan »

Hallo zusammen.

Da ich gerade etwas Zeit habe um meinem Hobby, dem Programmieren nachzugehen, habe ich mich entschlossen, mal ein simples Spiel mit DirectX 11 und C++ zu programmieren einfach mal um zu schauen wie das eigentlich so funktioniert. Da ich aus der .NET Welt komme und meine Fähigkeiten eher bescheiden sind, habe ich mich durch verschiedene Tutorials gekämpft und habe nun ein simples Grundgerüst für mein Spiel zusammengebastelt welches mit Erlaubt, verschiedene Einstellungen wie z.B.die Auflösung oder den Multisamplemodus zu setzen. Das ganze startete eigentlich als kleiner Versuch, ist nun aber zu einer Datei mit 200 Zeilen herangewachsen. Also wolle ich das ganze in verschiedene Header/Sourcedateien aufteilen. Nach getaner Arbeit aber funktionierte leider gar nichts mehr, und mein Ausgabefenster in Visual Studio 2010 Express wurde geflutet mit Kompilerwarnungen aller Art. Nun stellte sich heraus, dass ich das Konzept vom Headerdateien wohl doch nicht so ganz verstanden habe.

Nun zuerst mal der Code:

Code: Alles auswählen

#define WIN32_LEAN_AND_MEAN
#define NOMINMAX // std::numeric_limits<>::max

#include <exception>
#include <stdexcept>
#include <vector>
#include <utility> // std::pair<>
#include <limits> // std::numeric_limits<>::max
#include <memory>
#include <string>
#include <sstream>
#include <cassert>
#include <windows.h>
#include <dxgi.h>
#include <d3d11.h>
#include <dxerr.h>
#include <xinput.h>

#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxerr.lib")
#pragma comment(lib, "xinput.lib")

///////////////////////////////////////////////////////////////////////////////
// Manifest.hpp
///////////////////////////////////////////////////////////////////////////////

#if defined _M_IX86
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") 
#elif defined _M_IA64 
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") 
#elif defined _M_X64 
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") 
#else 
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 
#endif

///////////////////////////////////////////////////////////////////////////////
// Uncopyable.hpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	class Uncopyable
	{
	protected:

		Uncopyable() {}
		~Uncopyable() {}

	private:

		Uncopyable(const Uncopyable&);
		Uncopyable& operator=(const Uncopyable&);
	};
}

///////////////////////////////////////////////////////////////////////////////
// UnknownInterface.hpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	// Verwaltet eine COM-Schnittstelle.

	template <typename InterfaceType>
	class UnknownInterface
	{
	private:

		InterfaceType* mInterface;

	public:

		UnknownInterface() : mInterface(nullptr) {}

		UnknownInterface(const UnknownInterface& unknownInterface) : 
			mInterface(unknownInterface.mInterface)
		{
			if (mInterface)
			{
				mInterface->AddRef();
			}
		}

		~UnknownInterface()
		{
			Release();
		}

		UnknownInterface& operator=(const UnknownInterface& unknownInterface)
		{		
			if (this != &unknownInterface)
			{
				Release();
				mInterface = unknownInterface.mInterface;

				if (mInterface)
				{
					mInterface->AddRef();
				}
			}
					
			return *this;
		}

		InterfaceType* GetInterface() const
		{
			return mInterface;
		}

		IID GetIID() const
		{
			return __uuidof(InterfaceType);
		}

		void Release()
		{
			if (mInterface)
			{
				mInterface->Release();
				mInterface = nullptr;
			}
		}

		InterfaceType* operator->() const
		{
			assert(nullptr != mInterface);
			return mInterface;
		}

		InterfaceType* operator*() const
		{
			assert(nullptr != mInterface);
			return mInterface;
		}

		InterfaceType** operator&()
		{
			return &mInterface;
		}
	};
}

///////////////////////////////////////////////////////////////////////////////
// DXGI.hpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	namespace DXGI
	{
		typedef UnknownInterface<IDXGIFactory1> Factory1;
		typedef UnknownInterface<IDXGIAdapter1> Adapter1;
		typedef UnknownInterface<IDXGIOutput> Output;
		typedef UnknownInterface<IDXGISwapChain> SwapChain;
	}
}

///////////////////////////////////////////////////////////////////////////////
// D3D11.hpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	namespace D3D11
	{
		typedef UnknownInterface<ID3D11Device> Device;
		typedef UnknownInterface<ID3D11DeviceContext> DeviceContext;
		typedef UnknownInterface<ID3D11RasterizerState> RasterizerState;
		typedef UnknownInterface<ID3D11Texture2D> Texture2D;
		typedef UnknownInterface<ID3D11RenderTargetView> RenderTargetView;
		typedef UnknownInterface<ID3D11DepthStencilView> DepthStencilView;
		typedef UnknownInterface<ID3D11Buffer> Buffer;
		typedef UnknownInterface<ID3D11InputLayout> InputLayout;
		typedef UnknownInterface<ID3D11VertexShader> VertexShader;
		typedef UnknownInterface<ID3D11PixelShader> PixelShader;
		typedef UnknownInterface<ID3DBlob> Blob;
	}
}

///////////////////////////////////////////////////////////////////////////////
// Exception.hpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	class Win32RuntimeError : public std::runtime_error
	{
	private:

		unsigned long mError;

	public:

		Win32RuntimeError(unsigned long error, const char* message) : 
			std::runtime_error(message) {}

		unsigned long GetError() const
		{
			return mError;
		}
	};

	class DirectXRuntimeError : public std::runtime_error
	{
	private:

		HRESULT mError;

	public:

		DirectXRuntimeError(HRESULT error, const char* message) : 
			std::runtime_error(message) {}

		HRESULT GetError() const
		{
			return mError;
		}
	};
}

///////////////////////////////////////////////////////////////////////////////
// KeyboardState.hpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	struct KeyboardState
	{
	public:

		bool Alt;
		bool Buffer[256];

	public:

		KeyboardState();
		bool operator[](unsigned char key) const;
		void Reset();
	};
}

///////////////////////////////////////////////////////////////////////////////
// KeyboardState.cpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	KeyboardState::KeyboardState() : Alt(false)
	{
		ZeroMemory(&Buffer, sizeof(bool) * 256);
	}

	bool KeyboardState::operator[](unsigned char key) const
	{
		return Buffer[key];
	}

	void KeyboardState::Reset()
	{
		Alt = false;
		ZeroMemory(&Buffer, sizeof(bool) * 256);
	}
}

///////////////////////////////////////////////////////////////////////////////
// Keyboard.hpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	struct Keyboard : protected Uncopyable
	{
	private:

		KeyboardState mKeyboardStates[2];

	public:

		void Update(const KeyboardState& newKeyboardState);
		bool IsAltDown() const;
		bool IsAltToggled() const;
		bool IsKeyDown(unsigned char key) const;
		bool IsKeyToggled(unsigned char key) const;
	};
}

///////////////////////////////////////////////////////////////////////////////
// Keyboard.cpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	void Keyboard::Update(const KeyboardState& newKeyboardState)
	{
		mKeyboardStates[0] = mKeyboardStates[1];
		mKeyboardStates[1] = newKeyboardState;
	}

	bool Keyboard::IsAltDown() const
	{
		return mKeyboardStates[1].Alt;
	}

	bool Keyboard::IsAltToggled() const
	{
		return mKeyboardStates[1].Alt && !mKeyboardStates[0].Alt;
	}

	bool Keyboard::IsKeyDown(unsigned char key) const
	{
		return mKeyboardStates[1][key];
	}

	bool Keyboard::IsKeyToggled(unsigned char key) const
	{
		return mKeyboardStates[1][key] && !mKeyboardStates[0][key];
	}
}

///////////////////////////////////////////////////////////////////////////////
// Gamepad.hpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	class Gamepad : protected Uncopyable
	{
	private:

		bool mConnected;
		bool mStateChanged;
		float mLeftTrigger;
		float mRightTrigger;
		float mLeftTumbX;
		float mLeftTumbY;
		float mRightTumbX;
		float mRightTumbY;
		XINPUT_STATE mOldState;
		XINPUT_STATE mNewState;

	public:

		Gamepad();
		~Gamepad();
		bool IsConnected() const;
		bool HasStateChanged() const;
		bool IsButtonDown(unsigned short button) const;
		bool IsButtonToggled(unsigned short button) const;
		float GetLeftTrigger() const;
		float GetRightTrigger() const;
		float GetLeftThumbX() const;
		float GetLeftThumbY() const;
		float GetRightThumbX() const;
		float GetRightThumbY() const;
		void SetVibration(unsigned short leftMotorSpeed, unsigned short rightMotorSpeed) const;
		void Update();

	private:

		void HandleUpdatedState();
	};
}

///////////////////////////////////////////////////////////////////////////////
// Gamepad.cpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	Gamepad::Gamepad() :
		mConnected(false),
		mStateChanged(false),
		mLeftTrigger(0.0f),
		mRightTrigger(0.0f),
		mLeftTumbX(0.0f),
		mLeftTumbY(0.0f),
		mRightTumbX(0.0f),
		mRightTumbY(0.0f)
	{
		ZeroMemory(&mOldState, sizeof(mOldState));
		ZeroMemory(&mNewState, sizeof(mNewState));

		XInputEnable(TRUE);
	}

	Gamepad::~Gamepad()
	{
		XInputEnable(FALSE);
	}
							
	bool Gamepad::IsConnected() const
	{
		return mConnected;
	}

	bool Gamepad::HasStateChanged() const
	{
		return mStateChanged;
	}

	bool Gamepad::IsButtonDown(unsigned short button) const
	{
		return ((mNewState.Gamepad.wButtons & button) != 0);
	}

	bool Gamepad::IsButtonToggled(unsigned short button) const
	{
		return ((mNewState.Gamepad.wButtons & button) != 0) && ((mOldState.Gamepad.wButtons & button) == 0);
	}

	float Gamepad::GetLeftTrigger() const
	{
		return mLeftTrigger;
	}

	float Gamepad::GetRightTrigger() const
	{
		return mRightTrigger;
	}

	float Gamepad::GetLeftThumbX() const
	{
		return mLeftTumbX;
	}

	float Gamepad::GetLeftThumbY() const
	{
		return mLeftTumbY;
	}

	float Gamepad::GetRightThumbX() const
	{
		return mRightTumbX;
	}

	float Gamepad::GetRightThumbY() const
	{
		return mRightTumbY;
	}

	void Gamepad::SetVibration(unsigned short leftMotorSpeed, unsigned short rightMotorSpeed) const
	{
		XINPUT_VIBRATION vibration;
		vibration.wLeftMotorSpeed = leftMotorSpeed;
		vibration.wRightMotorSpeed = rightMotorSpeed;
				
		XInputSetState(0, &vibration);
	}

	void Gamepad::Update()
	{
		mOldState = mNewState;

		if (XInputGetState(0, &mNewState) == ERROR_DEVICE_NOT_CONNECTED)
		{
			mConnected = false;
		}
		else
		{
			// Prüfen ob sich der Status aktualisier hat und somit neue Eingaben
			// betätigt worden sind.

			mConnected = true;
			mStateChanged = (mOldState.dwPacketNumber != mNewState.dwPacketNumber);

			if (mStateChanged)
			{						
				HandleUpdatedState();
			}
		}
	}

	// Verarbeitet die Eingaben in brauchbare Werte.

	void Gamepad::HandleUpdatedState()
	{
		const auto leftTrigger = mNewState.Gamepad.bLeftTrigger;
		const auto rightTrigger = mNewState.Gamepad.bRightTrigger;
		const auto leftThumbX = mNewState.Gamepad.sThumbLX;
		const auto leftThumbY = mNewState.Gamepad.sThumbLY;
		const auto rightThumbX = mNewState.Gamepad.sThumbRX;
		const auto rightThumbY = mNewState.Gamepad.sThumbRY;

		mLeftTrigger = 0.0f;
		mRightTrigger = 0.0f;
		mLeftTumbX = 0.0f;
		mLeftTumbY = 0.0f;
		mRightTumbX = 0.0f;
		mRightTumbY = 0.0f;

		const auto triggerMaximalRange = static_cast<float>(std::numeric_limits<unsigned char>::max());
		const auto thumbMaximalRange = static_cast<float>(std::numeric_limits<short>::max());

		if (leftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
		{
			mLeftTrigger = leftTrigger / triggerMaximalRange;
		}

		if (rightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
		{
			mRightTrigger = rightTrigger / triggerMaximalRange;
		}
			
		if (leftThumbX > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE || leftThumbX < (-XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE))
		{
			mLeftTumbX = leftThumbX / thumbMaximalRange;
		}
			
		if (leftThumbY > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE || leftThumbY < (-XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE))
		{
			mLeftTumbY = leftThumbY / thumbMaximalRange;
		}

		if (rightThumbX > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE || rightThumbX < (-XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE))
		{
			mRightTumbX = rightThumbX / thumbMaximalRange;
		}
			
		if (rightThumbY > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE || rightThumbY < (-XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE))
		{
			mRightTumbY = -(rightThumbY / thumbMaximalRange);
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// RenderWindowClass.hpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	class RenderWindowClass : protected Uncopyable
	{
	protected:

		const HINSTANCE mInstanceHandle;

	protected:

		explicit RenderWindowClass(WNDPROC windowProcedure);
		virtual ~RenderWindowClass();
	};
}

///////////////////////////////////////////////////////////////////////////////
// RenderWindowClass.cpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	RenderWindowClass::RenderWindowClass(WNDPROC windowProcedure) : 
		mInstanceHandle(reinterpret_cast<HINSTANCE>(GetModuleHandleW(nullptr)))
	{
		WNDCLASSEXW extendedWindowClass;
		extendedWindowClass.cbSize = sizeof(extendedWindowClass);
		extendedWindowClass.cbClsExtra = 0;
		extendedWindowClass.cbWndExtra = 0;
		extendedWindowClass.hbrBackground = reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
		extendedWindowClass.hCursor = LoadCursorW(nullptr, IDC_ARROW);
		extendedWindowClass.hIcon = LoadIconW(nullptr, IDI_APPLICATION);
		extendedWindowClass.hIconSm = LoadIconW(nullptr, IDI_APPLICATION);
		extendedWindowClass.hInstance = mInstanceHandle;
		extendedWindowClass.lpfnWndProc = windowProcedure;
		extendedWindowClass.lpszClassName = L"RenderWindow";
		extendedWindowClass.lpszMenuName = nullptr;
		extendedWindowClass.style = CS_DBLCLKS;

		if (!RegisterClassExW(&extendedWindowClass))
		{
			throw Win32RuntimeError(GetLastError(), "RegisterClassExW() failed!");
		}
	}

	RenderWindowClass::~RenderWindowClass()
	{
		UnregisterClassW(L"RenderWindow", mInstanceHandle);
	}
}

///////////////////////////////////////////////////////////////////////////////
// RenderWindow.hpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	class RenderWindow : protected RenderWindowClass
	{		
	private:

		bool mActivated;
		bool mBorderless;
		HWND mWindowHandle;
		KeyboardState mKeyboardState;

	public:

		RenderWindow();
		~RenderWindow();
		bool IsMinimized() const;
		bool IsMaximized() const;
		bool IsActivated() const;
		bool IsBorderless() const;
		void SetCaption(const wchar_t* caption) const;
		void SetToForeground() const;
		const HINSTANCE GetInstanceHandle() const;
		const HWND GetWindowHandle() const;
		KeyboardState& GetKeyboardState();
		std::pair<unsigned int, unsigned int> GetClientSize() const;
		void SetClientSize(unsigned int width, unsigned int height) const;
		void SetBorderlessState(bool borderless);
		void Show(int showCommand) const;
		void Close() const;
		void CenterAtNearestWorkArea() const;

	private:

		bool MessageHandler(unsigned int message, WPARAM wordParameter, LPARAM longParameter);
		static LRESULT __stdcall WindowProcedure(HWND windowHandle, unsigned int message, WPARAM wordParameter, LPARAM longParameter);
	};
}

///////////////////////////////////////////////////////////////////////////////
// RenderWindow.cpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	RenderWindow::RenderWindow() : RenderWindowClass(&RenderWindow::WindowProcedure), 
		mActivated(false),
		mBorderless(false)
	{
		mWindowHandle = CreateWindowExW(
			WS_EX_APPWINDOW,
			L"RenderWindow",
			nullptr,
			WS_OVERLAPPEDWINDOW,
			CW_USEDEFAULT,
			CW_USEDEFAULT,
			CW_USEDEFAULT,
			CW_USEDEFAULT,
			nullptr,
			nullptr,
			mInstanceHandle,
			reinterpret_cast<void*>(this));

		if (mWindowHandle == nullptr)
		{
			throw Win32RuntimeError(GetLastError(), "RegisterClassExW() failed!");
		}
	}

	RenderWindow::~RenderWindow()
	{
		DestroyWindow(mWindowHandle);
	}

	bool RenderWindow::IsMinimized() const
	{
		return (IsIconic(mWindowHandle) == TRUE);
	}

	bool RenderWindow::IsMaximized() const
	{
		return (IsZoomed(mWindowHandle) == TRUE);
	}

	bool RenderWindow::IsActivated() const
	{
		return mActivated;
	}

	bool RenderWindow::IsBorderless() const
	{
		return mBorderless;
	}

	void RenderWindow::SetCaption(const wchar_t* caption) const
	{
		SetWindowTextW(mWindowHandle, caption);
	}

	void RenderWindow::SetToForeground() const
	{
		SetForegroundWindow(mWindowHandle);
	}

	const HINSTANCE RenderWindow::GetInstanceHandle() const
	{
		return mInstanceHandle;
	}

	const HWND RenderWindow::GetWindowHandle() const
	{
		return mWindowHandle;
	}

	KeyboardState& RenderWindow::GetKeyboardState()
	{
		return mKeyboardState;
	}

	std::pair<unsigned int, unsigned int> RenderWindow::GetClientSize() const
	{
		RECT clientRectangle;
		GetClientRect(mWindowHandle, &clientRectangle);

		return std::make_pair(static_cast<unsigned int>(clientRectangle.right), static_cast<unsigned int>(clientRectangle.bottom));
	}

	void RenderWindow::SetClientSize(unsigned int width, unsigned int height) const
	{
		RECT windowRectangle;
		windowRectangle.left = 0;
		windowRectangle.top = 0;
		windowRectangle.right = static_cast<long>(width);
		windowRectangle.bottom = static_cast<long>(height);

		const auto style = GetWindowLongPtrW(mWindowHandle, GWL_STYLE);
		const auto extendedStyle = GetWindowLongPtrW(mWindowHandle, GWL_EXSTYLE);

		AdjustWindowRectEx(&windowRectangle, style, FALSE, extendedStyle);

		SetWindowPos(
			mWindowHandle,
			nullptr, 
			0,
			0,
			static_cast<int>(windowRectangle.right - windowRectangle.left),
			static_cast<int>(windowRectangle.bottom - windowRectangle.top), 
			SWP_NOMOVE | 
			SWP_NOZORDER | 
			SWP_NOACTIVATE);	
	}

	void RenderWindow::SetBorderlessState(bool borderless)
	{
		if (mBorderless != borderless)
		{
			mBorderless = borderless;
			SetWindowLongPtrW(mWindowHandle, GWL_STYLE, mBorderless ? WS_POPUP : WS_OVERLAPPEDWINDOW);
			
			SetWindowPos(
				mWindowHandle,
				HWND_TOP,
				0, 
				0,
				0,
				0,
				SWP_NOMOVE | 
				SWP_NOSIZE | 
				SWP_NOZORDER | 
				SWP_SHOWWINDOW | 
				SWP_FRAMECHANGED);
		}
	}

	void RenderWindow::Show(int showCommand) const
	{
		ShowWindow(mWindowHandle, showCommand);
	}

	void RenderWindow::Close() const
	{
		PostMessageW(mWindowHandle, WM_CLOSE, 0, 0);
	}

	// Platziert das Fenster in der Mitte des nächstgelegenen Arbeitsbereiches.

	void RenderWindow::CenterAtNearestWorkArea() const
	{
		RECT windowRectangle;
		GetWindowRect(mWindowHandle, &windowRectangle);
            
		const auto windowWidth = windowRectangle.right - windowRectangle.left;
		const auto windowHeight = windowRectangle.bottom - windowRectangle.top;				
		const auto monitorHandle = ::MonitorFromRect(&windowRectangle, MONITOR_DEFAULTTONEAREST);

		MONITORINFO monitorInfo;
		monitorInfo.cbSize = sizeof(monitorInfo);			
			
		GetMonitorInfoW(monitorHandle, &monitorInfo);

		const auto x = static_cast<int>((monitorInfo.rcWork.right - windowWidth) / 2);
		const auto y = static_cast<int>((monitorInfo.rcWork.bottom - windowHeight) / 2);

		SetWindowPos(
			mWindowHandle,
			nullptr, 
			x,
			y,
			0,
			0, 
			SWP_NOSIZE | 
			SWP_NOZORDER | 
			SWP_NOACTIVATE);
	}

	// WICHTIG! Diese Methode darf unter keinen Umständen eine Ausnahme auslösen oder weiterleiten!

	bool RenderWindow::MessageHandler(unsigned int message, WPARAM wordParameter, LPARAM longParameter)
	{
		auto processMessage = true;
						
		switch (message)
		{
			case WM_SYSCOMMAND:
			{
				switch (wordParameter & 0xFFF0)
				{
					case SC_KEYMENU:
					{
						processMessage = false;
						break;
					}
					case SC_MOVE:
					case SC_SIZE:
					case SC_MONITORPOWER:
					case SC_SCREENSAVE:
					{
						// Wenn der Fensterrahmenlose Status gesetzt worden ist, können wir annehmen,
						// dass der Vollbildmodus gesetzt ist.

						if (mBorderless)
						{
							processMessage = false;
						}
							
						break;
					}
				}

				break;
			}
			case WM_ACTIVATE:
			{
				if (wordParameter == WA_INACTIVE)
				{
					mActivated = false;
					mKeyboardState.Reset();						
				}
				else
				{
					mActivated = true;
				}

				break;
			}
			case WM_KEYDOWN:
			case WM_SYSKEYDOWN:
			{
				mKeyboardState.Alt = (0 != (longParameter & (1 << 29)));
				mKeyboardState.Buffer[static_cast<unsigned char>(wordParameter & 0xFF)] = true;
				break;
			}
			case WM_KEYUP:
			case WM_SYSKEYUP:
			{
				mKeyboardState.Alt = (0 != (longParameter & (1 << 29)));
				mKeyboardState.Buffer[static_cast<unsigned char>(wordParameter & 0xFF)] = false;
				break;
		}
			case WM_GETMINMAXINFO:
			{
				// Die minimale Fenstergrösse (nicht Klientgrösse) festlegen.

				reinterpret_cast<MINMAXINFO*>(longParameter)->ptMinTrackSize.x = 400;
				reinterpret_cast<MINMAXINFO*>(longParameter)->ptMinTrackSize.y = 400;
				break;
			}
				
			case WM_CLOSE:
			{
				PostQuitMessage(0);
				break;
			}
		}

		return processMessage;
	}

	LRESULT __stdcall RenderWindow::WindowProcedure(HWND windowHandle, unsigned int message, WPARAM wordParameter, LPARAM longParameter)
	{
		auto processMessage = true;

		// Die beim erstellen des Fensters übergebene Fensterinstanz ermitteln und speichern.

		if (WM_NCCREATE == message)
		{
			SetWindowLongPtrW(
				windowHandle,
				GWL_USERDATA,
				reinterpret_cast<long>(
				reinterpret_cast<RenderWindow*>(
				reinterpret_cast<LPCREATESTRUCTW>(longParameter)->lpCreateParams)));
		}

		// Die Nachrichten an den Nachrichtenhandler der gespeicherten Fensterinstanz weiterleiten.

		const auto messageListener = reinterpret_cast<RenderWindow*>(GetWindowLongPtrW(windowHandle, GWL_USERDATA));
						
		if (messageListener)
		{
			processMessage = messageListener->MessageHandler(message, wordParameter, longParameter);
		}

		// Die WM_CLOSE Nachricht nicht weiterverarbeiten da ansonsten DestroyWindow
		// aufgerufen weden würde.

		if (message == WM_CLOSE || !processMessage)
		{
			return 0;
		}
		else
		{
			return DefWindowProcW(windowHandle, message, wordParameter, longParameter);
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// RenderSystem.hpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	typedef std::vector<std::pair<unsigned int, unsigned int>> MultisampleList;
	typedef std::vector<std::pair<unsigned int, unsigned int>> DisplayModeList;

	class RenderSystem : protected Uncopyable
	{
	private:

		DXGI_FORMAT mDisplayModeFormat;
		D3D_FEATURE_LEVEL mFeatureLevel;
		DXGI::Factory1 mFactory1;
		DXGI::Adapter1 mAdapter1;
		DXGI::Output mOutput;
		D3D11::Device mDevice;
		D3D11::DeviceContext mImmediateContext;
		MultisampleList mMultisampleList;
		DisplayModeList mDisplayModeList;

	public:

		explicit RenderSystem(DXGI_FORMAT displayModeFormat);
		~RenderSystem();
		DXGI_FORMAT GetDisplayModeFormat() const;
		D3D_FEATURE_LEVEL GetFeatureLevel() const;
		DXGI::Factory1& GetFactory1();
		DXGI::Adapter1& GetAdapter1();
		DXGI::Output& GetOutput();
		D3D11::Device& GetDevice();
		D3D11::DeviceContext& GetImmediateContext();
		unsigned int GetMultisampleListSize() const;
		unsigned int GetDisplayModeListSize() const;
		MultisampleList& GetMultisampleList();
		DisplayModeList& GetDisplayModeList();

	private:

		void CreateMultisampleList();
		void CreateDisplayModeList();
	};
}

///////////////////////////////////////////////////////////////////////////////
// RenderSystem.cpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	RenderSystem::RenderSystem(DXGI_FORMAT displayModeFormat) : 
		mDisplayModeFormat(displayModeFormat)
	{
		auto result = CreateDXGIFactory1(mFactory1.GetIID(), reinterpret_cast<void**>(&mFactory1));

		if (FAILED(result))
		{
			throw DirectXRuntimeError(result, "CreateDXGIFactory1() failed!");
		}

		// Den Standardadapter (Grafikkarte) verwenden.

		result = mFactory1->EnumAdapters1(0, &mAdapter1);

		if (FAILED(result))
		{
			throw DirectXRuntimeError(result, "IDXGIFactory1::EnumAdapters1() failed!");
		}

		// Das Standardausgabegerät (Monitor, Projektor) des Adapters verwenden.

		result = mAdapter1->EnumOutputs(0, &mOutput);

		if (FAILED(result))
		{
			throw DirectXRuntimeError(result, "IDXGIAdapter1::EnumOutputs() failed!");
		}
				
		unsigned int createDeviceFlags = 0;
#if defined(DEBUG) || defined(_DEBUG)
		createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
		result = D3D11CreateDevice(
			*mAdapter1,
			D3D_DRIVER_TYPE_UNKNOWN,
			nullptr,
			createDeviceFlags,
			nullptr,
			0,
			D3D11_SDK_VERSION,
			&mDevice,
			&mFeatureLevel,
			&mImmediateContext);

		if (FAILED(result))
		{
			throw DirectXRuntimeError(result, "D3D11CreateDevice() failed!");
		}

		CreateMultisampleList();
		CreateDisplayModeList();
	}

	RenderSystem::~RenderSystem()
	{
		mImmediateContext->ClearState();
		mImmediateContext->Flush();
	}

	DXGI_FORMAT RenderSystem::GetDisplayModeFormat() const
	{
		return mDisplayModeFormat;
	}

	D3D_FEATURE_LEVEL RenderSystem::GetFeatureLevel() const
	{
		return mFeatureLevel;
	}

	DXGI::Factory1& RenderSystem::GetFactory1()
	{
		return mFactory1;
	}

	DXGI::Adapter1& RenderSystem::GetAdapter1()
	{
		return mAdapter1;
	}

	DXGI::Output& RenderSystem::GetOutput()
	{
		return mOutput;
	}

	D3D11::Device& RenderSystem::GetDevice()
	{
		return mDevice;
	}

	D3D11::DeviceContext& RenderSystem::GetImmediateContext()
	{
		return mImmediateContext;
	}

	unsigned int RenderSystem::GetMultisampleListSize() const
	{
		return mMultisampleList.size();
	}

	unsigned int RenderSystem::GetDisplayModeListSize() const
	{
		return mDisplayModeList.size();
	}

	MultisampleList& RenderSystem::GetMultisampleList()
	{
		return mMultisampleList;
	}

	DisplayModeList& RenderSystem::GetDisplayModeList()
	{
		return mDisplayModeList;
	}

	void RenderSystem::CreateMultisampleList()
	{
		// Zuerst den ausgeschalteten Multisamplemodus speichern.

		mMultisampleList.push_back(std::pair<unsigned int, unsigned int>(1, 0));
			
		// Nach allen möglichen Multisamplemodi suchen.

		for (unsigned int sampleCount = 2; sampleCount < (D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT + 1); sampleCount += 2)
		{
			unsigned int sampleQualityLevels = 0;
			const auto result = mDevice->CheckMultisampleQualityLevels(mDisplayModeFormat, sampleCount, &sampleQualityLevels);

			if (FAILED(result))
			{
				throw DirectXRuntimeError(result, "ID3D11Device::CheckMultisampleQualityLevels()");
			}
								
			if (sampleQualityLevels)
			{
				mMultisampleList.push_back(std::pair<unsigned int, unsigned int>(sampleCount, sampleQualityLevels - 1));
			}
		}
	}

	void RenderSystem::CreateDisplayModeList()
	{
		// Die Anzahl an Displaymodi ermitteln.

		unsigned int displayModeCount = 0;
		auto result = mOutput->GetDisplayModeList(mDisplayModeFormat, 0, &displayModeCount, nullptr);

		if (FAILED(result))
		{
			throw DirectXRuntimeError(result, "IDXGIOutput::GetDisplayModeList()");
		}

		// Die Displaymodi in einer temporären Liste speichern.

		std::vector<DXGI_MODE_DESC> displayModeList(displayModeCount);
		result = mOutput->GetDisplayModeList(mDisplayModeFormat, 0, &displayModeCount, &displayModeList[0]);

		if (FAILED(result))
		{
			throw DirectXRuntimeError(result, "IDXGIOutput::GetDisplayModeList()");
		}

		unsigned int previousWidth = 0;
		unsigned int previousHeight = 0;

		// Die entgültige Displaymodusliste erstellen.

		for (auto iterator = displayModeList.begin(); iterator != displayModeList.end(); ++iterator)
		{
			// Doppelte Einträge aussortieren.

			if (previousWidth != iterator->Width || previousHeight != iterator->Height)
			{
				previousWidth = iterator->Width;
				previousHeight = iterator->Height;

				mDisplayModeList.push_back(std::pair<unsigned int, unsigned int>(previousWidth, previousHeight));
			}
		}

		if (!mDisplayModeList.size())
		{
			throw std::runtime_error("Failed to find valid display mode!");
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// RenderTarget.hpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	class RenderTarget : protected Uncopyable
	{
	private:

		bool mOccluded;
		bool mReady;
		bool mFullscreen;
		bool mLostFullscreen;
		bool mWasMaximized;
		unsigned int mClientWidthToRestore;
		unsigned int mClientHeightToRestore;
		RenderSystem* mRenderSystem;
		RenderWindow* mRenderWindow;
		DXGI::SwapChain mSwapChain;
		D3D11::Device mDevice;
		D3D11::DeviceContext mImmediateContext;
		D3D11::RenderTargetView mRenderTargetView;
		D3D11::Texture2D mDepthStencilBuffer;
		D3D11::DepthStencilView mDepthStencilView;
		D3D11_VIEWPORT mViewport;

	public:

		RenderTarget(RenderSystem* renderSystem, RenderWindow* renderWindow, unsigned int sampleCount, unsigned int sampleQuality);
		~RenderTarget();
		bool IsOccluded() const;
		bool IsReady() const;
		bool IsFullscreen() const;
		bool IsMinimizedWhileFullscreen() const;
		bool IsMultisampled() const;
		std::pair<unsigned int, unsigned int> GetMultisampleMode() const;
		std::pair<unsigned int, unsigned int> GetDisplayMode() const;
		bool UpdateState();
		bool SetWindowedMode();
		bool SetFullscreenMode(unsigned int width, unsigned int height);
		void Enable();
		void Clear(float red, float green, float blue, float alpha) const;
		void Present(unsigned int presentInterval, unsigned int presentFlags);

	private:
				
		bool CheckForPossibleClientSizeChange() const;
		bool ResizeTarget(unsigned int width, unsigned int height) const;
		void AdjustBuffers() const;
		void SetFullscreenState(bool fullscreen);
		void CreateSwapChainDepencies();
		void DestroySwapChainDepencies();
	};
}

///////////////////////////////////////////////////////////////////////////////
// RenderTarget.cpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	RenderTarget::RenderTarget(RenderSystem* renderSystem, RenderWindow* renderWindow, unsigned int sampleCount, unsigned int sampleQuality) :
		mOccluded(false),
		mReady(false),
		mFullscreen(false),
		mLostFullscreen(false),
		mWasMaximized(false),
		mClientWidthToRestore(0),
		mClientHeightToRestore(0),
		mRenderSystem(renderSystem),
		mRenderWindow(renderWindow)
	{
		if (renderSystem == nullptr)
		{
			throw std::invalid_argument("Invalid render system!");
		}

		if (renderWindow == nullptr)
		{
			throw std::invalid_argument("Invalid render window!");
		}

		mDevice = mRenderSystem->GetDevice();
		mImmediateContext = mRenderSystem->GetImmediateContext();
						
		// Die bestehende Fensterklientgrösse als Buffergrösse verwenden.

		const auto clientSize = mRenderWindow->GetClientSize();
		const auto windowHandle = mRenderWindow->GetWindowHandle();
						
		// Einen Front- und zwei Backbuffer verwenden.

		DXGI_SWAP_CHAIN_DESC swapChainDescription;
		swapChainDescription.BufferDesc.Format = mRenderSystem->GetDisplayModeFormat();
		swapChainDescription.BufferDesc.Width = clientSize.first;
		swapChainDescription.BufferDesc.Height = clientSize.second;
		swapChainDescription.BufferDesc.RefreshRate.Numerator = 0;
		swapChainDescription.BufferDesc.RefreshRate.Denominator = 0;
		swapChainDescription.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
		swapChainDescription.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
		swapChainDescription.BufferCount = 2;
		swapChainDescription.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
		swapChainDescription.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
		swapChainDescription.OutputWindow = windowHandle;
		swapChainDescription.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
		swapChainDescription.SampleDesc.Count = sampleCount;
		swapChainDescription.SampleDesc.Quality = sampleQuality;
		swapChainDescription.Windowed = TRUE;

		const auto result = mRenderSystem->GetFactory1()->CreateSwapChain(*mDevice, &swapChainDescription, &mSwapChain);

		if (FAILED(result))
		{
			throw DirectXRuntimeError(result, "IDXGIFactory1::CreateSwapChain() failed!");
		}

		// Die Fensterverwaltung selber übernehmen.

		mRenderSystem->GetFactory1()->MakeWindowAssociation(windowHandle, DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES);
	}

	RenderTarget::~RenderTarget()
	{
		mImmediateContext->OMSetRenderTargets(0, nullptr, nullptr);
			
		// Ein Swapchain kann nur im Fensterstatus freigegeben werden.

		if (mFullscreen)
		{
			mSwapChain->SetFullscreenState(FALSE, nullptr);

			// Das Renderfenster wiederherstellen und dazu nur Methoden verwenden die
			// keine Ausnahmen auslösen.

			mRenderWindow->SetBorderlessState(false);
			mRenderWindow->SetClientSize(mClientWidthToRestore, mClientHeightToRestore);
			
			if (mWasMaximized)
			{
				mRenderWindow->Show(SW_MAXIMIZE);
			}
		}
	}

	bool RenderTarget::IsOccluded() const
	{
		return mOccluded;
	}
				
	bool RenderTarget::IsReady() const
	{
		return mReady;
	}

	bool RenderTarget::IsFullscreen() const
	{
		return mFullscreen;
	}

	bool RenderTarget::IsMinimizedWhileFullscreen() const
	{
		return mLostFullscreen;
	}

	bool RenderTarget::IsMultisampled() const
	{
		DXGI_SWAP_CHAIN_DESC swapChainDescription;
		mSwapChain->GetDesc(&swapChainDescription);

		return swapChainDescription.SampleDesc.Count > 1;
	}

	std::pair<unsigned int, unsigned int> RenderTarget::GetMultisampleMode() const
	{
		DXGI_SWAP_CHAIN_DESC swapChainDescription;
		mSwapChain->GetDesc(&swapChainDescription);
			 
		return std::pair<unsigned int, unsigned int>(swapChainDescription.SampleDesc.Count, swapChainDescription.SampleDesc.Quality);
	}

	std::pair<unsigned int, unsigned int> RenderTarget::GetDisplayMode() const
	{
		// Der Displaymodus entspricht der Fensterklientgrösse.

		return mRenderWindow->GetClientSize();
	}

	bool RenderTarget::UpdateState()
	{
		auto targetResized = false;

		BOOL fullscreen;
		mSwapChain->GetFullscreenState(&fullscreen, nullptr);

		// Prüfen ob der gespeicherte Vollbildstatus mit der tatsächlichen übereinstimmt.

		if (mFullscreen && !fullscreen)
		{
			mFullscreen = false;
			mLostFullscreen = true;
				
			// Ansonsten das Fenster minimieren.

			DestroySwapChainDepencies();
			mRenderWindow->Show(SW_MINIMIZE);
		}

		// Nur fortfahren wenn das Renderfenster sichtbar ist.

		if (!mRenderWindow->IsMinimized())
		{
			// Den zuvor minimierten Vollbildmodus wiederherstellen wenn nötig.

			if (mLostFullscreen)
			{
				mLostFullscreen = false;
				SetFullscreenState(true);
				AdjustBuffers();
				CreateSwapChainDepencies();
			}
				
			// Das Rendertarget einer neuen Fensterklientgrösse anpassen wenn nötig.
				
			if (CheckForPossibleClientSizeChange())
			{
				targetResized = true;
				DestroySwapChainDepencies();
				AdjustBuffers();
				CreateSwapChainDepencies();										
			}
		}

		// Prüfen ob der Renderausgabereich (Renderfenster) komplett verdeckt ist.

		if (mOccluded)
		{
			Present(0, DXGI_PRESENT_TEST);
		}

		return targetResized;
	}

	bool RenderTarget::SetWindowedMode()
	{
		auto targetResized = false;

		// Nur fortfahren wenn das Renderfenster nicht minimiert ist.

		if (!mRenderWindow->IsMinimized())
		{
			if (mFullscreen)
			{
				// Den Fensterstatus setzen und den Fensterrahmen wiederherstellen.

				SetFullscreenState(false);
				mRenderWindow->SetBorderlessState(false);

				// Die Fenstermoduskonfiguration wiederherstellen.

				targetResized = ResizeTarget(mClientWidthToRestore, mClientHeightToRestore);

				if (mWasMaximized)
				{
					mWasMaximized = false;
					targetResized = true;
					mRenderWindow->Show(SW_MAXIMIZE);
				}

				// Die Backbuffer der neuen Fensterklientgrösse (Frontbuffergrösse) anpassen.

				if (targetResized)
				{
					DestroySwapChainDepencies();
					AdjustBuffers();
				}
			}

			// Das Rendertarget fertigstellen.

			if (!mReady)
			{
				CreateSwapChainDepencies();
			}
		}

		return targetResized;
	}

	bool RenderTarget::SetFullscreenMode(unsigned int width, unsigned int height)
	{
		auto targetResized = false;

		// Nur fortfahren wenn das Renderfenster nicht minimiert ist.

		if (!mRenderWindow->IsMinimized())
		{
			if (!mFullscreen)
			{
				DestroySwapChainDepencies();

				// Ein eventuell maximiertes Renderfenster normalisieren damit die korrekte
				// Fenstermoduskonfiguration ermittelt werden kann.

				if (mRenderWindow->IsMaximized())
				{
					mWasMaximized = true;
					mRenderWindow->Show(SW_RESTORE);
				}

				const auto clientSize = mRenderWindow->GetClientSize();
				mClientWidthToRestore = clientSize.first;
				mClientHeightToRestore = clientSize.second;
										
				// Vor dem setzen der gewünschten Vollbildauflösung den Fensterrahmen entfernen
				// da die Fenstergrösse ansonsten auf die Desktopgrösse limitiert ist.

				mRenderWindow->SetBorderlessState(true);

				if (ResizeTarget(width, height))
				{
					targetResized = true;
					AdjustBuffers();
				}

				// Nach dem setzen des Vollbildstatus die Backbuffergrösse unabhängig einer
				// Manipulation der Fensterklientgrösse (Frontbuffergrösse) anpassen da es
				// ansonsten zu einer IDXGISwapChain::Present() Performancewarnung kommt.

				SetFullscreenState(true);
				AdjustBuffers();
			}
			else
			{
				// Eine neue Vollbildauflösung setzen wenn nötig.

				if (ResizeTarget(width, height))
				{
					targetResized = true;
					DestroySwapChainDepencies();
					AdjustBuffers();
				}
			}

			// Das Rendertarget fertigstellen.

			if (!mReady)
			{
				CreateSwapChainDepencies();
			}
		}

		return targetResized;
	}
		
	void RenderTarget::Enable()
	{
		if (mReady)
		{
			mImmediateContext->OMSetRenderTargets(1, &mRenderTargetView, *mDepthStencilView);
			mImmediateContext->RSSetViewports(1, &mViewport);
		}
	}

	void RenderTarget::Clear(float red, float green, float blue, float alpha) const
	{
		if (mReady)
		{
			const float clearColor[4] = {0.0f, 0.125f, 0.3f, 1.0f};
			const unsigned int clearFlags = D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL;
						
			mImmediateContext->ClearRenderTargetView(*mRenderTargetView, clearColor);
			mImmediateContext->ClearDepthStencilView(*mDepthStencilView, clearFlags, 1.0f, 0);			
		}
	}

	void RenderTarget::Present(unsigned int presentInterval, unsigned int presentFlags)
	{
		const auto result = mSwapChain->Present(presentInterval, presentFlags);

		switch (result)
		{
			case S_OK:
			{
				mOccluded = false;
				break;
			}
			case DXGI_STATUS_OCCLUDED:
			{
				mOccluded = true;
				break;
			}
			default:
			{
				throw DirectXRuntimeError(result, "IDXGISwapChain::Present() failed!");
			}
		}
	}
		
	bool RenderTarget::CheckForPossibleClientSizeChange() const
	{
		const auto clientSize = mRenderWindow->GetClientSize();

		DXGI_SWAP_CHAIN_DESC swapChainDescription;
		mSwapChain->GetDesc(&swapChainDescription);

		// Prüfen ob die Backbuffergrösse und die Fensterklientgrösse (Frontbuffergrösse) übereinstimmen.

		if (clientSize.first != swapChainDescription.BufferDesc.Width || clientSize.second != swapChainDescription.BufferDesc.Height)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	bool RenderTarget::ResizeTarget(unsigned int width, unsigned int height) const
	{
		const auto clientSize = mRenderWindow->GetClientSize();

		if (clientSize.first != width || clientSize.second != height)
		{
			DXGI_SWAP_CHAIN_DESC swapChainDescription;
			mSwapChain->GetDesc(&swapChainDescription);

			swapChainDescription.BufferDesc.Width = width;
			swapChainDescription.BufferDesc.Height = height;

			const auto result = mSwapChain->ResizeTarget(&swapChainDescription.BufferDesc);

			if (FAILED(result))
			{
				throw DirectXRuntimeError(result, "IDXGISwapChain::ResizeTarget() failed!");
			}

			return true;
		}
		else
		{
			return false;
		}
	}

	void RenderTarget::AdjustBuffers() const
	{
		const auto clientSize = mRenderWindow->GetClientSize();

		DXGI_SWAP_CHAIN_DESC swapChainDescription;
		mSwapChain->GetDesc(&swapChainDescription);

		// Die Backbuffergrösse der Fensterklientgrösse (Frontbuffergrösse) anpassen.

		const auto result = mSwapChain->ResizeBuffers(
			swapChainDescription.BufferCount,
			clientSize.first,
			clientSize.second,
			swapChainDescription.BufferDesc.Format,
			swapChainDescription.Flags);

		if (FAILED(result))
		{
			throw DirectXRuntimeError(result, "IDXGISwapChain::ResizeBuffers() failed!");
		}
	}

	void RenderTarget::SetFullscreenState(bool fullscreen)
	{
		if (fullscreen)
		{
			const auto result = mSwapChain->SetFullscreenState(TRUE, *mRenderSystem->GetOutput());

			if (FAILED(result))
			{
				throw DirectXRuntimeError(result, "IDXGISwapChain::SetFullscreenState() failed!");
			}

			mFullscreen = true;
		}
		else
		{
			mSwapChain->SetFullscreenState(FALSE, nullptr);
			mFullscreen = false;
		}

		mLostFullscreen = false;			
	}

	void RenderTarget::CreateSwapChainDepencies()
	{
		D3D11::Texture2D backBuffer;
		auto result = mSwapChain->GetBuffer(0, backBuffer.GetIID(), reinterpret_cast<void**>(&backBuffer));

		if (FAILED(result))
		{
			throw DirectXRuntimeError(result, "IDXGISwapChain::GetBuffer() failed!");
		}

		D3D11::RenderTargetView renderTargetView;
		result = mDevice->CreateRenderTargetView(*backBuffer, nullptr, &renderTargetView);

		if (FAILED(result))
		{
			throw DirectXRuntimeError(result, "ID3D11Device::CreateRenderTargetView() failed!");
		}

		DXGI_SWAP_CHAIN_DESC swapChainDescription;
		mSwapChain->GetDesc(&swapChainDescription);

		// Die Depthstencilbuffergrösse der Backbuffergrösse anpassen.

		D3D11_TEXTURE2D_DESC depthStencilBufferDescription;
		depthStencilBufferDescription.Width = swapChainDescription.BufferDesc.Width;
		depthStencilBufferDescription.Height = swapChainDescription.BufferDesc.Height;
		depthStencilBufferDescription.MipLevels = 1;
		depthStencilBufferDescription.ArraySize = 1;
		depthStencilBufferDescription.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
		depthStencilBufferDescription.SampleDesc = swapChainDescription.SampleDesc;
		depthStencilBufferDescription.Usage = D3D11_USAGE_DEFAULT;
		depthStencilBufferDescription.BindFlags = D3D11_BIND_DEPTH_STENCIL;
		depthStencilBufferDescription.CPUAccessFlags = 0;
		depthStencilBufferDescription.MiscFlags = 0;
    
		D3D11::Texture2D depthStencilBuffer;
		result = mDevice->CreateTexture2D(&depthStencilBufferDescription, nullptr, &depthStencilBuffer);

		if (FAILED(result))
		{
			throw DirectXRuntimeError(result, "ID3D11Device::CreateTexture2D() failed!");
		}

		D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDescription;
		depthStencilViewDescription.Format = depthStencilBufferDescription.Format;
		depthStencilViewDescription.Flags = 0;
		depthStencilViewDescription.Texture2D.MipSlice = 0;
		depthStencilViewDescription.ViewDimension =  D3D11_DSV_DIMENSION_TEXTURE2D;

		// Eine angepasste Textur verwenden solle Multisampling aktiviert sein.

		if (swapChainDescription.SampleDesc.Count > 1)
		{
			depthStencilViewDescription.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
		}

		D3D11::DepthStencilView depthStencilView;
		result = mDevice->CreateDepthStencilView(*depthStencilBuffer, &depthStencilViewDescription, &depthStencilView);

		if (FAILED(result))
		{
			throw DirectXRuntimeError(result, "ID3D11Device::CreateDepthStencilView() failed!");
		}

		// Den Viewport der Backbuffergrösse (Fensterklientgrösse) anpassen.

		mViewport.Width = static_cast<float>(swapChainDescription.BufferDesc.Width);
		mViewport.Height = static_cast<float>(swapChainDescription.BufferDesc.Height);
		mViewport.TopLeftY = 0.0f;
		mViewport.TopLeftX = 0.0f;
		mViewport.MinDepth = 0.0f;
		mViewport.MaxDepth = 1.0f;

		// Erst nachdem alles erfolgreich erstellt worden ist, die Ressourcen aktualisieren.

		mRenderTargetView = renderTargetView;
		mDepthStencilBuffer = depthStencilBuffer;
		mDepthStencilView = depthStencilView;

		mReady = true;
	}

	void RenderTarget::DestroySwapChainDepencies()
	{
		mImmediateContext->OMSetRenderTargets(0, nullptr, nullptr);
		mRenderTargetView.Release();
		mDepthStencilBuffer.Release();
		mDepthStencilView.Release();

		mReady = false;
	}
}

///////////////////////////////////////////////////////////////////////////////
// Application.hpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	class Application : protected Uncopyable
	{
	private:

		std::unique_ptr<RenderSystem> mRenderSystem;
		std::unique_ptr<RenderWindow> mRenderWindow;
		std::unique_ptr<RenderTarget> mRenderTarget;
		Keyboard mKeyboard;
		Gamepad mGamepad;

	public:

		Application();
		void Tick(double elapsedTime);

	private:

		void Resize();
		void Update(double time, double timeStep);
		void Render();
		void HandleUserInput();
		void ToggleMultisampling();
		void ToggleScreen();
		void SetupRenderTarget(unsigned int sampleCount, unsigned int sampleQuality, unsigned int fullscreenWidth, unsigned int fullscreenHeight, bool fullscreen);
	};
}

///////////////////////////////////////////////////////////////////////////////
// Application.cpp
///////////////////////////////////////////////////////////////////////////////

namespace SuperAwesomeGame
{
	Application::Application()
	{
		mRenderSystem.reset(new RenderSystem(DXGI_FORMAT_R8G8B8A8_UNORM));
			
		if (mRenderSystem->GetFeatureLevel() < D3D_FEATURE_LEVEL_10_0)
		{
			throw std::runtime_error("Shader model 4.0 not supported!");
		}

		// Das Renderfenster der Desktopumgebung anpassen.

		unsigned int screenWidth = static_cast<unsigned int>(GetSystemMetrics(SM_CXSCREEN));
		unsigned int screenHeight = static_cast<unsigned int>(GetSystemMetrics(SM_CYSCREEN));

		mRenderWindow.reset(new RenderWindow);
		mRenderWindow->SetCaption(L"Super Awesome Game");
		mRenderWindow->SetClientSize(screenWidth - 300, screenHeight - 300);
		mRenderWindow->CenterAtNearestWorkArea();
		mRenderWindow->Show(SW_SHOWNORMAL);
		mRenderWindow->SetToForeground();
						
		// Im Fenstermodus mit ausgeschaltetem Multisampling starten.

		SetupRenderTarget(1, 0, 0, 0, false);
	}

	void Application::Tick(double elapsedTime)
	{
		static auto timeStep = 1.0 / 60;
		static auto timeAccumulator = 0.0;
		static auto time = 0.0;

		if (mRenderTarget->UpdateState())
		{
			Resize();
		}

		// Nur fortfahren wenn das Renderfenster und der Renderausgabereich (Renderfenster) sichtbar sind.

		if (!mRenderWindow->IsMinimized() && !mRenderTarget->IsOccluded())
		{
			// Die Anwendung nur aktualisieren wenn das Renderfenster aktiv ist.

			if (mRenderWindow->IsActivated())
			{
				if (elapsedTime > 0.0)
				{
					timeAccumulator += elapsedTime;
				}

				// Die Anwendung unabhängig der Framerate aktualisieren.

				while (timeAccumulator >= timeStep)
				{
					Update(time, timeStep);
					time += timeStep;
					timeAccumulator -= timeStep;
				}
			}			
							
			Render();
		}
	}

	void Application::Resize()
	{
		// ...
	}

	void Application::Update(double /*time*/, double /*timeStep*/)
	{
		HandleUserInput();

		// ...
	}

	void Application::Render()
	{
		mRenderTarget->Enable();
		mRenderTarget->Clear(0.0f, 0.125f, 0.3f, 0.0f);

		// ...

		mRenderTarget->Present(1, 0);
	}

	void Application::HandleUserInput()
	{
		mKeyboard.Update(mRenderWindow->GetKeyboardState());
		mGamepad.Update();

		if (mKeyboard.IsKeyDown(VK_ESCAPE))
		{
			mRenderWindow->Close();
		}
						
		// Multisampling ein/ausschalten.

		if (mKeyboard.IsAltDown() && mKeyboard.IsKeyToggled(VK_SHIFT))
		{
			ToggleMultisampling();
		}

		// Den Vollbildmodus ein/ausschalten.

		if (mKeyboard.IsAltDown() && mKeyboard.IsKeyToggled(VK_RETURN))
		{
			ToggleScreen();
		}
	}

	void Application::ToggleMultisampling()
	{
		unsigned int sampleCount = 0;
		unsigned int sampleQuality = 0;

		// Den bestmöglichen Multisamplemodus setzen.

		if (!mRenderTarget->IsMultisampled())
		{
			const auto index = mRenderSystem->GetMultisampleListSize() - 1;
			sampleCount = mRenderSystem->GetMultisampleList()[index].first;
			sampleQuality = mRenderSystem->GetMultisampleList()[index].second;
		}

		// Den bestehenden Displaymodus beibehalten.

		if (mRenderTarget->IsFullscreen())
		{		
			const auto displayMode = mRenderTarget->GetDisplayMode();
			SetupRenderTarget(sampleCount, sampleQuality, displayMode.first, displayMode.second, true);
		}
		else
		{
			SetupRenderTarget(sampleCount, sampleQuality, 0, 0, false);
		}
	}

	void Application::ToggleScreen()
	{
		// Den bestehenden Multisamplemodus beibehalten.

		const auto multisampleMode = mRenderTarget->GetMultisampleMode();

		if (mRenderTarget->IsFullscreen())
		{
			SetupRenderTarget(multisampleMode.first, multisampleMode.second, 0, 0, false);
		}
		else
		{
			// Den bestmöglichen Vollbildmodus setzen.

			const auto index = mRenderSystem->GetDisplayModeListSize() - 1;
			const auto width = mRenderSystem->GetDisplayModeList()[index].first;
			const auto height = mRenderSystem->GetDisplayModeList()[index].second;

			SetupRenderTarget(multisampleMode.first, multisampleMode.second, width, height, true);
		}
	}

	void Application::SetupRenderTarget(unsigned int sampleCount, unsigned int sampleQuality, unsigned int fullscreenWidth, unsigned int fullscreenHeight, bool fullscreen)
	{
		// Prüfen ob ein bestehendes Rendertarget neu erstellt werden muss.

		if (mRenderTarget.get())
		{
			const auto multisampleMode = mRenderTarget->GetMultisampleMode();
			const auto isFullscreen = mRenderTarget->IsFullscreen();

			// Sollte die Multisamplekonfiguration neu gesetzt werden müssen, so muss auch
			// das Rendertarget neu erstellt werden.

			if (multisampleMode.first != sampleCount || multisampleMode.second != sampleQuality)
			{					
				mRenderTarget.reset(nullptr);
				
				// Eine kurze Pause einlegen wenn der Fenstermodus nur temporär gesetzt werden
				// soll da ansonsten die Desktopauflösung manipuliert und der Vollbildauflösung
				// angepasst werden könnte.

				if (isFullscreen && fullscreen)
				{
					Sleep(250);
				}
			}
		}

		// Ein neues Rendertarget erstellen.
						
		if (mRenderTarget.get() == nullptr)
		{
			mRenderTarget.reset(new RenderTarget(mRenderSystem.get(), mRenderWindow.get(), sampleCount, sampleQuality));
		}

		// Den gewünschten Displaymodus setzen.

		if (fullscreen)
		{
			mRenderTarget->SetFullscreenMode(fullscreenWidth, fullscreenHeight);
		}
		else
		{
			mRenderTarget->SetWindowedMode();
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// WinMain.cpp
///////////////////////////////////////////////////////////////////////////////

int __stdcall WinMain(HINSTANCE /*instanceHandle*/, HINSTANCE /*previousInstanceHandle*/, char* /*commandLine*/, int /*showCommand*/)
{
	MSG message;
	ZeroMemory(&message, sizeof(message));

	LARGE_INTEGER frequency;
	LARGE_INTEGER previousTick;
	LARGE_INTEGER currentTick;

	QueryPerformanceFrequency(&frequency);
	QueryPerformanceCounter(&previousTick);

	try
	{
		SuperAwesomeGame::Application application;

		while (message.message != WM_QUIT)
		{
			// Alle auf dem Stapel liegenden Nachrichten verarbeiten und entfernen.

			if (PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE))
			{
				TranslateMessage(&message);
				DispatchMessageW(&message);
			}
			else
			{
				// Die versrichen Zeit seit dem letzten Durchgang berechnen und der
				// Anwendung übergeben.

				QueryPerformanceCounter(&currentTick);

				application.Tick(static_cast<double>(currentTick.QuadPart - previousTick.QuadPart) / frequency.QuadPart);
				previousTick = currentTick;
			}
		}
	}
	catch (const SuperAwesomeGame::DirectXRuntimeError& directXRuntimeError)
	{
		// Eine leserliche DirectX Fehlermeldung formatieren.

		std::stringstream message;
		message 
			<< DXGetErrorStringA(directXRuntimeError.GetError())
			<< L"\n\n"
			<< directXRuntimeError.what();

		MessageBoxA(
			nullptr, 
			message.str().c_str(), 
			"Unhandled DirectX Runtime Error", 
			MB_ICONERROR | 
			MB_SETFOREGROUND);
	}
	catch (const std::exception& unhandledException)
	{
		MessageBoxA(
			nullptr,
			unhandledException.what(), 
			"Unhandled Exception", 
			MB_ICONERROR | 
			MB_SETFOREGROUND);
	}

	return static_cast<int>(message.wParam);
}
DXGI.hpp
Damit ich nun z.B. std::numeric_limits<>::max verwenden kann, muss ich NOMINMAX definieren sobald die windows.h eingebunden wird.
Ich binde nun also dxgi.h ein. Die aber bindet intern auch die windows.h ein. Muss ich jetzt also auch hier NOMINMAX definieren obwohl ich ja nur dxgi.h und nicht windows.h verwenden will?

Exception.hpp
Um einen DirectX Fehlercode zu speichern, verwende ich einen HRESULT. Mehr wäre eigentlich nicht nötig, also habe ich auch nur winnt.h eingebunden. Danach aber wurden wieder Kompilerfehler angezeigt. Muss ich hier die komplette windows.h einbinden obwohl ich ja nur den HRESULT Typen brauche?

RenderWindowClass.hpp
Auch hier bräuchte ich eigentlich nur die HINSTANCE und WNDPROC Typen. Die windows.h würde ich also nur in RenderWindowClass.cpp einbinden.
Nun aber kann RenderWindow.hpp nicht wissen, ob RenderWindowClass.hpp windows.h einbindet oder nicht. Denn alles was gebraucht wird ist der RenderWindowClass Typ. Bedeutet, windows.h muss auch hier eingebunden werden.

Nach allmählicher Verzweiflung habe ich einfach in allen Headerdateien die windows.h eingebunden und hat es funktioniert. Habe ich da was falsch verstanden was in eine Headerdatei gehört und was nicht?

Besten Dank schon mal im Voraus.
Grüsse
RazorX
Establishment
Beiträge: 156
Registriert: 23.12.2010, 14:13
Kontaktdaten:

Re: Was gehört in eine Headerdatei?

Beitrag von RazorX »

Moin,

ich habe mir jetzt nicht alles intensiv angeschaut, aber ich versuche mal nach einem groben Überblick ein paar Tipps zu geben.
  • Natürlich musst du die Headerdateien überall einbinden, wo du sie brauchst. Das ist nunmal in C++ so, dass du nur das ansprichst was du in der Datei auch explizit benötigst (ähnlich zu Java's import).
  • Deine Headerdateien musst du mit einem Include-Guard versehen, heutzutage #pragma once, wobei der glaub nicht von allen Compilern unterstützt wird. MS VC++ unterstützt den, ansonsten eine Kombination aus #ifndef #define #endif, einfach mal googeln. (Include-Guards brauchst du um nach mehrmaligen Einbinden von der selben Datei keine doppelten Deklarationen zu erhalten.)
  • Microsoft empfielt in der MSDN stets windows.h zu includieren anstelle einer einzelnen Headerdateien, ich denke da werden viele Querverweise drin stecken wodurch du Fehler bekommen wirst. Es schadet ja letztlich nicht.
  • Das mit NOMINMAX ist leider so. Präprozessordirektiven gehen nunmal leider vor und es ist gerade in dem Fall mit std::min oder dem das du nanntest sehr ärgerlich. Letztlich ist es aber auch wieder nur eine kleine Anweisung, die du aber auch vlt zentral in einer Header ablegen könntest die du bevor du windows.h includierst ebenfalls includierst.
  • Was mir auch noch auffällt ist, dass du in der cpp keine header includierst und du wohl Definitionen und Deklarationen in unterschiedliche Dateinamen ablegst. Zugegeben ich benutze selbst immer .h Dateien, da es sich manchmal auch nicht vermeiden lässt auf C-speziefische Bibliotheken zuzugreifen, also weiß jetzt nicht ob die .hpp beim Compiler insbesondere in Bezug auf Namensauflösung ein bisschen schwarze Magie beinhaltet.
  • Zu dem ob die cpp auch die Header aus der hpp kennt: Wenn du eben die hpp in die cpp einbindest, bindest du automatisch alle daran hängenden Dateien ebenfalls ein, zumal du brauchst eigentlich eh die Klassendeklaration um die Implementierung zu schreiben.
Hoffe das konnte erstmal helfen. Wenn ich jetzt bezüglich der .hpp wirklich etwas grobes übersehen habe, gebt mir mal bitte ne Rückmeldung.

MfG
Sven

Edit:
Wenn du also zu "faul" bist das NOMINMAX immer zu definieren kannst du sowas anlegen:

Code: Alles auswählen

// nominmax.h (/hpp)
#pragma once

#ifndef NOMINMAX
#define NOMINMAX
#endif

// foo.h
#pragma once

#include "nominmax.h"
#include <windows.h>
Edit2: Wobei mir eine noch bösere Art in den Sinn kommt:

Code: Alles auswählen

// stdminmax.h (/hpp)
#pragma once

#include <limits>

#ifndef NOMINMAX
#define NOMINMAX
#endif

#ifdef min
#undef min
#endif

#ifdef max
#undef max
#endif

Sinan
Beiträge: 4
Registriert: 10.12.2011, 02:54
Echter Name: Sinan Fankhauser

Re: Was gehört in eine Headerdatei?

Beitrag von Sinan »

Morgen, ahh, habe jetzt pragma once verwendet und es funktioniert :oops:
Eine Frage hätte ich noch zu folgendem Code:

RenderWindow.hpp

Code: Alles auswählen

#pragma once

#include <utility>
#include "Manifest.hpp"
#include "RenderWindowClass.hpp"
#include "KeyboardState.hpp"

namespace SuperAwesomeGame
{
	class RenderWindow : protected RenderWindowClass
	{		
	private:

		bool mActivated;
		bool mBorderless;
		HWND mWindowHandle;
		KeyboardState mKeyboardState;

	public:

		RenderWindow();
		~RenderWindow();
		bool IsMinimized() const;
		bool IsMaximized() const;
		bool IsActivated() const;
		bool IsBorderless() const;
		void SetCaption(const wchar_t* caption) const;
		void SetToForeground() const;
		const HINSTANCE GetInstanceHandle() const;
		const HWND GetWindowHandle() const;
		KeyboardState& GetKeyboardState();
		std::pair<unsigned int, unsigned int> GetClientSize() const;
		void SetClientSize(unsigned int width, unsigned int height) const;
		void SetBorderlessState(bool borderless);
		void Show(int showCommand) const;
		void Close() const;
		void CenterAtNearestWorkArea() const;

	private:

		bool MessageHandler(unsigned int message, WPARAM wordParameter, LPARAM longParameter);
		static LRESULT __stdcall WindowProcedure(HWND windowHandle, unsigned int message, WPARAM wordParameter, LPARAM longParameter);
	};
}
RenderTarget.hpp

Code: Alles auswählen

#pragma once

#include <utility>
#include "Uncopyable.hpp"
#include "RenderSystem.hpp"
#include "RenderWindow.hpp"

namespace SuperAwesomeGame
{
	class RenderTarget : protected Uncopyable
	{
	private:

		bool mOccluded;
		bool mReady;
		bool mFullscreen;
		bool mLostFullscreen;
		bool mWasMaximized;
		unsigned int mClientWidthToRestore;
		unsigned int mClientHeightToRestore;
		RenderSystem* mRenderSystem;
		RenderWindow* mRenderWindow;
		DXGI::SwapChain mSwapChain;
		D3D11::Device mDevice;
		D3D11::DeviceContext mImmediateContext;
		D3D11::RenderTargetView mRenderTargetView;
		D3D11::Texture2D mDepthStencilBuffer;
		D3D11::DepthStencilView mDepthStencilView;
		D3D11_VIEWPORT mViewport;

	public:

		RenderTarget(RenderSystem* renderSystem, RenderWindow* renderWindow, unsigned int sampleCount, unsigned int sampleQuality);
		~RenderTarget();
		bool IsOccluded() const;
		bool IsReady() const;
		bool IsFullscreen() const;
		bool IsMinimizedWhileFullscreen() const;
		bool IsMultisampled() const;
		std::pair<unsigned int, unsigned int> GetMultisampleMode() const;
		std::pair<unsigned int, unsigned int> GetDisplayMode() const;
		bool UpdateState();
		bool SetWindowedMode();
		bool SetFullscreenMode(unsigned int width, unsigned int height);
		void Enable();
		void Clear(float red, float green, float blue, float alpha) const;
		void Present(unsigned int presentInterval, unsigned int presentFlags);

	private:
				
		bool CheckForPossibleClientSizeChange() const;
		bool ResizeTarget(unsigned int width, unsigned int height) const;
		void AdjustBuffers() const;
		void SetFullscreenState(bool fullscreen);
		void CreateSwapChainDepencies();
		void DestroySwapChainDepencies();
	};
}
Also mein RenderWindow verwendet ein std::pair Objekt aus dem utility Header um die Klientgrösse zurückzugeben.
Das RenderTarget verwendet ebenfalls std::pair für den Displaymodus. Muss ich nun utility in RenderTarget.hpp erneut einbinden obschon diese schon in RenderWindow.hpp eingebunden worden ist?
Denn eigentlich braucht RenderTarget.hpp ja nur die Klassendeklaration von RenderWindow. Rein theoretisch würde ja eine Forwärtsdeklaration der RednerWindow Klasse in RenderTarget.hpp genügen, oder?

RenderTarget.hpp

Code: Alles auswählen

#pragma once

#include <utility>
#include "Uncopyable.hpp"
#include "DXGI.hpp"
#include "D3D11.hpp"

namespace SuperAwesomeGame
{
	class RenderSystem; // Forwärtsdeklaration.
	class RenderWindow; // Forwärtsdeklaration.

	class RenderTarget : protected Uncopyable
	{
	private:

		bool mOccluded;
		bool mReady;
		bool mFullscreen;
		bool mLostFullscreen;
		bool mWasMaximized;
		unsigned int mClientWidthToRestore;
		unsigned int mClientHeightToRestore;
		RenderSystem* mRenderSystem;
		RenderWindow* mRenderWindow;
		DXGI::SwapChain mSwapChain;
		D3D11::Device mDevice;
		D3D11::DeviceContext mImmediateContext;
		D3D11::RenderTargetView mRenderTargetView;
		D3D11::Texture2D mDepthStencilBuffer;
		D3D11::DepthStencilView mDepthStencilView;
		D3D11_VIEWPORT mViewport;

	public:

		RenderTarget(RenderSystem* renderSystem, RenderWindow* renderWindow, unsigned int sampleCount, unsigned int sampleQuality);
		~RenderTarget();
		bool IsOccluded() const;
		bool IsReady() const;
		bool IsFullscreen() const;
		bool IsMinimizedWhileFullscreen() const;
		bool IsMultisampled() const;
		std::pair<unsigned int, unsigned int> GetMultisampleMode() const;
		std::pair<unsigned int, unsigned int> GetDisplayMode() const;
		bool UpdateState();
		bool SetWindowedMode();
		bool SetFullscreenMode(unsigned int width, unsigned int height);
		void Enable();
		void Clear(float red, float green, float blue, float alpha) const;
		void Present(unsigned int presentInterval, unsigned int presentFlags);

	private:
				
		bool CheckForPossibleClientSizeChange() const;
		bool ResizeTarget(unsigned int width, unsigned int height) const;
		void AdjustBuffers() const;
		void SetFullscreenState(bool fullscreen);
		void CreateSwapChainDepencies();
		void DestroySwapChainDepencies();
	};
}
Danach müsste ich einfach in RenderTarget.cpp die beiden Header RenderSystem.hpp und RenderWindow.hpp einbinden.
Benutzeravatar
Jonathan
Establishment
Beiträge: 2749
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Was gehört in eine Headerdatei?

Beitrag von Jonathan »

Also mein RenderWindow verwendet ein std::pair Objekt aus dem utility Header um die Klientgrösse zurückzugeben.
Das RenderTarget verwendet ebenfalls std::pair für den Displaymodus. Muss ich nun utility in RenderTarget.hpp erneut einbinden obschon diese schon in RenderWindow.hpp eingebunden worden ist?
Denn eigentlich braucht RenderTarget.hpp ja nur die Klassendeklaration von RenderWindow. Rein theoretisch würde ja eine Forwärtsdeklaration der RednerWindow Klasse in RenderTarget.hpp genügen, oder?
Nein, wenn RenderTarget RenderWindow inkludiert, steht da ja schon alles drin.
Nochmla einbinden geht aber trotzdem, dann wird das zweite mal halt ignoriert (durch die Include-Guards, bzw. pragma once).
Du müsstest utility nichteinmal in RenderTarget includieren, falls du in jeder cpp-Datei, die RenderTarget includiert vor eben dieser 'Includierung' die utility direkt oder indirekt (über andere Header) includierst.
Denn Header werden die kompiliert sondern immer nur in Source Dateien eingebunden, die dann als Gesamtheit (mit allen includierten Headern) übersetzt wird. Was dann letztendlich in welcher Datei stand ist egal, Hauptsache nach dem Präprozessorschritt ist alles in der RIchtigen Reihenfolge drin.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
RazorX
Establishment
Beiträge: 156
Registriert: 23.12.2010, 14:13
Kontaktdaten:

Re: Was gehört in eine Headerdatei?

Beitrag von RazorX »

Moin,

also prinzipiell genügt es natürlich die eine zu includieren die dann wiederrum eine andere includiert. Solange das aus deinen eigenen Dateien kommt ist das kein Problem und ist natürlich auch konform. Wo du jedoch aufpassen solltest wäre sowas wie das oben erwähnte: du includierst dxgi.h und das includiert windows.h. Sich bei Fremd-Frameworks auf include-Beziehungen zu verlassen kann beim nächsten Update u.U. zu bösen Fehlern führen.

Nun vielleicht ein kleiner Anreitz, den du vorallem brauchst wenn du templates in einer dll benutzt. Da u.U. die Typgrößen/Klassenimplementierungen von kompilierter Version und verwendete Entwicklungsumgebung abweichen können, musst du intern genutzte Templates kapseln. Dies eignet sich aber auch schön um Variablen nach außen hin zu verbergen (dann kannst du dir die Forwarddeklaration sparen).

Code: Alles auswählen

// hpp
#pragma once

#include <utility>
#include "Manifest.hpp"
#include "KeyboardState.hpp"

namespace SuperAwesomeGame
{
        class RenderTarget : protected Uncopyable
        {
        private:
                struct RenderTargetData* mData;
        };
};

// cpp
#include "RenderTarget.hpp"
#include "RenderWindowClass.hpp"
#include "RenderSystem.hpp"

namespace SuperAwesomeGame
{
       struct RenderTargetData
       {
                bool mOccluded;
                bool mReady;
                bool mFullscreen;
                bool mLostFullscreen;
                bool mWasMaximized;
                unsigned int mClientWidthToRestore;
                unsigned int mClientHeightToRestore;
                RenderSystem* mRenderSystem;
                RenderWindow* mRenderWindow;
                DXGI::SwapChain mSwapChain;
                D3D11::Device mDevice;
                D3D11::DeviceContext mImmediateContext;
                D3D11::RenderTargetView mRenderTargetView;
                D3D11::Texture2D mDepthStencilBuffer;
                D3D11::DepthStencilView mDepthStencilView;
                D3D11_VIEWPORT mViewport;
         };
};
MfG
Sinan
Beiträge: 4
Registriert: 10.12.2011, 02:54
Echter Name: Sinan Fankhauser

Re: Was gehört in eine Headerdatei?

Beitrag von Sinan »

Hey ich Dank euch.
Die Idee mit den Daten in einer eigener Struktur ist super. Habe mich noch ein wenig im Netz umgeschaut für weitere Informationen und mit auf das Pimpl-Idom gestossen.
Nun habe ich eine Gamepad.hpp Headerdatei ganz ohne Verweise auf die xinput.h :) Cool.

Code: Alles auswählen

#pragma once

#include <memory>
#include "Uncopyable.hpp"

namespace SuperAwesomeGame
{
	enum GamepadButton
	{
		GamepadDPadUp,
		GamepadDPadDown,
		GamepadDPadLeft,
		GamepadDPadRight,
		GamepadStart,
		GamepadBack,
		GamepadLeftThumb,
		GamepadRightThumb,
		GamepadLeftShoulder,
		GamepadRightShoulder,
		GamepadA,
		GamepadB,
		GamepadX,
		GamepadY	
	};

	class Gamepad : protected Uncopyable
	{
	private:

		struct Private;
		std::unique_ptr<Private> mPrivate;

	public:

		Gamepad();
		~Gamepad();
		bool IsConnected() const;
		bool HasStateChanged() const;
		bool IsButtonDown(GamepadButton gamepadButton) const;
		bool IsButtonToggled(GamepadButton gamepadButton) const;
		float GetLeftTrigger() const;
		float GetRightTrigger() const;
		float GetLeftThumbX() const;
		float GetLeftThumbY() const;
		float GetRightThumbX() const;
		float GetRightThumbY() const;
		void SetVibration(unsigned short leftMotorSpeed, unsigned short rightMotorSpeed) const;
		void Update() const; // Konstant da nun nichts mehr direkt verändert wird.
	};
}
Intern in Gamepad.cpp konvertiere ich einfach die Daten von GamepadButton in einen XINPUT_GAMEPAD_XXX Wert. Auch habe ich private Methoden in die Pimpl Struktur verschoben. Nun habe ich eine elegante Schnittstelle :)
Nun hätte ich noch eine hypothetische Frage zu Templates und DLLs. std::unique_ptr<> verwende ich um die Pimpl Struktur zu erstellen und freizugeben da die Möglichkeit besteht, dass im Konstruktor der jeweiligen Klasse eine Ausnhame auftreten könnte und somit kein Destruktor mehr aufgerufen wird.
Nun hätte ich also z.B. eine Input.dll mit meiner Gamepad.hpp Datei mit Compiler A erstellt, und würde diese in einem Programm, welches Compiler B verwendet, der womöglich eine andere Implementation von std::unique_ptr<> nutzt, einbinden. Würde ich da in ein Problem laufen?
Müsste ich da in Gamepad.hpp ausser auf Deklarationen ganz auf Template-Typen verzichten?

Also einfach:
struct Private;
Private* mPrivate;

Und danach im Konstruktor einen try/catch Block einrichten?
Besten Dank
RazorX
Establishment
Beiträge: 156
Registriert: 23.12.2010, 14:13
Kontaktdaten:

Re: Was gehört in eine Headerdatei?

Beitrag von RazorX »

Also es wäre natürlich das einfachste selber die Datenstruktur zu erstellen und freizugeben. Nichtsdestotrotz ist es möglich eine Templateinstantiierung zu exportieren (es wäre auch für alle anderen Templates machbar, ist nur verdammt unschön).

Code: Alles auswählen

// api.hpp
#pragma once

#ifdef _DLL
   #define API __declspec(dllexport)
   #define TEMPLATE
#else
   #define API __declspec(dllimport)
   #define TEMPLATE extern
#endif

Code: Alles auswählen

// foo.hpp
#pragma once

#include "api.hpp"

// Edit: Die Deklaration ist blödsinn, durch das Schlüsselwort struct bei der Benutzung ist es eh schon als nicht definierter Datentyp identifiziert.
//struct Private;

TEMPLATE template class std::unique_ptr<struct Private>;

class API Foo
{
    private:
         std::unique_ptr<struct Private> mPrivate;
};
Habs jetzt nicht ausprobiert, könnte mir aber vorstellen, dass der wegen undefinierten Typen meckert. Dieses Vorgehen kannst du aber im Prinzip auf jede Template-Klasse übertragen also auch z.B.:

Code: Alles auswählen

// bar.hpp
#pragma once

#include "api.hpp"

TEMPLATE template class std::list<int>;

class API Bar
{
private:
    std::list<int> mList;
};
Wenn man diesen Export nicht mit aufnimmt, kommt es zu Compilerwarnungen, die eben darauf aufmerksam machen, dass es beim einbinden zu Versionskonflikten kommen kann.

Schau dir auch diesen Artikel mal an: http://support.microsoft.com/kb/168958/en-us

MfG
Sinan
Beiträge: 4
Registriert: 10.12.2011, 02:54
Echter Name: Sinan Fankhauser

Re: Was gehört in eine Headerdatei?

Beitrag von Sinan »

Interessanter Link, Danke.
Nun ich nehme auch an, das es gar keine gute Idee ist, eine Ausnahme aus einer Dll heraus zu werfen. Also am einfachsten ist es wohl, DLL intern Ausnahmen zu verwenden, diese am Rand der DLL abzufangen und als Fehlercode zurückzugeben.
Wie ich sehe, gibt es für mich noch viel zu Erforschen in der Welt von C++.
Ich dank euch allen für die tollen Tips.

MfG
RazorX
Establishment
Beiträge: 156
Registriert: 23.12.2010, 14:13
Kontaktdaten:

Re: Was gehört in eine Headerdatei?

Beitrag von RazorX »

Nunja, Exceptions aus einer DLL herauszuschmeißen ist erstmal nicht schlimm, es ist nur eine Frage der Dokumentation. Wenn man sich an Java/C# orientiert wo man bei der Methodenbeschreibung direkt ersehen kann welche Exceptions auftreten können, dann ist es legitim. Wenn jedoch die Exceptions relativ willkürlich auftreten und nirgends niedergeschrieben werden, ist es verdammt gefährlich.

MfG
Benutzeravatar
Krishty
Establishment
Beiträge: 8366
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Was gehört in eine Headerdatei?

Beitrag von Krishty »

RazorX hat geschrieben:Nunja, Exceptions aus einer DLL herauszuschmeißen ist erstmal nicht schlimm, es ist nur eine Frage der Dokumentation.
Doch und nö. Ausnahmen in C++ über DLL-Grenzen zu werfen hat nur in absoluten Ausnahmefällen vorhersagbare Wirkung und ist eines der gefährlichsten Dinge, die man tun kann. Ich empfehle dringend, es zu lassen. Sinan hat da schon völlig recht.

(Erklärung)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
RazorX
Establishment
Beiträge: 156
Registriert: 23.12.2010, 14:13
Kontaktdaten:

Re: Was gehört in eine Headerdatei?

Beitrag von RazorX »

Gute Erklärung. Ja habe um ehrlich zu sein das noch nie ausprobiert, aber du hast natürlich recht. Die fehlenden Typinformationen und vorallem das was ich ja weiter oben schon erwähnt habe mit verschiedenen Typimplementierungen machen es unmöglich.

MfG
RazorX
Establishment
Beiträge: 156
Registriert: 23.12.2010, 14:13
Kontaktdaten:

Re: Was gehört in eine Headerdatei?

Beitrag von RazorX »

Wobei mir gerade die Idee kommt, das man doch den Exceptiontyp evtl. exportieren könnte?

Code: Alles auswählen

// foo.hpp nutzt bar.dll
#pragma once

#include "bar.hpp"

void main()
{
    try{
         Bar b;
         b.do();
     } catch(std::wstring e)
     {...}
}

Code: Alles auswählen

// bar.hpp
#pragma once

#include <string>

#ifdef _DLL
   #define API __declspec(dllexport)
   #define EXTERN
#else
   #define API __declspec(dllimport)
   #define EXTERN extern
#endif

EXTERN template class API std::wstring;

class API Bar
{
public:
    void do()
    { throw std::wstring(L"Test"); }
};
Bin gerade nicht zu Hause und kann es nicht testen, von daher bitte nicht hauen wenn es gerade völliger Blödsinn ist, aber das eigentlich Problem ist ja die fehlende Typinformation. Wenn man nun jedoch den Typ mit exportiert, sollte das doch aber umgangen sein?
Antworten