274 lines
6.4 KiB
C++
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);
|
|
}
|
|
}
|
|
}
|