437 lines
9.3 KiB
C++
437 lines
9.3 KiB
C++
// Copyright (C) 2021-2022, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
|
|
#include "verus.h"
|
|
|
|
using namespace verus;
|
|
using namespace verus::Game;
|
|
|
|
struct MyRendererDelegate : CGI::RendererDelegate
|
|
{
|
|
PBaseGame _p = nullptr;
|
|
|
|
MyRendererDelegate(PBaseGame p) : _p(p) {}
|
|
~MyRendererDelegate() {}
|
|
|
|
virtual void Renderer_OnDraw() override
|
|
{
|
|
_p->BaseGame_Draw();
|
|
}
|
|
};
|
|
VERUS_TYPEDEFS(MyRendererDelegate);
|
|
|
|
struct BaseGame::Pimpl : AllocatorAware
|
|
{
|
|
PBaseGame _p = nullptr;
|
|
Scene::MainCamera _camera;
|
|
Spirit _cameraSpirit;
|
|
bool _defaultCameraMovement = true;
|
|
bool _escapeKeyExitGame = true;
|
|
bool _minimized = false;
|
|
bool _rawInputEvents = false;
|
|
bool _showFPS = true;
|
|
|
|
Pimpl(PBaseGame p) : _p(p)
|
|
{
|
|
}
|
|
|
|
~Pimpl()
|
|
{
|
|
}
|
|
};
|
|
|
|
BaseGame::BaseGame()
|
|
{
|
|
Utils::MakeEx(&_alloc); // For paths.
|
|
Make_D(); // For log.
|
|
_p = new Pimpl(this);
|
|
}
|
|
|
|
BaseGame::~BaseGame()
|
|
{
|
|
delete _p;
|
|
|
|
_engineInit.Free();
|
|
|
|
Free_D();
|
|
Utils::FreeEx(&_alloc);
|
|
SDL_Quit();
|
|
|
|
if (_restartApp)
|
|
RestartApp();
|
|
}
|
|
|
|
void BaseGame::Initialize(VERUS_MAIN_DEFAULT_ARGS, App::Window::RcDesc windowDesc)
|
|
{
|
|
const int ret = SDL_Init(SDL_INIT_EVERYTHING);
|
|
if (ret)
|
|
throw VERUS_RUNTIME_ERROR << "SDL_Init(); " << ret;
|
|
|
|
Utils::I().InitPaths();
|
|
|
|
App::Settings::Make();
|
|
VERUS_QREF_SETTINGS;
|
|
settings.ParseCommandLineArgs(argc, argv);
|
|
settings.Load();
|
|
settings.HandleHighDpi();
|
|
settings.HandleCommandLineArgs();
|
|
settings.Validate();
|
|
settings.Save();
|
|
App::Window::Desc updatedWindowDesc = windowDesc;
|
|
BaseGame_UpdateSettings(updatedWindowDesc);
|
|
if (App::Settings::Platform::uwp == settings._platform)
|
|
settings._gapi = 12;
|
|
|
|
// Allocate memory:
|
|
_engineInit.Make();
|
|
|
|
#if defined(_DEBUG) || defined(VERUS_RELEASE_DEBUG)
|
|
Utils::TestAll();
|
|
#endif
|
|
|
|
// Window and renderer:
|
|
_window.Init(updatedWindowDesc);
|
|
SDL_GetWindowSize(_window.GetSDL(),
|
|
&settings._displaySizeWidth,
|
|
&settings._displaySizeHeight); // Display size, window size and swap chain size must match.
|
|
CGI::Renderer::I().SetMainWindow(&_window);
|
|
_engineInit.Init(new MyRendererDelegate(this));
|
|
|
|
VERUS_QREF_IM;
|
|
const int focus = im.GainFocus(this);
|
|
VERUS_RT_ASSERT(0 == focus);
|
|
|
|
// Configure:
|
|
VERUS_QREF_RENDERER;
|
|
VERUS_QREF_SM;
|
|
VERUS_QREF_MM;
|
|
_p->_camera.SetAspectRatio(renderer.GetSwapChainAspectRatio());
|
|
_p->_camera.Update();
|
|
if (Scene::SceneManager::IsValidSingleton())
|
|
Scene::SceneManager::I().SetCamera(&_p->_camera);
|
|
|
|
renderer.BeginFrame(); // Begin recording a command buffer.
|
|
renderer.InitCmd();
|
|
_engineInit.InitCmd();
|
|
if (Scene::MaterialManager::IsValidSingleton())
|
|
Scene::MaterialManager::I().InitCmd();
|
|
BaseGame_LoadContent();
|
|
renderer.EndFrame(); // End recording a command buffer.
|
|
}
|
|
|
|
void BaseGame::Loop(bool relativeMouseMode)
|
|
{
|
|
VERUS_QREF_ASYNC;
|
|
VERUS_QREF_IM;
|
|
VERUS_QREF_RENDERER;
|
|
VERUS_QREF_TIMER;
|
|
|
|
if (relativeMouseMode)
|
|
{
|
|
if (SDL_SetRelativeMouseMode(SDL_TRUE))
|
|
throw VERUS_RUNTIME_ERROR << "SDL_SetRelativeMouseMode(SDL_TRUE)";
|
|
}
|
|
|
|
timer.Update();
|
|
|
|
SDL_Event event;
|
|
bool quit = false;
|
|
|
|
do // The Game Loop.
|
|
{
|
|
while (SDL_PollEvent(&event))
|
|
{
|
|
ImGui_ImplSDL2_ProcessEvent(&event);
|
|
|
|
// Toggle fullscreen (Alt+Enter):
|
|
if ((SDL_KEYDOWN == event.type) && (SDLK_RETURN == event.key.keysym.sym) && (event.key.keysym.mod & KMOD_ALT))
|
|
ToggleFullscreen();
|
|
|
|
bool keyboardShortcut = false;
|
|
if (_p->_rawInputEvents && !ImGui::GetIO().WantCaptureMouse)
|
|
{
|
|
switch (event.type)
|
|
{
|
|
case SDL_KEYDOWN:
|
|
{
|
|
if (event.key.keysym.mod & (KMOD_CTRL | KMOD_SHIFT | KMOD_ALT))
|
|
keyboardShortcut = BaseGame_SDL_OnKeyboardShortcut(event.key.keysym.sym, event.key.keysym.mod);
|
|
}
|
|
break;
|
|
case SDL_MOUSEMOTION:
|
|
{
|
|
BaseGame_SDL_OnMouseMotion(event.motion.xrel, event.motion.yrel);
|
|
}
|
|
break;
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
{
|
|
if (2 == event.button.clicks)
|
|
BaseGame_SDL_OnMouseDoubleClick(event.button.button);
|
|
else
|
|
BaseGame_SDL_OnMouseButtonDown(event.button.button);
|
|
}
|
|
break;
|
|
case SDL_MOUSEBUTTONUP:
|
|
{
|
|
BaseGame_SDL_OnMouseButtonUp(event.button.button);
|
|
}
|
|
break;
|
|
case SDL_MOUSEWHEEL:
|
|
{
|
|
BaseGame_SDL_OnMouseWheel(event.wheel.y);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!keyboardShortcut && !im.HandleEvent(event))
|
|
{
|
|
switch (event.type)
|
|
{
|
|
case SDL_QUIT:
|
|
{
|
|
if (BaseGame_CanQuitEventLoop())
|
|
quit = true;
|
|
}
|
|
break;
|
|
case SDL_WINDOWEVENT:
|
|
{
|
|
switch (event.window.event)
|
|
{
|
|
case SDL_WINDOWEVENT_MOVED:
|
|
{
|
|
BaseGame_OnWindowMoved();
|
|
}
|
|
break;
|
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
|
{
|
|
if (renderer.OnWindowSizeChanged(event.window.data1, event.window.data2))
|
|
{
|
|
Scene::PCamera pCamera = Scene::SceneManager::IsValidSingleton() ? Scene::SceneManager::I().GetCamera() : nullptr;
|
|
if (pCamera)
|
|
{
|
|
pCamera->SetAspectRatio(renderer.GetSwapChainAspectRatio());
|
|
pCamera->Update();
|
|
}
|
|
BaseGame_OnWindowSizeChanged();
|
|
}
|
|
}
|
|
break;
|
|
case SDL_WINDOWEVENT_MINIMIZED:
|
|
{
|
|
if (!_p->_minimized)
|
|
{
|
|
_p->_minimized = true;
|
|
BaseGame_OnDeactivated();
|
|
renderer->OnMinimized();
|
|
}
|
|
}
|
|
break;
|
|
case SDL_WINDOWEVENT_SHOWN:
|
|
case SDL_WINDOWEVENT_RESTORED:
|
|
{
|
|
if (_p->_minimized)
|
|
{
|
|
_p->_minimized = false;
|
|
BaseGame_OnActivated();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_p->_escapeKeyExitGame && im.IsKeyDownEvent(SDL_SCANCODE_ESCAPE))
|
|
Exit();
|
|
|
|
if (_p->_minimized)
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
|
if (_p->_minimized || _restartApp)
|
|
continue;
|
|
|
|
//
|
|
// UPDATE
|
|
//
|
|
|
|
renderer.BeginFrame();
|
|
|
|
async.Update();
|
|
|
|
timer.Update();
|
|
|
|
if (_p->_defaultCameraMovement) // Handle input:
|
|
{
|
|
const float speed = im.IsKeyPressed(SDL_SCANCODE_SPACE) ? 20.f : 2.f;
|
|
if (im.IsKeyPressed(SDL_SCANCODE_W))
|
|
_p->_cameraSpirit.MoveFront(speed);
|
|
if (im.IsKeyPressed(SDL_SCANCODE_S))
|
|
_p->_cameraSpirit.MoveFront(-speed);
|
|
if (im.IsKeyPressed(SDL_SCANCODE_A))
|
|
_p->_cameraSpirit.MoveSide(-speed);
|
|
if (im.IsKeyPressed(SDL_SCANCODE_D))
|
|
_p->_cameraSpirit.MoveSide(speed);
|
|
}
|
|
im.HandleInput();
|
|
if (_restartApp)
|
|
continue;
|
|
|
|
if (Physics::Bullet::IsValidSingleton())
|
|
Physics::Bullet::I().Simulate();
|
|
|
|
if (_p->_defaultCameraMovement)
|
|
{
|
|
_p->_cameraSpirit.HandleActions();
|
|
_p->_cameraSpirit.Update();
|
|
_p->_camera.MoveEyeTo(_p->_cameraSpirit.GetPosition());
|
|
_p->_camera.MoveAtTo(_p->_cameraSpirit.GetPosition() + _p->_cameraSpirit.GetFrontDirection());
|
|
if (Scene::Water::IsValidSingleton())
|
|
{
|
|
VERUS_QREF_WATER;
|
|
if (water.IsInitialized())
|
|
_p->_camera.ExcludeWaterLine();
|
|
}
|
|
_p->_camera.Update();
|
|
if (Scene::SceneManager::IsValidSingleton())
|
|
Scene::SceneManager::I().SetCamera(&_p->_camera);
|
|
}
|
|
|
|
BaseGame_Update();
|
|
if (_restartApp)
|
|
continue;
|
|
|
|
if (Audio::AudioSystem::IsValidSingleton())
|
|
Audio::AudioSystem::I().Update();
|
|
|
|
// Draw current frame:
|
|
renderer.Draw();
|
|
im.ResetInputState();
|
|
renderer.EndFrame();
|
|
|
|
// Show FPS:
|
|
if (_p->_showFPS && timer.IsEventEvery(500))
|
|
{
|
|
char title[40];
|
|
CSZ gapi = "";
|
|
switch (renderer->GetGapi())
|
|
{
|
|
case CGI::Gapi::vulkan: gapi = "Vulkan"; break;
|
|
case CGI::Gapi::direct3D11: gapi = "Direct3D 11"; break;
|
|
case CGI::Gapi::direct3D12: gapi = "Direct3D 12"; break;
|
|
}
|
|
sprintf_s(title, "GAPI: %s, FPS: %.1f", gapi, renderer.GetFps());
|
|
SDL_SetWindowTitle(renderer.GetMainWindow()->GetSDL(), title);
|
|
}
|
|
} while (!quit); // The Game Loop.
|
|
|
|
BaseGame_UnloadContent();
|
|
|
|
if (relativeMouseMode)
|
|
{
|
|
if (SDL_SetRelativeMouseMode(SDL_FALSE))
|
|
throw VERUS_RUNTIME_ERROR << "SDL_SetRelativeMouseMode(SDL_FALSE)";
|
|
}
|
|
}
|
|
|
|
void BaseGame::Exit()
|
|
{
|
|
Utils::PushQuitEvent();
|
|
}
|
|
|
|
void BaseGame::InputFocus_OnMouseMove(float dx, float dy)
|
|
{
|
|
if (!SDL_GetRelativeMouseMode())
|
|
return;
|
|
if (_p->_defaultCameraMovement)
|
|
{
|
|
_p->_cameraSpirit.TurnPitch(dy);
|
|
_p->_cameraSpirit.TurnYaw(dx);
|
|
}
|
|
}
|
|
|
|
bool BaseGame::IsFullscreen() const
|
|
{
|
|
const Uint32 flags = SDL_GetWindowFlags(_window.GetSDL());
|
|
return flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
}
|
|
|
|
void BaseGame::ToggleFullscreen()
|
|
{
|
|
const bool fullscreen = IsFullscreen();
|
|
if (SDL_SetWindowFullscreen(_window.GetSDL(), fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP))
|
|
throw VERUS_RUNTIME_ERROR << "SDL_SetWindowFullscreen()";
|
|
}
|
|
|
|
Scene::RCamera BaseGame::GetCamera()
|
|
{
|
|
return _p->_camera;
|
|
}
|
|
|
|
RSpirit BaseGame::GetCameraSpirit()
|
|
{
|
|
return _p->_cameraSpirit;
|
|
}
|
|
|
|
void BaseGame::EnableDefaultCameraMovement(bool b)
|
|
{
|
|
_p->_defaultCameraMovement = b;
|
|
}
|
|
|
|
void BaseGame::EnableEscapeKeyExitGame(bool b)
|
|
{
|
|
_p->_escapeKeyExitGame = b;
|
|
}
|
|
|
|
void BaseGame::EnableRawInputEvents(bool b)
|
|
{
|
|
_p->_rawInputEvents = b;
|
|
}
|
|
|
|
void BaseGame::ShowFPS(bool b)
|
|
{
|
|
_p->_showFPS = b;
|
|
}
|
|
|
|
void BaseGame::BulletDebugDraw()
|
|
{
|
|
VERUS_QREF_BULLET;
|
|
bullet.DebugDraw();
|
|
}
|
|
|
|
void BaseGame::RestartApp()
|
|
{
|
|
#ifdef _WIN32
|
|
wchar_t pathname[MAX_PATH] = {};
|
|
GetModuleFileName(nullptr, pathname, MAX_PATH);
|
|
|
|
wchar_t commandLine[MAX_PATH] = {};
|
|
wcscpy_s(commandLine, L"--restarted");
|
|
|
|
STARTUPINFO si = {};
|
|
PROCESS_INFORMATION pi = {};
|
|
si.cb = sizeof(si);
|
|
|
|
CreateProcess(
|
|
pathname,
|
|
commandLine,
|
|
nullptr,
|
|
nullptr,
|
|
FALSE,
|
|
CREATE_NEW_PROCESS_GROUP,
|
|
nullptr,
|
|
nullptr,
|
|
&si,
|
|
&pi);
|
|
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
#endif
|
|
}
|
|
|
|
void BaseGame::RequestAppRestart()
|
|
{
|
|
_restartApp = true;
|
|
Exit();
|
|
}
|