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(¤tTick);
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);
}
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