691 lines
23 KiB
C++
691 lines
23 KiB
C++
#include "DXCore.h"
|
|
#include "Input.h"
|
|
|
|
#include <WindowsX.h>
|
|
#include <sstream>
|
|
|
|
// Define the static instance variable so our OS-level
|
|
// message handling function below can talk to our object
|
|
DXCore* DXCore::DXCoreInstance = 0;
|
|
|
|
// --------------------------------------------------------
|
|
// The global callback function for handling windows OS-level messages.
|
|
//
|
|
// This needs to be a global function (not part of a class), but we want
|
|
// to forward the parameters to our class to properly handle them.
|
|
// --------------------------------------------------------
|
|
LRESULT DXCore::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return DXCoreInstance->ProcessMessage(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// Constructor - Set up fields and timer
|
|
//
|
|
// hInstance - The application's OS-level handle (unique ID)
|
|
// titleBarText - Text for the window's title bar
|
|
// windowWidth - Width of the window's client (internal) area
|
|
// windowHeight - Height of the window's client (internal) area
|
|
// debugTitleBarStats - Show debug stats in the title bar, like FPS?
|
|
// --------------------------------------------------------
|
|
DXCore::DXCore(
|
|
HINSTANCE hInstance, // The application's handle
|
|
const char* titleBarText, // Text for the window's title bar
|
|
unsigned int windowWidth, // Width of the window's client area
|
|
unsigned int windowHeight, // Height of the window's client area
|
|
bool debugTitleBarStats) // Show extra stats (fps) in title bar?
|
|
{
|
|
// Save a static reference to this object.
|
|
// - Since the OS-level message function must be a non-member (global) function,
|
|
// it won't be able to directly interact with our DXCore object otherwise.
|
|
// - (Yes, a singleton might be a safer choice here).
|
|
DXCoreInstance = this;
|
|
|
|
// Save params
|
|
this->hInstance = hInstance;
|
|
this->titleBarText = titleBarText;
|
|
this->width = windowWidth;
|
|
this->height = windowHeight;
|
|
this->titleBarStats = debugTitleBarStats;
|
|
|
|
// Initialize fields
|
|
this->hasFocus = true;
|
|
|
|
this->fpsFrameCount = 0;
|
|
this->fpsTimeElapsed = 0.0f;
|
|
this->currentTime = 0;
|
|
this->deltaTime = 0;
|
|
this->startTime = 0;
|
|
this->totalTime = 0;
|
|
|
|
// Query performance counter for accurate timing information
|
|
__int64 perfFreq;
|
|
QueryPerformanceFrequency((LARGE_INTEGER*)&perfFreq);
|
|
perfCounterSeconds = 1.0 / (double)perfFreq;
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// Destructor - Clean up (release) all DirectX references
|
|
// --------------------------------------------------------
|
|
DXCore::~DXCore()
|
|
{
|
|
// Note: Since we're using smart pointers (ComPtr),
|
|
// we don't need to explicitly clean up those DirectX objects
|
|
// - If we weren't using smart pointers, we'd need
|
|
// to call Release() on each DirectX object created in DXCore
|
|
|
|
// Delete input manager singleton
|
|
delete& Input::GetInstance();
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// Created the actual window for our application
|
|
// --------------------------------------------------------
|
|
HRESULT DXCore::InitWindow()
|
|
{
|
|
// Start window creation by filling out the
|
|
// appropriate window class struct
|
|
WNDCLASS wndClass = {}; // Zero out the memory
|
|
wndClass.style = CS_HREDRAW | CS_VREDRAW; // Redraw on horizontal or vertical movement/adjustment
|
|
wndClass.lpfnWndProc = DXCore::WindowProc;
|
|
wndClass.cbClsExtra = 0;
|
|
wndClass.cbWndExtra = 0;
|
|
wndClass.hInstance = hInstance; // Our app's handle
|
|
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); // Default icon
|
|
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); // Default arrow cursor
|
|
wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
|
|
wndClass.lpszMenuName = NULL;
|
|
wndClass.lpszClassName = "Direct3DWindowClass";
|
|
|
|
// Attempt to register the window class we've defined
|
|
if (!RegisterClass(&wndClass))
|
|
{
|
|
// Get the most recent error
|
|
DWORD error = GetLastError();
|
|
|
|
// If the class exists, that's actually fine. Otherwise,
|
|
// we can't proceed with the next step.
|
|
if (error != ERROR_CLASS_ALREADY_EXISTS)
|
|
return HRESULT_FROM_WIN32(error);
|
|
}
|
|
|
|
// Adjust the width and height so the "client size" matches
|
|
// the width and height given (the inner-area of the window)
|
|
RECT clientRect;
|
|
SetRect(&clientRect, 0, 0, width, height);
|
|
AdjustWindowRect(
|
|
&clientRect,
|
|
WS_OVERLAPPEDWINDOW, // Has a title bar, border, min and max buttons, etc.
|
|
false); // No menu bar
|
|
|
|
// Center the window to the screen
|
|
RECT desktopRect;
|
|
GetClientRect(GetDesktopWindow(), &desktopRect);
|
|
int centeredX = (desktopRect.right / 2) - (clientRect.right / 2);
|
|
int centeredY = (desktopRect.bottom / 2) - (clientRect.bottom / 2);
|
|
|
|
// Actually ask Windows to create the window itself
|
|
// using our settings so far. This will return the
|
|
// handle of the window, which we'll keep around for later
|
|
hWnd = CreateWindow(
|
|
wndClass.lpszClassName,
|
|
titleBarText.c_str(),
|
|
WS_OVERLAPPEDWINDOW,
|
|
centeredX,
|
|
centeredY,
|
|
clientRect.right - clientRect.left, // Calculated width
|
|
clientRect.bottom - clientRect.top, // Calculated height
|
|
0, // No parent window
|
|
0, // No menu
|
|
hInstance, // The app's handle
|
|
0); // No other windows in our application
|
|
|
|
// Ensure the window was created properly
|
|
if (hWnd == NULL)
|
|
{
|
|
DWORD error = GetLastError();
|
|
return HRESULT_FROM_WIN32(error);
|
|
}
|
|
|
|
// The window exists but is not visible yet
|
|
// We need to tell Windows to show it, and how to show it
|
|
ShowWindow(hWnd, SW_SHOW);
|
|
|
|
// Initialize the input manager now that we definitely have a window
|
|
Input::GetInstance().Initialize(hWnd);
|
|
|
|
// Return an "everything is ok" HRESULT value
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------
|
|
// Initializes DirectX, which requires a window. This method
|
|
// also creates several DirectX objects we'll need to start
|
|
// drawing things to the screen.
|
|
// --------------------------------------------------------
|
|
HRESULT DXCore::InitDirectX()
|
|
{
|
|
// This will hold options for DirectX initialization
|
|
unsigned int deviceFlags = 0;
|
|
|
|
#if defined(DEBUG) || defined(_DEBUG)
|
|
// If we're in debug mode in visual studio, we also
|
|
// want to make a "Debug DirectX Device" to see some
|
|
// errors and warnings in Visual Studio's output window
|
|
// when things go wrong!
|
|
deviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
|
|
#endif
|
|
|
|
// Create a description of how our swap
|
|
// chain should work
|
|
DXGI_SWAP_CHAIN_DESC swapDesc = {};
|
|
swapDesc.BufferCount = 2;
|
|
swapDesc.BufferDesc.Width = width;
|
|
swapDesc.BufferDesc.Height = height;
|
|
swapDesc.BufferDesc.RefreshRate.Numerator = 60;
|
|
swapDesc.BufferDesc.RefreshRate.Denominator = 1;
|
|
swapDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
swapDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
|
|
swapDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
|
|
swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
swapDesc.Flags = 0;
|
|
swapDesc.OutputWindow = hWnd;
|
|
swapDesc.SampleDesc.Count = 1;
|
|
swapDesc.SampleDesc.Quality = 0;
|
|
swapDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
|
swapDesc.Windowed = true;
|
|
|
|
// Result variable for below function calls
|
|
HRESULT hr = S_OK;
|
|
|
|
// Attempt to initialize DirectX
|
|
hr = D3D11CreateDeviceAndSwapChain(
|
|
0, // Video adapter (physical GPU) to use, or null for default
|
|
D3D_DRIVER_TYPE_HARDWARE, // We want to use the hardware (GPU)
|
|
0, // Used when doing software rendering
|
|
deviceFlags, // Any special options
|
|
0, // Optional array of possible verisons we want as fallbacks
|
|
0, // The number of fallbacks in the above param
|
|
D3D11_SDK_VERSION, // Current version of the SDK
|
|
&swapDesc, // Address of swap chain options
|
|
swapChain.GetAddressOf(), // Pointer to our Swap Chain pointer
|
|
device.GetAddressOf(), // Pointer to our Device pointer
|
|
&dxFeatureLevel, // This will hold the actual feature level the app will use
|
|
context.GetAddressOf()); // Pointer to our Device Context pointer
|
|
if (FAILED(hr)) return hr;
|
|
|
|
// The above function created the back buffer render target
|
|
// for us, but we need a reference to it
|
|
ID3D11Texture2D* backBufferTexture = 0;
|
|
swapChain->GetBuffer(
|
|
0,
|
|
__uuidof(ID3D11Texture2D),
|
|
(void**)&backBufferTexture);
|
|
|
|
// Now that we have the texture, create a render target view
|
|
// for the back buffer so we can render into it. Then release
|
|
// our local reference to the texture, since we have the view.
|
|
if (backBufferTexture != 0)
|
|
{
|
|
device->CreateRenderTargetView(
|
|
backBufferTexture,
|
|
0,
|
|
backBufferRTV.GetAddressOf());
|
|
backBufferTexture->Release();
|
|
}
|
|
|
|
// Set up the description of the texture to use for the depth buffer
|
|
D3D11_TEXTURE2D_DESC depthStencilDesc = {};
|
|
depthStencilDesc.Width = width;
|
|
depthStencilDesc.Height = height;
|
|
depthStencilDesc.MipLevels = 1;
|
|
depthStencilDesc.ArraySize = 1;
|
|
depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
|
|
depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
|
|
depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
|
|
depthStencilDesc.CPUAccessFlags = 0;
|
|
depthStencilDesc.MiscFlags = 0;
|
|
depthStencilDesc.SampleDesc.Count = 1;
|
|
depthStencilDesc.SampleDesc.Quality = 0;
|
|
|
|
// Create the depth buffer and its view, then
|
|
// release our reference to the texture
|
|
ID3D11Texture2D* depthBufferTexture = 0;
|
|
device->CreateTexture2D(&depthStencilDesc, 0, &depthBufferTexture);
|
|
if (depthBufferTexture != 0)
|
|
{
|
|
device->CreateDepthStencilView(
|
|
depthBufferTexture,
|
|
0,
|
|
depthStencilView.GetAddressOf());
|
|
depthBufferTexture->Release();
|
|
}
|
|
|
|
// Bind the views to the pipeline, so rendering properly
|
|
// uses their underlying textures
|
|
context->OMSetRenderTargets(
|
|
1,
|
|
backBufferRTV.GetAddressOf(),
|
|
depthStencilView.Get());
|
|
|
|
// Lastly, set up a viewport so we render into
|
|
// to correct portion of the window
|
|
D3D11_VIEWPORT viewport = {};
|
|
viewport.TopLeftX = 0;
|
|
viewport.TopLeftY = 0;
|
|
viewport.Width = (float)width;
|
|
viewport.Height = (float)height;
|
|
viewport.MinDepth = 0.0f;
|
|
viewport.MaxDepth = 1.0f;
|
|
context->RSSetViewports(1, &viewport);
|
|
|
|
// Return the "everything is ok" HRESULT value
|
|
return S_OK;
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// When the window is resized, the underlying
|
|
// buffers (textures) must also be resized to match.
|
|
//
|
|
// If we don't do this, the window size and our rendering
|
|
// resolution won't match up. This can result in odd
|
|
// stretching/skewing.
|
|
// --------------------------------------------------------
|
|
void DXCore::OnResize()
|
|
{
|
|
// Release the buffers before resizing the swap chain
|
|
backBufferRTV.Reset();
|
|
depthStencilView.Reset();
|
|
|
|
// Resize the underlying swap chain buffers
|
|
swapChain->ResizeBuffers(
|
|
2,
|
|
width,
|
|
height,
|
|
DXGI_FORMAT_R8G8B8A8_UNORM,
|
|
0);
|
|
|
|
// Recreate the render target view for the back buffer
|
|
// texture, then release our local texture reference
|
|
ID3D11Texture2D* backBufferTexture = 0;
|
|
swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&backBufferTexture));
|
|
if (backBufferTexture != 0)
|
|
{
|
|
device->CreateRenderTargetView(
|
|
backBufferTexture,
|
|
0,
|
|
backBufferRTV.ReleaseAndGetAddressOf()); // ReleaseAndGetAddressOf() cleans up the old object before giving us the pointer
|
|
backBufferTexture->Release();
|
|
}
|
|
|
|
// Set up the description of the texture to use for the depth buffer
|
|
D3D11_TEXTURE2D_DESC depthStencilDesc;
|
|
depthStencilDesc.Width = width;
|
|
depthStencilDesc.Height = height;
|
|
depthStencilDesc.MipLevels = 1;
|
|
depthStencilDesc.ArraySize = 1;
|
|
depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
|
|
depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
|
|
depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
|
|
depthStencilDesc.CPUAccessFlags = 0;
|
|
depthStencilDesc.MiscFlags = 0;
|
|
depthStencilDesc.SampleDesc.Count = 1;
|
|
depthStencilDesc.SampleDesc.Quality = 0;
|
|
|
|
// Create the depth buffer and its view, then
|
|
// release our reference to the texture
|
|
ID3D11Texture2D* depthBufferTexture = 0;
|
|
device->CreateTexture2D(&depthStencilDesc, 0, &depthBufferTexture);
|
|
if (depthBufferTexture != 0)
|
|
{
|
|
device->CreateDepthStencilView(
|
|
depthBufferTexture,
|
|
0,
|
|
depthStencilView.ReleaseAndGetAddressOf()); // ReleaseAndGetAddressOf() cleans up the old object before giving us the pointer
|
|
depthBufferTexture->Release();
|
|
}
|
|
|
|
// Bind the views to the pipeline, so rendering properly
|
|
// uses their underlying textures
|
|
context->OMSetRenderTargets(
|
|
1,
|
|
backBufferRTV.GetAddressOf(), // This requires a pointer to a pointer (an array of pointers), so we get the address of the pointer
|
|
depthStencilView.Get());
|
|
|
|
// Lastly, set up a viewport so we render into
|
|
// to correct portion of the window
|
|
D3D11_VIEWPORT viewport = {};
|
|
viewport.TopLeftX = 0;
|
|
viewport.TopLeftY = 0;
|
|
viewport.Width = (float)width;
|
|
viewport.Height = (float)height;
|
|
viewport.MinDepth = 0.0f;
|
|
viewport.MaxDepth = 1.0f;
|
|
context->RSSetViewports(1, &viewport);
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------
|
|
// This is the main game loop, handling the following:
|
|
// - OS-level messages coming in from Windows itself
|
|
// - Calling update & draw back and forth, forever
|
|
// --------------------------------------------------------
|
|
HRESULT DXCore::Run()
|
|
{
|
|
// Grab the start time now that
|
|
// the game loop is running
|
|
__int64 now;
|
|
QueryPerformanceCounter((LARGE_INTEGER*)&now);
|
|
startTime = now;
|
|
currentTime = now;
|
|
previousTime = now;
|
|
|
|
// Give subclass a chance to initialize
|
|
Init();
|
|
|
|
// Our overall game and message loop
|
|
MSG msg = {};
|
|
while (msg.message != WM_QUIT)
|
|
{
|
|
// Determine if there is a message waiting
|
|
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
// Translate and dispatch the message
|
|
// to our custom WindowProc function
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
else
|
|
{
|
|
// Update timer and title bar (if necessary)
|
|
UpdateTimer();
|
|
if(titleBarStats)
|
|
UpdateTitleBarStats();
|
|
|
|
// Update the input manager
|
|
Input::GetInstance().Update();
|
|
|
|
// The game loop
|
|
Update(deltaTime, totalTime);
|
|
Draw(deltaTime, totalTime);
|
|
|
|
// Frame is over, notify the input manager
|
|
Input::GetInstance().EndOfFrame();
|
|
}
|
|
}
|
|
|
|
// We'll end up here once we get a WM_QUIT message,
|
|
// which usually comes from the user closing the window
|
|
return (HRESULT)msg.wParam;
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------
|
|
// Sends an OS-level window close message to our process, which
|
|
// will be handled by our message processing function
|
|
// --------------------------------------------------------
|
|
void DXCore::Quit()
|
|
{
|
|
PostMessage(this->hWnd, WM_CLOSE, NULL, NULL);
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------
|
|
// Uses high resolution time stamps to get very accurate
|
|
// timing information, and calculates useful time stats
|
|
// --------------------------------------------------------
|
|
void DXCore::UpdateTimer()
|
|
{
|
|
// Grab the current time
|
|
__int64 now;
|
|
QueryPerformanceCounter((LARGE_INTEGER*)&now);
|
|
currentTime = now;
|
|
|
|
// Calculate delta time and clamp to zero
|
|
// - Could go negative if CPU goes into power save mode
|
|
// or the process itself gets moved to another core
|
|
deltaTime = max((float)((currentTime - previousTime) * perfCounterSeconds), 0.0f);
|
|
|
|
// Calculate the total time from start to now
|
|
totalTime = (float)((currentTime - startTime) * perfCounterSeconds);
|
|
|
|
// Save current time for next frame
|
|
previousTime = currentTime;
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------
|
|
// Updates the window's title bar with several stats once
|
|
// per second, including:
|
|
// - The window's width & height
|
|
// - The current FPS and ms/frame
|
|
// - The version of DirectX actually being used (usually 11)
|
|
// --------------------------------------------------------
|
|
void DXCore::UpdateTitleBarStats()
|
|
{
|
|
fpsFrameCount++;
|
|
|
|
// Only calc FPS and update title bar once per second
|
|
float timeDiff = totalTime - fpsTimeElapsed;
|
|
if (timeDiff < 1.0f)
|
|
return;
|
|
|
|
// How long did each frame take? (Approx)
|
|
float mspf = 1000.0f / (float)fpsFrameCount;
|
|
|
|
// Quick and dirty title bar text (mostly for debugging)
|
|
std::ostringstream output;
|
|
output.precision(6);
|
|
output << titleBarText <<
|
|
" Width: " << width <<
|
|
" Height: " << height <<
|
|
" FPS: " << fpsFrameCount <<
|
|
" Frame Time: " << mspf << "ms";
|
|
|
|
// Append the version of DirectX the app is using
|
|
switch (dxFeatureLevel)
|
|
{
|
|
case D3D_FEATURE_LEVEL_11_1: output << " DX 11.1"; break;
|
|
case D3D_FEATURE_LEVEL_11_0: output << " DX 11.0"; break;
|
|
case D3D_FEATURE_LEVEL_10_1: output << " DX 10.1"; break;
|
|
case D3D_FEATURE_LEVEL_10_0: output << " DX 10.0"; break;
|
|
case D3D_FEATURE_LEVEL_9_3: output << " DX 9.3"; break;
|
|
case D3D_FEATURE_LEVEL_9_2: output << " DX 9.2"; break;
|
|
case D3D_FEATURE_LEVEL_9_1: output << " DX 9.1"; break;
|
|
default: output << " DX ???"; break;
|
|
}
|
|
|
|
// Actually update the title bar and reset fps data
|
|
SetWindowText(hWnd, output.str().c_str());
|
|
fpsFrameCount = 0;
|
|
fpsTimeElapsed += 1.0f;
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// Allocates a console window we can print to for debugging
|
|
//
|
|
// bufferLines - Number of lines in the overall console buffer
|
|
// bufferColumns - Numbers of columns in the overall console buffer
|
|
// windowLines - Number of lines visible at once in the window
|
|
// windowColumns - Number of columns visible at once in the window
|
|
// --------------------------------------------------------
|
|
void DXCore::CreateConsoleWindow(int bufferLines, int bufferColumns, int windowLines, int windowColumns)
|
|
{
|
|
// Our temp console info struct
|
|
CONSOLE_SCREEN_BUFFER_INFO coninfo;
|
|
|
|
// Get the console info and set the number of lines
|
|
AllocConsole();
|
|
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
|
|
coninfo.dwSize.Y = bufferLines;
|
|
coninfo.dwSize.X = bufferColumns;
|
|
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
|
|
|
|
SMALL_RECT rect;
|
|
rect.Left = 0;
|
|
rect.Top = 0;
|
|
rect.Right = windowColumns;
|
|
rect.Bottom = windowLines;
|
|
SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), TRUE, &rect);
|
|
|
|
FILE *stream;
|
|
freopen_s(&stream, "CONIN$", "r", stdin);
|
|
freopen_s(&stream, "CONOUT$", "w", stdout);
|
|
freopen_s(&stream, "CONOUT$", "w", stderr);
|
|
|
|
// Prevent accidental console window close
|
|
HWND consoleHandle = GetConsoleWindow();
|
|
HMENU hmenu = GetSystemMenu(consoleHandle, FALSE);
|
|
EnableMenuItem(hmenu, SC_CLOSE, MF_GRAYED);
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Gets the actual path to this executable
|
|
//
|
|
// - As it turns out, the relative path for a program is different when
|
|
// running through VS and when running the .exe directly, which makes
|
|
// it a pain to properly load external files (like textures)
|
|
// - Running through VS: Current Dir is the *project folder*
|
|
// - Running from .exe: Current Dir is the .exe's folder
|
|
// - This has nothing to do with DEBUG and RELEASE modes - it's purely a
|
|
// Visual Studio "thing", and isn't obvious unless you know to look
|
|
// for it. In fact, it could be fixed by changing a setting in VS, but
|
|
// the option is stored in a user file (.suo), which is ignored by most
|
|
// version control packages by default. Meaning: the option must be
|
|
// changed on every PC. Ugh. So instead, here's a helper.
|
|
// --------------------------------------------------------------------------
|
|
std::string DXCore::GetExePath()
|
|
{
|
|
// Assume the path is just the "current directory" for now
|
|
std::string path = ".\\";
|
|
|
|
// Get the real, full path to this executable
|
|
char currentDir[1024] = {};
|
|
GetModuleFileName(0, currentDir, 1024);
|
|
|
|
// Find the location of the last slash charaacter
|
|
char* lastSlash = strrchr(currentDir, '\\');
|
|
if (lastSlash)
|
|
{
|
|
// End the string at the last slash character, essentially
|
|
// chopping off the exe's file name. Remember, c-strings
|
|
// are null-terminated, so putting a "zero" character in
|
|
// there simply denotes the end of the string.
|
|
*lastSlash = 0;
|
|
|
|
// Set the remainder as the path
|
|
path = currentDir;
|
|
}
|
|
|
|
// Toss back whatever we've found
|
|
return path;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------
|
|
// Same as GetExePath(), except it returns a wide character
|
|
// string, which most of the Windows API requires.
|
|
// ---------------------------------------------------
|
|
std::wstring DXCore::GetExePath_Wide()
|
|
{
|
|
// Grab the path as a standard string
|
|
std::string path = GetExePath();
|
|
|
|
// Convert to a wide string
|
|
wchar_t widePath[1024] = {};
|
|
mbstowcs_s(0, widePath, path.c_str(), 1024);
|
|
|
|
// Create a wstring for it and return
|
|
return std::wstring(widePath);
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------
|
|
// Gets the full path to a given file. NOTE: This does
|
|
// NOT "find" the file, it simply concatenates the given
|
|
// relative file path onto the executable's path
|
|
// ----------------------------------------------------
|
|
std::string DXCore::GetFullPathTo(std::string relativeFilePath)
|
|
{
|
|
return GetExePath() + "\\" + relativeFilePath;
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------
|
|
// Same as GetFullPathTo, but with wide char strings.
|
|
//
|
|
// Gets the full path to a given file. NOTE: This does
|
|
// NOT "find" the file, it simply concatenates the given
|
|
// relative file path onto the executable's path
|
|
// ----------------------------------------------------
|
|
std::wstring DXCore::GetFullPathTo_Wide(std::wstring relativeFilePath)
|
|
{
|
|
return GetExePath_Wide() + L"\\" + relativeFilePath;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------
|
|
// Handles messages that are sent to our window by the
|
|
// operating system. Ignoring these messages would cause
|
|
// our program to hang and Windows would think it was
|
|
// unresponsive.
|
|
// --------------------------------------------------------
|
|
LRESULT DXCore::ProcessMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
// Check the incoming message and handle any we care about
|
|
switch (uMsg)
|
|
{
|
|
// This is the message that signifies the window closing
|
|
case WM_DESTROY:
|
|
PostQuitMessage(0); // Send a quit message to our own program
|
|
return 0;
|
|
|
|
// Prevent beeping when we "alt-enter" into fullscreen
|
|
case WM_MENUCHAR:
|
|
return MAKELRESULT(0, MNC_CLOSE);
|
|
|
|
// Prevent the overall window from becoming too small
|
|
case WM_GETMINMAXINFO:
|
|
((MINMAXINFO*)lParam)->ptMinTrackSize.x = 200;
|
|
((MINMAXINFO*)lParam)->ptMinTrackSize.y = 200;
|
|
return 0;
|
|
|
|
// Sent when the window size changes
|
|
case WM_SIZE:
|
|
// Don't adjust anything when minimizing,
|
|
// since we end up with a width/height of zero
|
|
// and that doesn't play well with the GPU
|
|
if (wParam == SIZE_MINIMIZED)
|
|
return 0;
|
|
|
|
// Save the new client area dimensions.
|
|
width = LOWORD(lParam);
|
|
height = HIWORD(lParam);
|
|
|
|
// If DX is initialized, resize
|
|
// our required buffers
|
|
if (device)
|
|
OnResize();
|
|
|
|
return 0;
|
|
|
|
// Has the mouse wheel been scrolled?
|
|
case WM_MOUSEWHEEL:
|
|
Input::GetInstance().SetWheelDelta(GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA);
|
|
return 0;
|
|
|
|
// Is our focus state changing?
|
|
case WM_SETFOCUS: hasFocus = true; return 0;
|
|
case WM_KILLFOCUS: hasFocus = false; return 0;
|
|
case WM_ACTIVATE: hasFocus = (LOWORD(wParam) != WA_INACTIVE); return 0;
|
|
}
|
|
|
|
// Let Windows handle any messages we're not touching
|
|
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|