#include "Engine.h" #include "Nuake/Scene/Scene.h" #include "Nuake/Resource/Project.h" #include "Nuake/Physics/PhysicsManager.h" #include "Nuake/AI/NavManager.h" #include "Nuake/FileSystem/FileSystem.h" #include "Nuake/Core/Input.h" #include "Nuake/Rendering/Renderer.h" #include "Nuake/Rendering/Renderer2D.h" #include "Nuake/Scripting/ScriptingEngineNet.h" #include "Nuake/Threading/JobSystem.h" #include "Nuake/Core/RegisterCoreTypes.h" #include "Nuake/Modules/Modules.h" #include "Nuake/Modules/ModuleDB.h" #include "Nuake/Subsystems/EngineSubsystemScriptable.h" #include #include #include #include #include #ifdef NK_VK #include "Nuake/Rendering/Vulkan/VulkanRenderer.h" #endif #include #include "Nuake/Resource/Resolvers/MeshResolver.h" namespace Nuake { Ref Engine::currentProject; Ref Engine::currentWindow; std::string Engine::queuedScene = ""; GameState Engine::gameState = GameState::Stopped; float Engine::lastFrameTime = 0.0f; float Engine::fixedUpdateRate = 1.0f / 90.0f; float Engine::fixedUpdateDifference = 0.f; float Engine::time = 0.f; Timestep Engine::timeStep = 0.f; float Engine::timeScale = 1.0f; MulticastDelegate> Engine::OnSceneLoaded; void Engine::Init() { //Window::Get()->OnWindowSetScene().AddStatic(&Engine::OnWindowSetScene); PhysicsManager::Get().Init(); NavManager::Get().Initialize(); #ifdef NK_VK VkRenderer::Get().Initialize(); #endif // Creates the window currentWindow = Window::Get(); Input::Init(); Logger::Log("Engine initialized"); RegisterCoreTypes::RegisterCoreComponents(); Modules::StartupModules(); ScriptingEngineNet::Get().OnGameAssemblyLoaded().AddStatic(&Engine::OnScriptingEngineGameAssemblyLoaded); // Writting bindings.json Logger::Log("Exporting bindings.json", "modules", VERBOSE); auto api = ModuleDB::Get().GenerateModuleAPI(); FileSystem::BeginWriteFile("bindings.json", true); FileSystem::WriteLine(api.dump(4)); FileSystem::EndWriteFile(); InitializeCoreSubsystems(); // Init base resolvers auto& resolvers = ResourceResolverManager::Get(); resolvers.RegisterResolver(CreateRef()); resolvers.RegisterResolver(CreateRef()); } void Engine::Tick() { ZoneScoped; JobSystem::Get().Update(); time = static_cast(glfwGetTime()); timeStep = time - lastFrameTime; lastFrameTime = time; if (Engine::IsPlayMode()) { if (!queuedScene.empty()) { Ref nextScene = Scene::New(); if (FileSystem::FileExists(queuedScene)) { const std::string& fileContent = FileSystem::ReadFile(queuedScene); nextScene->Path = queuedScene; nextScene->Deserialize(json::parse(fileContent)); // Uninit current scene GetCurrentScene()->OnExit(); // Set new scene SetCurrentScene(nextScene); // Init new scene PhysicsManager::Get().ReInit(); GetCurrentScene()->OnInit(); queuedScene = ""; } } } float scaledTimeStep = timeStep * timeScale; // Tick all subsystems if (Engine::IsPlayMode()) { for (auto subsystem : subsystems) { if (subsystem == nullptr) continue; if (subsystem->CanEverTick()) { subsystem->Tick(scaledTimeStep); } } } // Dont update if no scene is loaded. if (currentWindow->GetScene()) { currentWindow->Update(scaledTimeStep); // Play mode update all the entities, Editor does not. if (!Engine::IsPlayMode()) { GetCurrentScene()->EditorUpdate(scaledTimeStep); } fixedUpdateDifference += timeStep; // Fixed update while (fixedUpdateDifference >= fixedUpdateRate) { const float scaledFixedTimestep = fixedUpdateRate * timeScale; currentWindow->FixedUpdate(scaledFixedTimestep); ModuleDB::Get().FixedUpdate(scaledFixedTimestep); fixedUpdateDifference -= fixedUpdateRate; } ModuleDB::Get().Update(scaledTimeStep); Input::Update(); } } void Engine::EnterPlayMode() { lastFrameTime = (float)glfwGetTime(); // Reset timestep timer. // Dont trigger init if already in player mode. if (GetGameState() == GameState::Playing || GetGameState() == GameState::Loading) { Logger::Log("Cannot enter play mode if is already in play mode or is loading.", "engine", WARNING); return; } SetGameState(GameState::Loading); PhysicsManager::Get().ReInit(); if (GetCurrentScene()->OnInit()) { SetGameState(GameState::Playing); } else { Logger::Log("Cannot enter play mode. Scene OnInit failed", "engine", CRITICAL); GetCurrentScene()->OnExit(); } } void Engine::ExitPlayMode() { // Dont trigger exit if already not in play mode. if (gameState != GameState::Stopped) { GetCurrentScene()->OnExit(); Input::ShowMouse(); SetGameState(GameState::Stopped); } } void Engine::Draw() { ZoneScoped; ImGui_ImplVulkan_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); //ImGui::ShowDemoWindow(); RenderCommand::Clear(); // Start imgui frame { ZoneScopedN("ImGui New Frame"); //ImGui_ImplOpenGL3_NewFrame(); //ImGui_ImplGlfw_NewFrame(); // ImGui_ImplVulkan_NewFrame(); //ImGui_ImplGlfw_NewFrame(); //ImGui::NewFrame(); } // Draw scene Window::Get()->Draw(); } void Engine::EndDraw() { ZoneScoped; Window::Get()->EndDraw(); } void Engine::SetGameState(GameState state) { gameState = state; ModuleDB::Get().OnGameStateChanged(state); } void Engine::Close() { glfwTerminate(); } Ref Engine::GetCurrentScene() { if (currentWindow) { return currentWindow->GetScene(); } return nullptr; } bool Engine::SetCurrentScene(Ref scene) { bool result = currentWindow->SetScene(scene); if (result) { OnSceneLoaded.Broadcast(scene); ModuleDB::Get().OnSceneLoaded(scene); } return result; } bool Engine::QueueSceneSwitch(const std::string& scenePath) { if (!IsPlayMode()) { return false; } queuedScene = scenePath; return true; } Ref Engine::GetProject() { return currentProject; } Ref Engine::GetScriptedSubsystem(const std::string& subsystemName) { if (scriptedSubsystemMap.contains(subsystemName)) { return scriptedSubsystemMap[subsystemName]; } return nullptr; } Ref Engine::GetScriptedSubsystem(const int subsystemId) { if (subsystemId >= subsystems.size()) { return nullptr; } return std::reinterpret_pointer_cast(subsystems[subsystemId]); } void Engine::OnWindowSetScene(Ref oldScene, Ref newScene) { // Inform the subsystems that we are going to destroy/swap out the old scene for (auto subsystem : subsystems) { if (subsystem == nullptr) continue; subsystem->OnScenePreDestroy(oldScene); } // Hook into when the internal pieces of the scene are just about to be ready and when the scene is finally // ready to present (ie, all initialized and loaded). if (newScene != nullptr) { newScene->OnPreInitialize().AddStatic(&Engine::OnScenePreInitialize, newScene); newScene->OnPostInitialize().AddStatic(&Engine::OnScenePostInitialize, newScene); } } void Engine::InitializeCoreSubsystems() { } void Engine::OnScriptingEngineGameAssemblyLoaded() { if (!Engine::IsPlayMode() && Engine::GetGameState() != GameState::Loading) { return; } subsystems.clear(); scriptedSubsystemMap.clear(); const Coral::ManagedAssembly& gameAssembly = ScriptingEngineNet::Get().GetGameAssembly(); const auto scriptTypeEngineSubsystem = gameAssembly.GetType("Nuake.Net.EngineSubsystem"); const auto& types = gameAssembly.GetTypes(); for (const auto& type : types) { // Initialize all subsystems if (type->IsSubclassOf(scriptTypeEngineSubsystem)) { const std::string typeName = std::string(type->GetFullName()); Logger::Log("Creating Scripted Subsystem " + typeName); Coral::ManagedObject scriptedSubsystem = type->CreateInstance(); scriptedSubsystem.SetPropertyValue("EngineSubsystemID", subsystems.size()); Ref subsystemScript = CreateRef(scriptedSubsystem); subsystems.push_back(subsystemScript); scriptedSubsystemMap[typeName] = subsystemScript; subsystemScript->Initialize(); } } } void Engine::OnScenePreInitialize(Ref scene) { for (auto subsystem : subsystems) { if (subsystem == nullptr) continue; subsystem->OnScenePreInitialize(scene); } } void Engine::OnScenePostInitialize(Ref scene) { for (auto subsystem : subsystems) { if (subsystem == nullptr) continue; subsystem->OnScenePostInitialize(scene); } } bool Engine::LoadProject(Ref project) { currentProject = project; // TODO(antopilo) only generate manifest in editor context FileSystem::SetRootDirectory(FileSystem::GetParentPath(project->FullPath)); GenerateManifest(); if (!Engine::SetCurrentScene(currentProject->DefaultScene)) { return false; } ScriptingEngineNet::Get().Initialize(); ScriptingEngineNet::Get().LoadProjectAssembly(project); return true; } Ref Engine::GetCurrentWindow() { return currentWindow; } void Engine::GenerateManifest() { Logger::Log("Generating project manifest", "manifest", VERBOSE); // This will auto generate a manifest entry for all assets in the project ResourceManifest& manifest = ResourceManager::Manifest; ResourceResolverManager& resolverManager = ResourceResolverManager::Get(); OnFileFunc onFileScanFunc = [&resolverManager, &manifest](Ref file) { if (!resolverManager.IsFileTypeResolvable(file->GetExtension())) { return; // We don't care about this file type, we dont support it. } // Register the resource in the manifest const std::string& path = file->GetRelativePath(); const UUID uuid = resolverManager.ResolveUUID(file); manifest.RegisterResource(uuid, path); }; FileSystem::ForeachFile(onFileScanFunc); } }