Files
Destrum/destrum/src/Scene/Scene.cpp
T

274 lines
6.4 KiB
C++

#include <destrum/Scene/Scene.h>
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/Util/ImGuiUtils.h>
#include <algorithm>
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<JoltPhysicsWorld>()) {
}
Scene::Scene(const std::string& name, std::unique_ptr<PhysicsWorld> 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<GameObject>(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);
}
}
}