309 lines
8.3 KiB
C++
309 lines
8.3 KiB
C++
#include "Application.h"
|
|
#ifdef NFENGINE
|
|
#include "GL\glew.h"
|
|
#include "GL\wglew.h"
|
|
#endif
|
|
|
|
namespace nf {
|
|
DEBUGINIT;
|
|
|
|
Application::Application(Config& config) :
|
|
m_currentConfig(config),
|
|
m_wndPlacement{ sizeof(m_wndPlacement) },
|
|
m_running(false)
|
|
{
|
|
Log("Creating NF application");
|
|
Log("Width: " + std::to_string(m_currentConfig.width) + ", Height: " + std::to_string(m_currentConfig.height) + ", Fullscreen: " + std::to_string(m_currentConfig.fullscreen) + ", Title: " + m_currentConfig.title);
|
|
|
|
m_hInst = GetModuleHandle(NULL);
|
|
registerWindowClass();
|
|
RECT windowSize = getWindowRect();
|
|
|
|
m_window = CreateWindowEx(NULL, m_wclassName, toWide(m_currentConfig.title), WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, 1280, windowSize.bottom, NULL, NULL, m_hInst, NULL);
|
|
m_defaultWindowStyle = GetWindowLong(m_window, GWL_STYLE);
|
|
SetProp(m_window, L"App", this);
|
|
if (m_currentConfig.fullscreen) toggleFullscreen();
|
|
}
|
|
|
|
void Application::setWindowIcon(HANDLE hIcon) {
|
|
SendMessage(m_window, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
|
|
SendMessage(m_window, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
|
|
}
|
|
//TODO: Test these top-level features
|
|
void Application::setWindowCursor(HCURSOR hCursor) {
|
|
SetClassLongPtr(m_window, GCLP_HCURSOR, (LONG_PTR)hCursor);
|
|
}
|
|
|
|
void Application::addState(IGamestate* state, const char* stateName) {
|
|
if (m_states.find(stateName) == m_states.end()) {
|
|
m_states[stateName] = state;
|
|
}
|
|
else {
|
|
Error(("State \"" + (std::string)stateName + (std::string)"\" already exists!").c_str());
|
|
}
|
|
}
|
|
|
|
void Application::addDefaultState(const char* stateName) {
|
|
if (!m_defaultStateAdded) {
|
|
if (m_states.find(stateName) != m_states.end()) {
|
|
m_DefaultState = m_states[stateName];
|
|
m_defaultStateAdded = true;
|
|
}
|
|
else {
|
|
Error(("State \"" + (std::string)stateName + (std::string)"\" doesn't exist!").c_str());
|
|
}
|
|
}
|
|
else {
|
|
Error("More than one default state defined");
|
|
}
|
|
}
|
|
|
|
void Application::changeState(const char* stateName) {
|
|
if (m_states.find(stateName) != m_states.end()) {
|
|
m_currentState->onExit();
|
|
m_currentState = m_states[stateName];
|
|
m_currentState->onEnter(this);
|
|
}
|
|
else {
|
|
Error(("State \"" + (std::string)stateName + (std::string)"\" doesn't exist!").c_str());
|
|
}
|
|
}
|
|
|
|
void Application::run() {
|
|
addIntroState();
|
|
showWindow(true);
|
|
m_running = true;
|
|
MSG msg = { };
|
|
std::thread mainThread(&Application::startMainThread, this);
|
|
while (m_running) {
|
|
while (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
if (msg.message == WM_QUIT) {
|
|
m_running = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
mainThread.join();
|
|
}
|
|
|
|
void Application::showWindow(bool show) {
|
|
if (show)
|
|
ShowWindow(m_window, SW_SHOW);
|
|
else
|
|
ShowWindow(m_window, SW_HIDE);
|
|
}
|
|
|
|
const Config& Application::getConfig() const {
|
|
return m_currentConfig;
|
|
}
|
|
//TODO: Test this
|
|
int Application::getFPS() const {
|
|
return m_FPS;
|
|
}
|
|
|
|
bool Application::isInput(unsigned int code) {
|
|
if (code < 164) {
|
|
return m_input[code];
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void Application::addIntroState() {
|
|
m_sIntro = new IntroGamestate;
|
|
m_sIntro->onEnter(this);
|
|
m_currentState = m_sIntro;
|
|
}
|
|
|
|
void Application::startMainThread() {
|
|
createOpenGLContext();
|
|
while (m_running) {
|
|
m_deltaTime = m_fpsDuration.count();
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
m_currentState->update();
|
|
m_currentState->render();
|
|
SwapBuffers(m_hdc);
|
|
m_frames++;
|
|
m_fpsClock2 = std::chrono::steady_clock::now();
|
|
m_fpsDuration = m_fpsClock2 - m_fpsClock1;
|
|
if (m_fpsDuration.count() >= 1.0) {
|
|
m_fpsClock1 = std::chrono::steady_clock::now();
|
|
m_FPS = m_frames;
|
|
m_frames = 0;
|
|
Log(m_FPS);
|
|
}
|
|
m_fpsDuration = std::chrono::steady_clock::now() - m_frameClock;
|
|
while (m_fpsDuration.count() < m_minFrametime) {
|
|
m_fpsDuration = std::chrono::steady_clock::now() - m_frameClock;
|
|
}
|
|
m_frameClock = std::chrono::steady_clock::now();
|
|
}
|
|
m_currentState->onExit();
|
|
ReleaseDC(m_window, m_hdc);
|
|
wglMakeCurrent(NULL, NULL);
|
|
wglDeleteContext(m_hglrc);
|
|
}
|
|
|
|
void Application::registerWindowClass() {
|
|
if (!FindWindow(L"NFClass", NULL)) {
|
|
m_wclassName = L"NFClass";
|
|
WNDCLASS wclass = { };
|
|
wclass.lpszClassName = m_wclassName;
|
|
wclass.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wclass.hIcon = NULL;
|
|
wclass.hInstance = m_hInst;
|
|
wclass.lpfnWndProc = Application::WindowProc;
|
|
RegisterClass(&wclass);
|
|
}
|
|
else {
|
|
Error("Cannot run two NF applications at once.");
|
|
}
|
|
}
|
|
|
|
void Application::toggleFullscreen() {
|
|
DWORD wndStyle = GetWindowLong(m_window, GWL_STYLE);
|
|
if (wndStyle & WS_OVERLAPPEDWINDOW) {
|
|
GetWindowPlacement(m_window, &m_wndPlacement);
|
|
MONITORINFO mi = { sizeof(mi) };
|
|
GetMonitorInfo(MonitorFromWindow(m_window, MONITOR_DEFAULTTOPRIMARY), &mi);
|
|
SetWindowLong(m_window, GWL_STYLE, wndStyle & ~WS_OVERLAPPEDWINDOW);
|
|
SetWindowPos(m_window, HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top, mi.rcMonitor.right - mi.rcMonitor.left, mi.rcMonitor.bottom - mi.rcMonitor.top, SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
|
|
}
|
|
else {
|
|
SetWindowLong(m_window, GWL_STYLE, m_defaultWindowStyle);
|
|
SetWindowPlacement(m_window, &m_wndPlacement);
|
|
SetWindowPos(m_window, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
|
|
}
|
|
}
|
|
|
|
RECT Application::getWindowRect() const {
|
|
int w = m_currentConfig.width;
|
|
int h = m_currentConfig.height;
|
|
RECT temp = { };
|
|
temp.right = w;
|
|
temp.bottom = h;
|
|
AdjustWindowRect(&temp, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, false);
|
|
temp.right -= temp.left;
|
|
temp.bottom -= temp.top;
|
|
return temp;
|
|
}
|
|
|
|
LRESULT CALLBACK Application::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|
Application* app = (Application*)GetProp(hWnd, L"App");
|
|
//TODO: Dragging blocks thread
|
|
switch (uMsg) {
|
|
case WM_CREATE: {
|
|
return 0;
|
|
}
|
|
case WM_SYSKEYDOWN: {
|
|
if (GetKeyState(VK_RETURN) & 0x8000) {
|
|
app->toggleFullscreen();
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
case WM_MENUCHAR: {
|
|
return MNC_CLOSE << 16;
|
|
}
|
|
case WM_LBUTTONDOWN: {
|
|
app->m_input[1] = true;
|
|
return 0;
|
|
}
|
|
case WM_LBUTTONUP: {
|
|
app->m_input[1] = false;
|
|
return 0;
|
|
}
|
|
case WM_RBUTTONDOWN: {
|
|
app->m_input[2] = true;
|
|
return 0;
|
|
}
|
|
case WM_RBUTTONUP: {
|
|
app->m_input[2] = false;
|
|
return 0;
|
|
}
|
|
case WM_MBUTTONDOWN: {
|
|
app->m_input[4] = true;
|
|
return 0;
|
|
}
|
|
case WM_MBUTTONUP: {
|
|
app->m_input[4] = false;
|
|
return 0;
|
|
}
|
|
case WM_KEYDOWN: {
|
|
if (wParam < 164 && !(lParam & (1 << 30))) {
|
|
app->m_input[wParam] = true;
|
|
}
|
|
break;
|
|
}
|
|
case WM_KEYUP: {
|
|
if (wParam < 164) {
|
|
app->m_input[wParam] = false;
|
|
}
|
|
break;
|
|
}
|
|
case WM_CLOSE: {
|
|
DestroyWindow(hWnd);
|
|
return 0;
|
|
}
|
|
case WM_DESTROY: {
|
|
PostQuitMessage(0);
|
|
return 0;
|
|
}
|
|
}
|
|
return DefWindowProc(hWnd, uMsg, wParam, lParam);//TODO: Fill out events
|
|
}
|
|
|
|
void Application::createOpenGLContext() {
|
|
m_hdc = GetDC(m_window);
|
|
PIXELFORMATDESCRIPTOR pfd = {
|
|
sizeof(PIXELFORMATDESCRIPTOR),
|
|
1,
|
|
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, //Flags
|
|
PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette.
|
|
32, // Colordepth of the framebuffer.
|
|
0, 0, 0, 0, 0, 0,
|
|
0,
|
|
0,
|
|
0,
|
|
0, 0, 0, 0,
|
|
24, // Number of bits for the depthbuffer
|
|
8, // Number of bits for the stencilbuffer
|
|
0, // Number of Aux buffers in the framebuffer.
|
|
PFD_MAIN_PLANE,
|
|
0,
|
|
0, 0, 0
|
|
};
|
|
int pf = ChoosePixelFormat(m_hdc, &pfd);
|
|
SetPixelFormat(m_hdc, pf, &pfd);
|
|
m_hglrc = wglCreateContext(m_hdc);
|
|
wglMakeCurrent(m_hdc, m_hglrc);
|
|
glewExperimental = GL_TRUE;
|
|
if (glewInit() != GLEW_OK) {
|
|
Error("Could not initialize GLEW");
|
|
}
|
|
const int attrib[] = {
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
0, 0
|
|
};
|
|
wglDeleteContext(m_hglrc);
|
|
m_hglrc = wglCreateContextAttribsARB(m_hdc, NULL, attrib);
|
|
wglMakeCurrent(m_hdc, m_hglrc);
|
|
wglSwapIntervalEXT(0);
|
|
Log("OpenGL version: " + std::string((char*)glGetString(GL_VERSION)));
|
|
GLuint vao;
|
|
glGenVertexArrays(1, &vao);
|
|
glBindVertexArray(vao);
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
}
|
|
|
|
Application::~Application() {
|
|
Log("Exiting NF application");
|
|
//TODO: Iterate through m_activeStates and m_states and exit and unload them
|
|
}
|
|
}
|