/****************************************************************************** * File - core.cpp * Author - Joey Pollack * Date - 2021/08/30 (y/m/d) * Mod Date - 2021/08/30 (y/m/d) * Description - The Core Engine Class. Manages the engine components. ******************************************************************************/ #include "core.h" #include "version.h" #include "core_console.h" #include #include // Run modes #include #include // Sub Systems #include #include #include #include // #include // #include #include #include namespace lunarium { Core* Core::mpInstance = nullptr; Core::Core() : mbIsInit(false), mpArgs(nullptr), mpWindow(nullptr), mpGraphics(nullptr), mpInput(nullptr), mGUI(GUI::GetInstance()), mbMidRender(false), mbMidTextureRender(false), mpRunMode(nullptr), mbShowGuiDemo(false) { } Core& Core::GetInstance() { if (!mpInstance) { mpInstance = new Core; } return *mpInstance; } //////////////////////////////////////////////////////////// // SHUTDOWN //////////////////////////////////////////////////////////// void Core::Shutdown() { if (!mpInstance) return; Logger::Log(LogCategory::CORE, LogLevel::INFO, "Lunarium is shutting down!"); int x, y; mpInstance->MainWindow().GetPosition(&x, &y); mpInstance->mState.Display.WindowStartPosition.X = x; mpInstance->mState.Display.WindowStartPosition.Y = y; mpInstance->mpWindow->GetFramebufferSize(&mpInstance->mState.Display.WindowedSize.Width, &mpInstance->mState.Display.WindowedSize.Height); mpInstance->mpWindow->GetPosition(&mpInstance->mState.Display.WindowStartPosition.X, &mpInstance->mState.Display.WindowStartPosition.Y); mpInstance->mState.SaveToFile(); // Run Mode shuts down first mpInstance->mpRunMode->Shutdown(); delete mpInstance->mpRunMode; mpInstance->mpRunMode = nullptr; // Shutdown subsystems CoreAPI::FreeInstance(); ScriptManager::FreeInstance(); //LuaConsole::FreeInstance(); //LogGui::FreeInstance(); GUI::GetInstance().Shutdown(); GUI::FreeInstance(); mpInstance->mpInput->Shutdown(); delete mpInstance->mpInput; mpInstance->mpInput = nullptr; mpInstance->mpGraphics->Shutdown(); delete mpInstance->mpGraphics; mpInstance->mpGraphics = nullptr; mpInstance->mpWindow->Shutdown(); delete mpInstance->mpWindow; mpInstance->mpWindow = nullptr; delete mpInstance->mpArgs; mpInstance->mpArgs = nullptr; mpInstance->mbIsInit = false; delete mpInstance; mpInstance = nullptr; } //////////////////////////////////////////////////////////// // INITIALIZATION //////////////////////////////////////////////////////////// void Core::Initialize(int argc, char** argv) { if (mbIsInit) { return; } // Setup the log system mMasterLogFile.open("Lunarium_Master.log", std::ios_base::app); mMasterLogFile << "\n\n"; mErrorLogFile.open("Lunarium_Errors.log", std::ios_base::app); mErrorLogFile << "\n\n"; if (mMasterLogFile.is_open()) Logger::GetInstance()->AddListener(new FileListener(mMasterLogFile)); if (mErrorLogFile.is_open()) Logger::GetInstance()->AddListener(new FileListener(mErrorLogFile, LogLevel::ERROR | LogLevel::FATAL_ERROR)); // Init the Debug log window OpRes result; mPanels[gui::PanelType::PT_CORE_CONSOLE] = new CoreConsole; // result = LogGui::GetInstance().Initialize(); // if (Failed(result)) // { // Logger::Log(LogCategory::CORE, LogLevel::WARNING, // "Could not initialized the debug log window: %s", result.Description); // } Logger::Log(LogCategory::CORE, LogLevel::INFO, "Running Lunarium version %s", Version::GetVersion().ToString().c_str()); // Attempt to load the engine state file. This file should be placed in the same directory as the lunarium program. if (Failed(State::CreateFromFile("engine_state.xml", mState))) { Logger::Log(LogCategory::CORE, LogLevel::WARNING, "Unable to load state file: engine_state.xml. Loading default state."); mState = State::CreateDefault(); } else { Logger::Log(LogCategory::CORE, LogLevel::INFO, "Loaded state file: engine_state.xml"); } // Parse command line args -- None right now std::vector sd; mpArgs = new Args(argc, argv, '-', sd); // Init Graphics/Window system mpWindow = new Window; result = mpWindow->Initialize(mState); if (Failed(result)) { Logger::Log(LogCategory::CORE, LogLevel::FATAL_ERROR, "Could not initialize the Window system: %s", result.Description.c_str()); return; } if (RenderSystem::OPENGL == mState.Display.Renderer) { mpGraphics = new OglGraphics; } else if (RenderSystem::VULKAN == mState.Display.Renderer) { Logger::Log(LogCategory::CORE, LogLevel::FATAL_ERROR, "Can not create Vulkan graphics system because it is not yet implemented. Must use OpenGL instead."); return; } else { Logger::Log(LogCategory::CORE, LogLevel::FATAL_ERROR, "Could not create graphics system: Unknown render framework specified."); return; } // TODO: This should probably be based on a state setting instead #ifdef _DEBUG result = mpGraphics->Initialize(mpWindow); #else result = mpGraphics->Initialize(mpWindow, false); #endif if (Failed(result)) { Logger::Log(LogCategory::CORE, LogLevel::FATAL_ERROR, "Could not initialized the graphics system: %s", result.Description); return; } mpGraphics->SetClearColor(Color(0.5f, 0.5f, 0.75f, 1.0f)); // INPUT mpInput = new InputManager; mpInput->Initialize(mpWindow); // GUI result = mGUI.Initialize(mpWindow->GetWindow()); if (Failed(result)) { Logger::Log(LogCategory::CORE, LogLevel::WARNING, "Could not initialized the main GUI system: %s", result.Description); } // SCRIPTING ScriptManager& scriptMan = ScriptManager::GetInstance(); result = scriptMan.Initialize(); if (Failed(result)) { Logger::Log(LogCategory::CORE, LogLevel::WARNING, "Could not initialized the LUA script manager: %s", result.Description); } CoreAPI& capi = CoreAPI::GetInstance(); result = capi.Initialize(scriptMan); if (Failed(result)) { Logger::Log(LogCategory::CORE, LogLevel::WARNING, "Could not initialized the LUA Core API: %s", result.Description); } // RUN MODE const char* types[] = { "game", "editor", "test" }; Logger::Log(LogCategory::CORE, LogLevel::INFO, "Running in mode: %s", types[mState.Mode]); if (RunMode::MODE_TEST == mState.Mode) { mpRunMode = new Tester; } else if (RunMode::MODE_EDITOR == mState.Mode) { #if BUILD_NO_EDITOR Logger::Log(LogCategory::CORE, LogLevel::FATAL_ERROR, "The Editor is not available with this build"); return; #else mpRunMode = new editor::Editor; delete mPanels[gui::PanelType::PT_CORE_CONSOLE]; mPanels[gui::PanelType::PT_CORE_CONSOLE] = nullptr; // Editor has it's own console // LogGui::GetInstance().SetStickToWindow(false); // LuaConsole::GetInstance().SetStickToWindow(false); #endif } // Initialize the Run Mode if (Failed(mpRunMode->Initialize())) { Logger::Log(LogCategory::CORE, LogLevel::FATAL_ERROR, "Could not initialize the Run Mode: %s", result.Description.c_str()); return; } mbIsInit = true; } bool Core::IsInit() const { return mbIsInit; } const State& Core::GetState() const { return mState; } void Core::SignalShutdown() { mpWindow->SetShouldCloseFlag(true); } //////////////////////////////////////////////////////////// // GAME LOOP //////////////////////////////////////////////////////////// void Core::RunGameLoop() { mFrameCounter.Reset(); // TODO: Init frame counter while (!mpWindow->ShouldWindowClose()) { mFrameCounter.NewFrame(); // Display FPS in window title for now std::string title = "Lunarium - FPS: "; title += std::to_string(mFrameCounter.GetFrameData().CurrentFPS); glfwSetWindowTitle(mpWindow->GetWindow(), title.c_str()); // Get pointers to gui panels CoreConsole* con = (CoreConsole*)mPanels[gui::PanelType::PT_CORE_CONSOLE]; // Poll input Window::PollEvents(); auto keyEvents = mpInput->PollKeys(); if (!ImGui::GetIO().WantCaptureKeyboard) { // Send key events for (int i = 0; i < keyEvents.KeysPressed.size(); i++) { mpRunMode->OnKeyPress(keyEvents.KeysPressed[i]); } for (int i = 0; i < keyEvents.KeysReleased.size(); i++) { mpRunMode->OnKeyRelease(keyEvents.KeysReleased[i]); } } else { // Check if there is a new LUA command std::string command; if (con && con->GetNewCommand(command)) { // Logger::Log(LogCategory::CORE, LogLevel::INFO, "New LUA command: %s", command.c_str()); OpRes result = ScriptManager::RunScript(command.c_str()); if (Failed(result)) { Logger::Log(LogCategory::CORE, LogLevel::INFO_VERBOSE, result.Description.c_str()); } } } // UPDATE game state mpRunMode->OnTick(mFrameCounter.GetFrameData().LastFrameTime); // DEBUG PANELS if (Core::Input().IsKeyPressed(KeyCode::F2, true) && con) { con->SetOpen(!con->IsOpen()); } if (Core::Input().IsKeyPressed(KeyCode::F3, true)) { mbShowGuiDemo = !mbShowGuiDemo; } // RENDER if (mbMidTextureRender) { Logger::Log(LogCategory::CORE, LogLevel::WARNING, "Render to texture was not ended!"); EndRenderToTexture(); } mGUI.NewFrame(); mpGraphics->BeginDraw(); mbMidRender = true; // Gui windows if (con) { con->DoFrame(); } if (mbShowGuiDemo) { mGUI.ShowDemoWindow(mbShowGuiDemo); } // Run mode mpRunMode->OnRender(mpGraphics); // END RENDER mGUI.EndFrame(); mpGraphics->EndDraw(); mbMidRender = false; } } OpRes Core::BeginRenderToTexture(int id) { if (mbMidRender) { return OpRes::Fail("Can not switch render targets in the middle of rendering"); } mbMidTextureRender = true; mpGraphics->BeginDraw(id); return OpRes::OK(); } Image* Core::EndRenderToTexture() { if (!mbMidTextureRender) { return nullptr; } mbMidTextureRender = false; return mpGraphics->EndDraw(); } //////////////////////////////////////////////////////////// // STATIC INTERFACE //////////////////////////////////////////////////////////// Window& Core::MainWindow() { return *mpInstance->mpWindow; } IGraphics& Core::Graphics() { return *mpInstance->mpGraphics; } InputManager& Core::Input() { return *mpInstance->mpInput; } }