/****************************************************************************** * 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 #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::Info(LogCategory::CORE, "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); mGraphicsLogFile.open("Lunarium_Graphics.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)); if (mGraphicsLogFile.is_open()) Logger::GetInstance()->AddListener(new FileListener(mGraphicsLogFile, LogLevel::GRAPHICS_INTERNAL_DEBUG | LogLevel::GRAPHICS_INTERNAL_ERROR)); Logger::GetInstance()->SetAllowRepeats(true); // Init the Debug log window OpRes result; mPanelIDs.CoreConsole = AddPanel(new CoreConsole); Logger::Info(LogCategory::CORE, "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::Warn(LogCategory::CORE, "Unable to load state file: engine_state.xml. Loading default state."); mState = State::CreateDefault(); } else { Logger::Info(LogCategory::CORE, "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::Fatal(LogCategory::CORE, "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::Fatal(LogCategory::CORE, "Can not create Vulkan graphics system because it is not yet implemented. Must use OpenGL instead."); return; } else { Logger::Fatal(LogCategory::CORE, "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::Fatal(LogCategory::CORE, "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::Warn(LogCategory::CORE, "Could not initialized the main GUI system: %s", result.Description); } // SCRIPTING ScriptManager& scriptMan = ScriptManager::GetInstance(); result = scriptMan.Initialize(); if (Failed(result)) { Logger::Warn(LogCategory::CORE, "Could not initialized the LUA script manager: %s", result.Description); } CoreAPI& capi = CoreAPI::GetInstance(); result = capi.Initialize(scriptMan); if (Failed(result)) { Logger::Warn(LogCategory::CORE, "Could not initialized the LUA Core API: %s", result.Description); } // RUN MODE const char* types[] = { "game", "editor", "test" }; Logger::Info(LogCategory::CORE, "Running in mode: %s", types[mState.Mode]); if (RunMode::MODE_TEST == mState.Mode) { mpRunMode = new TestBed; } else if (RunMode::MODE_EDITOR == mState.Mode) { #if BUILD_NO_EDITOR Logger::Fatal(LogCategory::CORE, "The Editor is not available with this build"); return; #else mpRunMode = new editor::Editor; FreePanel(mPanelIDs.CoreConsole); // Editor uses it's own console #endif } // Initialize the Run Mode if (Failed(mpRunMode->Initialize())) { Logger::Fatal(LogCategory::CORE, "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[mPanelIDs.CoreConsole]; // 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::Debug(LogCategory::SCRIPTING, "New LUA command: %s", command.c_str()); OpRes result = ScriptManager::RunScript(command.c_str()); if (Failed(result)) { Logger::Error(LogCategory::SCRIPTING, 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::Warn(LogCategory::CORE, "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; } //////////////////////////////////////////////////////////// // HELPERS //////////////////////////////////////////////////////////// uint32_t Core::AddPanel(gui::Panel* p) { mPanels.push_back(p); return mPanels.size() - 1; } void Core::FreePanel(uint32_t id) { delete mPanels[id]; mPanels[id] = nullptr; } CoreLogListener::CoreLogListener(uint32_t acceptedLogLevels, uint32_t acceptedLogCategories, const char* myName) : LogListener(acceptedLogLevels, acceptedLogCategories, myName) { } bool CoreLogListener::Log(LogMessage& message) { if (!LevelIsSet(message.LogLevel) || !CategoryIsSet(message.LogCategory)) return false; std::cout << std::endl; switch (message.LogLevel) { case LogLevel::GRAPHICS_INTERNAL_ERROR: case LogLevel::FATAL_ERROR: case LogLevel::ERROR: std::cout << Terminal::Color(TermColor::TC_RED); break; case LogLevel::WARNING: std::cout << Terminal::Color(TermColor::TC_YELLOW); break; case LogLevel::INFO: std::cout << Terminal::Color(TermColor::TC_GREEN); break; case LogLevel::DEBUG: std::cout << Terminal::Color(TermColor::TC_BLUE); break; case LogLevel::TRACE: std::cout << Terminal::Color(TermColor::TC_CYAN); break; case LogLevel::GRAPHICS_INTERNAL_DEBUG: break; } std::cout << Logger::TimeStamp() << Logger::GetCategoryName(message.LogCategory) << Logger::GetLevelName(message.LogLevel) << message.Message << Terminal::Color(TermColor::TC_DEFAULT) << std::flush; return true; } }