#include "EngineInit.h" #include "Core/Logger.h" #include "Core/GarbageCollection.h" #include "Core/ThreadSyncManager.h" #include "Core/ThreadPool.h" #include "Core/Factory.hpp" #include "Core/AssetResolver.h" #include "Window/Window.h" #include "Render/VulkanImpl/VulkanRenderer.h" #include "Sound/SoundSystem.h" #include "Game/ImGUImpl.h" #include "Game/RenderThread.h" #include "Game/Input.h" #include "Game/Component/Component.h" #include "Game/ComponentModule.h" #include "Game/GameManager.h" #include "Game/ScriptableObject.h" #if SH_EDITOR #include "Editor/EditorResource.h" #include "Editor/EditorWorld.h" #include "Editor/AssetDatabase.h" #include "Editor/UI/CustomInspector.h" #else #include "Core/AssetBundle.h" #include "Render/VulkanImpl/VulkanShaderPassBuilder.h" #include "Render/VulkanImpl/VulkanContext.h" #include "Game/World.h" #include "Game/AssetLoaderFactory.h" #include "Game/Asset/TextureLoader.h" #include "Game/Asset/ModelLoader.h" #include "Game/Asset/MeshLoader.h" #include "Game/Asset/MaterialLoader.h" #include "Game/Asset/ShaderLoader.h" #include "Game/Asset/WorldLoader.h" #include "Game/Asset/PrefabLoader.h" #include "Game/Asset/TextLoader.h" #include "Game/Asset/BinaryLoader.h" #include "Game/Asset/FontLoader.h" #include "Game/Asset/SoundLoader.h" #include "Game/Asset/ScriptableObjectLoader.h" #include "Game/Asset/ComputeShaderLoader.h" #include "Game/Asset/TextureAsset.h" #include "Game/Asset/MaterialAsset.h" #include "Game/Asset/ShaderAsset.h" #include "Game/Asset/ModelAsset.h" #include "Game/Asset/MeshAsset.h" #include "Game/Asset/WorldAsset.h" #include "Game/Asset/PrefabAsset.h" #include "Game/Asset/TextAsset.h" #include "Game/Asset/BinaryAsset.h" #include "Game/Asset/FontAsset.h" #include "Game/Asset/SoundAsset.h" #include "Game/Asset/ScriptableObjectAsset.h" #include "Game/Asset/ComputeShaderAsset.h" #endif namespace sh { EngineInit::EngineInit() : moduleLoader() { } EngineInit::~EngineInit() { Clean(); } void EngineInit::Clean() { SH_INFO("Engine shutdown"); gameManager->Destroy(); #if SH_EDITOR project.reset(); editor::AssetDatabase::Destroy(); editor::EditorResource::Destroy(); editor::CustomInspectorManager::Destroy(); #endif renderer->WaitForCurrentFrame(); while(gc->GetRootSetCount() != gc->GetObjectCount()) { gc->Collect(); gc->DestroyPendingKillObjs(); } gui.reset(); renderer.reset(); sound::SoundSystem::Destroy(); window.reset(); } inline void EngineInit::InitResource() { SH_INFO("Resource initialization"); #if SH_EDITOR editor::EditorResource::GetInstance()->LoadAllAssets(*project); #endif } void EngineInit::ProcessInput() { game::Input::Update(); sh::window::Event e; while (window->PollEvent(e)) { game::Input::UpdateEvent(e); gui->ProcessEvent(e); switch (e.type) { case sh::window::Event::EventType::Close: bStop = true; break; case sh::window::Event::EventType::Resize: if (window->GetWidth() == 0) { renderer->Pause(true); } else { renderer->Pause(false); renderThread->AddBeginTaskFromOtherThread( [renderer = renderer.get(), width = window->GetWidth(), height = window->GetHeight()] { renderer->GetContext()->SetViewport({ 0, 0.f }, { width, height }); } ); gui->Resize(); } break; case sh::window::Event::EventType::WindowFocus: SH_INFO("FocusIn"); break; case sh::window::Event::EventType::WindowFocusOut: SH_INFO("FocusOut"); sh::game::Input::ResetKeyState(); break; } } } void EngineInit::Start() { SH_INFO("Engine start"); game::ComponentModule* componentModule = game::ComponentModule::GetInstance(); componentModule->RegisterWaitingComponents(); SH_INFO_FORMAT("Init {} type", game::ScriptableObject::GetStaticType().name.ToString()); // 타입이 존재하는 곳을 메인 static 영역에 지정하기 위해.. SH_INFO_FORMAT("System thread count: {}", std::thread::hardware_concurrency()); SH_INFO("GarbageCollection initialization"); gc = core::GarbageCollection::GetInstance(); // GC 초기화 SH_INFO("Window initialization"); window = std::make_unique(); window->Create(u8"ShellEngine", 1024, 768, sh::window::Window::Style::Resize); window->SetFps(limitFps); SH_INFO("Renderer initialization"); renderer = std::make_unique(); renderer->CreateContext(*window); renderer->Init(*window); renderer->GetContext()->SetViewport({ 150.f, 0.f }, { window->GetWidth() - 150.f, window->GetHeight() - 180}); SH_INFO("SoundSystem initialization"); sound::SoundSystem::GetInstance()->Init(); SH_INFO("UIContext initialization"); gui = std::make_unique(*window, static_cast(*renderer)); gui->Init(); SH_INFO("GameManager initialization"); gameManager = game::GameManager::GetInstance(); gameManager->Init(*renderer, *gui); auto& worldFactory = *core::Factory::GetInstance(); worldFactory.Register(game::World::GetStaticType().name.ToString(), [renderer = renderer.get(), componentModule, gui = gui.get()]() -> game::World* { return core::SObject::Create(*renderer, *gui); } ); SH_INFO("Thread creation"); core::ThreadPool::GetInstance()->Init(std::max(2u, std::thread::hardware_concurrency() / 2)); core::ThreadSyncManager::Init(); #if SH_EDITOR game::Component::SetIsEditor(true); // 에셋이 다른 에셋을 필요로 하고 있지만 로드가 안 된 상태에서 호출될 것이다. // 두 에셋이 순환 참조를 하고 있을 때 무한 루프에 빠질 걱정은 할 필요 없다. // 한 에셋을 로드 할 때 UUID를 먼저 지정한 후에 resolver를 호출 하기 때문 core::AssetResolverRegistry::SetResolver( [this](const core::UUID& uuid) -> core::SObject* { SH_INFO_FORMAT("Using resolver: {}", uuid.ToString()); static auto& assetDatabase = *editor::AssetDatabase::GetInstance(); auto assetInfoPtr = assetDatabase.GetAssetPath(uuid); if (assetInfoPtr == nullptr) return nullptr; if (!assetInfoPtr->originalPath.empty()) return assetDatabase.ImportAsset(assetInfoPtr->originalPath); return assetDatabase.ImportAsset(assetInfoPtr->cachePath); } ); project = std::make_unique(*renderer, *gui); worldFactory.Register(editor::EditorWorld::GetStaticType().name.ToString(), [project = project.get()]() -> game::World* { return core::SObject::Create(*project); } ); auto defaultWorld = core::SObject::Create(*project); // 기본 월드 gameManager->LoadWorld(defaultWorld->GetUUID()); #else static render::vk::VulkanShaderPassBuilder vkShaderPassBuilder{ static_cast(*renderer->GetContext()) }; auto assetLoaderFactory = game::AssetLoaderFactory::GetInstance(); assetLoaderFactory->RegisterLoader(game::TextureAsset::ASSET_NAME, std::make_unique(*renderer->GetContext())); assetLoaderFactory->RegisterLoader(game::ModelAsset::ASSET_NAME, std::make_unique(*renderer->GetContext())); assetLoaderFactory->RegisterLoader(game::MeshAsset::ASSET_NAME, std::make_unique(*renderer->GetContext())); assetLoaderFactory->RegisterLoader(game::MaterialAsset::ASSET_NAME, std::make_unique(*renderer->GetContext())); assetLoaderFactory->RegisterLoader(game::ShaderAsset::ASSET_NAME, std::make_unique(&vkShaderPassBuilder)); assetLoaderFactory->RegisterLoader(game::WorldAsset::ASSET_NAME, std::make_unique()); assetLoaderFactory->RegisterLoader(game::PrefabAsset::ASSET_NAME, std::make_unique()); assetLoaderFactory->RegisterLoader(game::TextAsset::ASSET_NAME, std::make_unique()); assetLoaderFactory->RegisterLoader(game::BinaryAsset::ASSET_NAME, std::make_unique()); assetLoaderFactory->RegisterLoader(game::FontAsset::ASSET_NAME, std::make_unique()); assetLoaderFactory->RegisterLoader(game::SoundAsset::ASSET_NAME, std::make_unique()); assetLoaderFactory->RegisterLoader(game::ScriptableObjectAsset::ASSET_NAME, std::make_unique()); assetLoaderFactory->RegisterLoader(game::ComputeShaderAsset::ASSET_NAME, std::make_unique(*renderer->GetContext())); core::AssetResolverRegistry::SetResolver( [this](const core::UUID& uuid) -> core::SObject* { auto asset = assetBundle->LoadAsset(uuid); if (asset == nullptr) return nullptr; static game::AssetLoaderFactory& factory = *game::AssetLoaderFactory::GetInstance(); auto assetLoader = factory.GetLoader(asset->GetType()); if (assetLoader == nullptr) return nullptr; core::SObject* assetPtr = assetLoader->Load(*asset); return assetPtr; } ); assetBundle = std::make_unique(); if (!assetBundle->LoadBundle("assets.bundle")) return; if (!gameManager->LoadGame("gameManager.bin", *assetBundle)) return; #endif SH_INFO("Render thread creation"); renderThread = game::RenderThread::GetInstance(); renderThread->Init(*renderer); core::ThreadSyncManager::AddThread(*renderThread); InitResource(); renderThread->Run(); SH_INFO("Start loop"); Loop(); SH_INFO("End loop"); renderThread->Stop(); renderThread->GetThread().join(); } inline void EngineInit::Loop() { while (!bStop) { window->ProcessFrame(); std::string deltaTime = std::to_string(window->GetDeltaTime()); deltaTime.erase(deltaTime.begin() + 5, deltaTime.end()); window->SetTitle("ShellEngine [DeltaTime:" + deltaTime + "ms]"); ProcessInput(); if (bStop) return; gameManager->UpdateWorlds(window->GetDeltaTime()); // 스레드간 Sync도 여기서 이뤄짐 gc->Update(); core::ThreadSyncManager::AwakeThread(); } } }