diff --git a/Dependencies/assimp/code/Common/scene.cpp b/Dependencies/assimp/code/Common/scene.cpp index 982b5194c..e0c5d2071 100644 --- a/Dependencies/assimp/code/Common/scene.cpp +++ b/Dependencies/assimp/code/Common/scene.cpp @@ -112,7 +112,8 @@ aiScene::~aiScene() { delete mCameras[a]; } } - delete[] mCameras; + delete[] mCameras;if (OvPhysics::Core::PhysicsEngine::IsDispatching()) + return; aiMetadata::Dealloc(mMetaData); diff --git a/Sources/OvCore/include/OvCore/ECS/Actor.h b/Sources/OvCore/include/OvCore/ECS/Actor.h index 7add0c76f..2ec6eb521 100644 --- a/Sources/OvCore/include/OvCore/ECS/Actor.h +++ b/Sources/OvCore/include/OvCore/ECS/Actor.h @@ -387,6 +387,7 @@ namespace OvCore::ECS void RecursiveActiveUpdate(); void RecursiveWasActiveUpdate(); + void ReleaseRemovedComponent(std::shared_ptr p_component); public: /* Some events that are triggered when an action occur on the actor instance */ diff --git a/Sources/OvCore/include/OvCore/ECS/Actor.inl b/Sources/OvCore/include/OvCore/ECS/Actor.inl index acfeb84aa..69e154aa8 100644 --- a/Sources/OvCore/include/OvCore/ECS/Actor.inl +++ b/Sources/OvCore/include/OvCore/ECS/Actor.inl @@ -49,6 +49,7 @@ namespace OvCore::ECS { ComponentRemovedEvent.Invoke(*result.get()); m_components.erase(it); + ReleaseRemovedComponent(std::move(result)); return true; } } diff --git a/Sources/OvCore/include/OvCore/ECS/Components/CPhysicalObject.h b/Sources/OvCore/include/OvCore/ECS/Components/CPhysicalObject.h index 1b38afa4f..780f0dede 100644 --- a/Sources/OvCore/include/OvCore/ECS/Components/CPhysicalObject.h +++ b/Sources/OvCore/include/OvCore/ECS/Components/CPhysicalObject.h @@ -163,6 +163,9 @@ namespace OvCore::ECS::Components * @param p_activationState */ void SetActivationState(OvPhysics::Entities::PhysicalObject::EActivationState p_activationState); + + /** the component(s) stops emitting events but stays alive */ + void MarkForRemoval() { m_pendingRemoval = true; } /** * Serialize the component @@ -204,6 +207,7 @@ namespace OvCore::ECS::Components protected: std::unique_ptr m_physicalObject; + bool m_pendingRemoval = false; }; template<> diff --git a/Sources/OvCore/src/OvCore/ECS/Actor.cpp b/Sources/OvCore/src/OvCore/ECS/Actor.cpp index 332b73688..27a929b6c 100644 --- a/Sources/OvCore/src/OvCore/ECS/Actor.cpp +++ b/Sources/OvCore/src/OvCore/ECS/Actor.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include @@ -110,6 +112,18 @@ void OvCore::ECS::Actor::SetActive(bool p_active) } } +void OvCore::ECS::Actor::ReleaseRemovedComponent(std::shared_ptr p_component) +{ + if (auto* physical = dynamic_cast(p_component.get())) + { + physical->MarkForRemoval(); + + if (OvPhysics::Core::PhysicsEngine::IsDispatching()) + OvPhysics::Core::PhysicsEngine::DeferDestruction(std::move(p_component)); + } + // otherwise p_component dies here, as before +} + bool OvCore::ECS::Actor::IsSelfActive() const { return m_active; @@ -390,8 +404,10 @@ bool OvCore::ECS::Actor::RemoveComponent(OvCore::ECS::Components::AComponent& p_ { if (it->get() == &p_component) { + auto removed = *it; ComponentRemovedEvent.Invoke(p_component); m_components.erase(it); + ReleaseRemovedComponent(std::move(removed)); return true; } } diff --git a/Sources/OvCore/src/OvCore/ECS/Components/CPhysicalObject.cpp b/Sources/OvCore/src/OvCore/ECS/Components/CPhysicalObject.cpp index b7762f275..89d2bad57 100644 --- a/Sources/OvCore/src/OvCore/ECS/Components/CPhysicalObject.cpp +++ b/Sources/OvCore/src/OvCore/ECS/Components/CPhysicalObject.cpp @@ -200,6 +200,7 @@ void OvCore::ECS::Components::CPhysicalObject::BindListener() /* Collision Events */ m_physicalObject->CollisionStartEvent += [this](OvPhysics::Entities::PhysicalObject& otherPhysicalObject) { + if (m_pendingRemoval) return; auto& otherObject = otherPhysicalObject.GetUserData>().get(); CollisionEnterEvent.Invoke(otherObject); @@ -207,6 +208,7 @@ void OvCore::ECS::Components::CPhysicalObject::BindListener() }; m_physicalObject->CollisionStayEvent += [this](OvPhysics::Entities::PhysicalObject& otherPhysicalObject) { + if (m_pendingRemoval) return; auto& otherObject = otherPhysicalObject.GetUserData>().get(); CollisionStayEvent.Invoke(otherObject); @@ -214,6 +216,7 @@ void OvCore::ECS::Components::CPhysicalObject::BindListener() }; m_physicalObject->CollisionStopEvent += [this](OvPhysics::Entities::PhysicalObject& otherPhysicalObject) { + if (m_pendingRemoval) return; auto& otherObject = otherPhysicalObject.GetUserData>().get(); CollisionExitEvent.Invoke(otherObject); @@ -223,6 +226,7 @@ void OvCore::ECS::Components::CPhysicalObject::BindListener() /* Trigger Events */ m_physicalObject->TriggerStartEvent += [this](OvPhysics::Entities::PhysicalObject& otherPhysicalObject) { + if (m_pendingRemoval) return; auto& otherObject = otherPhysicalObject.GetUserData>().get(); TriggerEnterEvent.Invoke(otherObject); @@ -230,6 +234,7 @@ void OvCore::ECS::Components::CPhysicalObject::BindListener() }; m_physicalObject->TriggerStayEvent += [this](OvPhysics::Entities::PhysicalObject& otherPhysicalObject) { + if (m_pendingRemoval) return; auto& otherObject = otherPhysicalObject.GetUserData>().get(); TriggerStayEvent.Invoke(otherObject); @@ -237,6 +242,7 @@ void OvCore::ECS::Components::CPhysicalObject::BindListener() }; m_physicalObject->TriggerStopEvent += [this](OvPhysics::Entities::PhysicalObject& otherPhysicalObject) { + if (m_pendingRemoval) return; auto& otherObject = otherPhysicalObject.GetUserData>().get(); TriggerExitEvent.Invoke(otherObject); diff --git a/Sources/OvCore/src/OvCore/SceneSystem/Scene.cpp b/Sources/OvCore/src/OvCore/SceneSystem/Scene.cpp index cc5c5cd8d..10b2d8eb0 100644 --- a/Sources/OvCore/src/OvCore/SceneSystem/Scene.cpp +++ b/Sources/OvCore/src/OvCore/SceneSystem/Scene.cpp @@ -22,6 +22,7 @@ #include #include #include +#include OvCore::SceneSystem::Scene::Scene( const std::filesystem::path& p_projectAssetsPath, @@ -36,7 +37,7 @@ OvCore::SceneSystem::Scene::Scene( OvCore::SceneSystem::Scene::~Scene() { std::for_each(m_actors.begin(), m_actors.end(), [](OvCore::ECS::Actor* element) - { + { delete element; }); @@ -81,7 +82,7 @@ void OvCore::SceneSystem::Scene::AddDefaultSkysphere() auto skysphereMaterial = Global::ServiceLocator::Get().GetResource(":Materials\\Skysphere.ovmat"); auto sphereModel = Global::ServiceLocator::Get().GetResource(":Models\\Sphere.fbx"); - + if (skysphereMaterial) { materialRenderer.SetMaterialAtIndex(0, *skysphereMaterial); @@ -315,6 +316,12 @@ bool OvCore::SceneSystem::Scene::DestroyActor(ECS::Actor& p_target) if (found != m_actors.end()) { + if (OvPhysics::Core::PhysicsEngine::IsDispatching()) + { + // deleting now wood destroy physical components inside Bullet step. + p_target.MarkAsDestroy(); + return true; + } delete *found; m_actors.erase(found); return true; @@ -337,8 +344,10 @@ void OvCore::SceneSystem::Scene::MoveActorToBack(ECS::Actor& p_actor) void OvCore::SceneSystem::Scene::CollectGarbages() { + if (OvPhysics::Core::PhysicsEngine::IsDispatching()) + return; m_actors.erase(std::remove_if(m_actors.begin(), m_actors.end(), [this](ECS::Actor* element) - { + { bool isGarbage = !element->IsAlive(); if (isGarbage) { @@ -351,7 +360,7 @@ void OvCore::SceneSystem::Scene::CollectGarbages() OvCore::ECS::Actor* OvCore::SceneSystem::Scene::FindActorByName(const std::string& p_name) const { auto result = std::find_if(m_actors.begin(), m_actors.end(), [p_name](OvCore::ECS::Actor* element) - { + { return element->GetName() == p_name; }); @@ -526,4 +535,4 @@ void OvCore::SceneSystem::Scene::OnDeserialize(tinyxml2::XMLDocument & p_doc, ti } } } -} \ No newline at end of file +} diff --git a/Sources/OvEditor/src/OvEditor/Panels/Inspector.cpp b/Sources/OvEditor/src/OvEditor/Panels/Inspector.cpp index 09273b619..e2526ed94 100644 --- a/Sources/OvEditor/src/OvEditor/Panels/Inspector.cpp +++ b/Sources/OvEditor/src/OvEditor/Panels/Inspector.cpp @@ -142,7 +142,7 @@ void OvEditor::Panels::Inspector::FocusActor(Actor& p_target) m_componentAddedListener = m_targetActor->ComponentAddedEvent += [this] (auto&) { EDITOR_EXEC(DelayAction([this] { Refresh(); })); }; m_behaviourAddedListener = m_targetActor->BehaviourAddedEvent += [this](auto&) { EDITOR_EXEC(DelayAction([this] { Refresh(); })); }; - m_componentRemovedListener = m_targetActor->ComponentRemovedEvent += [this](auto&) { EDITOR_EXEC(DelayAction([this] { Refresh(); })); }; + m_componentRemovedListener = m_targetActor->ComponentRemovedEvent += [this](auto&) { EDITOR_EXEC(DelayAction([this] { Refresh(); }, 0)); }; m_behaviourRemovedListener = m_targetActor->BehaviourRemovedEvent += [this](auto&) { EDITOR_EXEC(DelayAction([this] { Refresh(); })); }; _Populate(); diff --git a/Sources/OvPhysics/include/OvPhysics/Core/PhysicsEngine.h b/Sources/OvPhysics/include/OvPhysics/Core/PhysicsEngine.h index 3f48d6e31..68cb1abd4 100644 --- a/Sources/OvPhysics/include/OvPhysics/Core/PhysicsEngine.h +++ b/Sources/OvPhysics/include/OvPhysics/Core/PhysicsEngine.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -68,6 +69,11 @@ namespace OvPhysics::Core * Returns the current world gravity */ OvMaths::FVector3 GetGravity() const; + /** True while the simulation step and collision stop dispatch are running */ + static bool IsDispatching(); + + /** keeps an object alive until the current physics update finished */ + static void DeferDestruction(std::shared_ptr p_object); private: void PreUpdate(); @@ -96,6 +102,9 @@ namespace OvPhysics::Core std::unique_ptr m_solver; static std::map, bool> m_collisionEvents; + static void FlushGraveyard(); + static bool s_dispatching; + static std::vector> s_graveyard; std::vector> m_physicalObjects; }; } diff --git a/Sources/OvPhysics/src/OvPhysics/Core/PhysicsEngine.cpp b/Sources/OvPhysics/src/OvPhysics/Core/PhysicsEngine.cpp index 494d0e742..99c9a0ef9 100644 --- a/Sources/OvPhysics/src/OvPhysics/Core/PhysicsEngine.cpp +++ b/Sources/OvPhysics/src/OvPhysics/Core/PhysicsEngine.cpp @@ -20,6 +20,8 @@ using namespace OvPhysics::Tools; using namespace OvPhysics::Entities; std::map, bool> OvPhysics::Core::PhysicsEngine::m_collisionEvents; +bool OvPhysics::Core::PhysicsEngine::s_dispatching = false; +std::vector> OvPhysics::Core::PhysicsEngine::s_graveyard; OvPhysics::Core::PhysicsEngine::PhysicsEngine(const Settings::PhysicsSettings & p_settings) { @@ -37,6 +39,8 @@ OvPhysics::Core::PhysicsEngine::PhysicsEngine(const Settings::PhysicsSettings & OvPhysics::Core::PhysicsEngine::~PhysicsEngine() { + // release physical object(s) whose destruction was deferred during the last update, while the engine and the physics event(s) are still alive. + FlushGraveyard(); } @@ -56,15 +60,43 @@ void OvPhysics::Core::PhysicsEngine::PostUpdate() bool OvPhysics::Core::PhysicsEngine::Update(float p_deltaTime) { - PreUpdate(); + FlushGraveyard(); //release objects removed during the previous update... - if (m_world->stepSimulation(p_deltaTime, 10)) + bool simulated = false; { - PostUpdate(); - return true; + struct DispatchScope + { + DispatchScope() { s_dispatching = true; } + ~DispatchScope() { s_dispatching = false; } + } scope; + + PreUpdate(); + + if (m_world->stepSimulation(p_deltaTime, 10)) + { + PostUpdate(); + simulated = true; + } } - return false; + return simulated; // no flush here +} + +bool OvPhysics::Core::PhysicsEngine::IsDispatching() +{ + return s_dispatching; +} + +void OvPhysics::Core::PhysicsEngine::DeferDestruction(std::shared_ptr p_object) +{ + s_graveyard.push_back(std::move(p_object)); +} + +void OvPhysics::Core::PhysicsEngine::FlushGraveyard() +{ + auto dead = std::move(s_graveyard); + s_graveyard.clear(); + dead.clear(); } std::optional OvPhysics::Core::PhysicsEngine::Raycast(OvMaths::FVector3 p_origin, OvMaths::FVector3 p_direction, float p_distance)