#include #include #include #include unsigned int Scene::m_idCounter = 0; namespace { class IterationGuard { public: explicit IterationGuard(Scene& scene) : m_Scene(scene) { m_Scene.BeginIteration(); } ~IterationGuard() { m_Scene.EndIteration(); } private: Scene& m_Scene; }; } Scene::Scene(const std::string& name) : Scene(name, std::make_unique()) { } Scene::Scene(const std::string& name, std::unique_ptr physicsWorld) : m_Physics(std::move(physicsWorld)), m_name(name), m_id(++m_idCounter) { } Scene::~Scene() { Unload(); RemoveAll(); } GameObject* Scene::CreateGameObject(std::string name) { auto object = std::make_shared(std::move(name)); GameObject* rawPtr = object.get(); object->SetScene(this); m_pendingAdditions.emplace_back(std::move(object)); return rawPtr; } void Scene::Remove(GameObject* object) { if (object == nullptr) { return; } const auto containsObject = [object](const auto& objects) { return std::any_of(objects.begin(), objects.end(), [object](const auto& candidate) { return candidate != nullptr && candidate.get() == object; }); }; if (!containsObject(m_objects) && !containsObject(m_pendingAdditions)) { return; } object->Destroy(); if (IsIterating()) { return; } const auto eraseDestroyed = [](auto& objects) { for (const auto& candidate : objects) { if (candidate != nullptr && candidate->IsBeingDestroyed()) { candidate->SetScene(nullptr); } } std::erase_if(objects, [](const auto& candidate) { return candidate == nullptr || candidate->IsBeingDestroyed(); }); }; // Destroying an object also marks its descendants. Remove all of those // marked entries together, after destruction has finished. eraseDestroyed(m_objects); eraseDestroyed(m_pendingAdditions); } void Scene::RemoveAll() { for (const auto& object : m_objects) { if (object != nullptr) { object->Destroy(); } } for (const auto& object : m_pendingAdditions) { if (object != nullptr) { object->Destroy(); } } if (IsIterating()) { return; } for (const auto& object : m_objects) { if (object != nullptr) { object->SetScene(nullptr); } } for (const auto& object : m_pendingAdditions) { if (object != nullptr) { object->SetScene(nullptr); } } m_objects.clear(); m_pendingAdditions.clear(); } void Scene::Load() { LoadBindings(); } void Scene::Update(float dt) { CommitPendingAdditions(); IterationGuard iteration(*this); for (const auto& object : m_objects) { if (object != nullptr && !object->IsBeingDestroyed() && object->IsActiveInHierarchy()) { object->Update(dt); } } } void Scene::FixedUpdate(float dt) { CommitPendingAdditions(); IterationGuard iteration(*this); for (const auto& object : m_objects) { if (object != nullptr && !object->IsBeingDestroyed() && object->IsActiveInHierarchy()) { object->FixedUpdate(dt); } } m_Physics.FixedUpdate(dt); } void Scene::LateUpdate(float dt) { IterationGuard iteration(*this); for (const auto& object : m_objects) { if (object != nullptr && !object->IsBeingDestroyed() && object->IsActiveInHierarchy()) { object->LateUpdate(dt); } } } void Scene::Render(const RenderContext& ctx) { IterationGuard iteration(*this); for (const auto& object : m_objects) { if (object != nullptr && !object->IsBeingDestroyed() && object->IsActiveInHierarchy()) { object->Render(ctx); } } } void Scene::RenderImgui() { ImGuiUtils::RenderSceneInspector( m_name, m_objects, m_pendingAdditions, m_SelectedObjectId); } void Scene::CleanupDestroyedGameObjects() { if (IsIterating()) { return; } const auto cleanup = [](auto& objects) { for (const auto& object : objects) { if (object != nullptr && object->IsBeingDestroyed()) { object->Destroy(); } } for (const auto& object : objects) { if (object != nullptr && !object->IsBeingDestroyed()) { object->CleanupComponents(); } } for (const auto& object : objects) { if (object != nullptr && object->IsBeingDestroyed()) { object->SetScene(nullptr); } } std::erase_if(objects, [](const auto& object) { return object == nullptr || object->IsBeingDestroyed(); }); }; cleanup(m_objects); cleanup(m_pendingAdditions); } void Scene::BeginIteration() { ++m_IterationDepth; } void Scene::EndIteration() { if (m_IterationDepth == 0) { return; } --m_IterationDepth; if (m_IterationDepth == 0) { CleanupDestroyedGameObjects(); } } void Scene::Unload() { if (m_BeingUnloaded) { return; } UnloadBindings(); m_BeingUnloaded = true; } void Scene::DestroyGameObjects() { for (const auto& object : m_objects) { if (object != nullptr) { object->Destroy(); } } for (const auto& object : m_pendingAdditions) { if (object != nullptr) { object->Destroy(); } } } void Scene::CommitPendingAdditions() { if (m_pendingAdditions.empty() || IsIterating()) { return; } for (auto& object : m_pendingAdditions) { if (object != nullptr && !object->IsBeingDestroyed()) { m_Physics.RegisterGameObject(*object); m_objects.emplace_back(std::move(object)); } } std::erase_if(m_pendingAdditions, [](const auto& object) { return object == nullptr || object->IsBeingDestroyed(); }); } void Scene::RefreshPhysics() { for (const auto& object : m_objects) { if (object != nullptr) { m_Physics.RefreshGameObject(*object); } } for (const auto& object : m_pendingAdditions) { if (object != nullptr) { m_Physics.RefreshGameObject(*object); } } }