diff --git a/include/Nazara/ChipmunkPhysics2D/ChipmunkPhysWorld2D.hpp b/include/Nazara/ChipmunkPhysics2D/ChipmunkPhysWorld2D.hpp index 923b38a5d..bac523d5b 100644 --- a/include/Nazara/ChipmunkPhysics2D/ChipmunkPhysWorld2D.hpp +++ b/include/Nazara/ChipmunkPhysics2D/ChipmunkPhysWorld2D.hpp @@ -46,7 +46,7 @@ namespace Nz using DebugDrawGetColorCallback = std::function; public: - struct Callback; + struct ContactCallbacks; struct DebugDrawOptions; struct NearestQueryResult; struct RaycastHit; @@ -75,8 +75,8 @@ namespace Nz void RegionQuery(const Rectf& boundingBox, UInt32 collisionGroup, UInt32 categoryMask, UInt32 collisionMask, const FunctionRef& callback); void RegionQuery(const Rectf& boundingBox, UInt32 collisionGroup, UInt32 categoryMask, UInt32 collisionMask, std::vector* bodies); - void RegisterCallbacks(unsigned int collisionId, Callback callbacks); - void RegisterCallbacks(unsigned int collisionIdA, unsigned int collisionIdB, Callback callbacks); + void RegisterCallbacks(unsigned int collisionId, ContactCallbacks callbacks); + void RegisterCallbacks(unsigned int collisionIdA, unsigned int collisionIdB, ContactCallbacks callbacks); void SetDamping(float dampingValue); void SetGravity(const Vector2f& gravity); @@ -90,13 +90,13 @@ namespace Nz void UseSpatialHash(float cellSize, std::size_t entityCount); ChipmunkPhysWorld2D& operator=(const ChipmunkPhysWorld2D&) = delete; - ChipmunkPhysWorld2D& operator=(ChipmunkPhysWorld2D&&) = delete; ///TODO + ChipmunkPhysWorld2D& operator=(ChipmunkPhysWorld2D&&) = delete; - struct Callback + struct ContactCallbacks { ContactEndCallback endCallback = nullptr; - ContactPreSolveCallback preSolveCallback = nullptr; ContactPostSolveCallback postSolveCallback = nullptr; + ContactPreSolveCallback preSolveCallback = nullptr; ContactStartCallback startCallback = nullptr; void* userdata = nullptr; }; @@ -142,13 +142,13 @@ namespace Nz static constexpr std::size_t FreeBodyIdGrowRate = 256; void DeferBodyAction(ChipmunkRigidBody2D& rigidBody, PostStep&& func); - void InitCallbacks(cpCollisionHandler* handler, Callback callbacks); + void InitCallbacks(cpCollisionHandler* handler, ContactCallbacks callbacks); inline UInt32 RegisterBody(ChipmunkRigidBody2D& rigidBody); inline void UnregisterBody(UInt32 bodyIndex); inline void UpdateBodyPointer(ChipmunkRigidBody2D& rigidBody); std::size_t m_maxStepCount; - std::unordered_map> m_callbacks; + std::unordered_map> m_callbacks; std::unordered_map> m_rigidBodyPostSteps; std::vector m_bodies; cpSpace* m_handle; diff --git a/include/Nazara/ChipmunkPhysics2D/Systems/ChipmunkPhysics2DSystem.hpp b/include/Nazara/ChipmunkPhysics2D/Systems/ChipmunkPhysics2DSystem.hpp index c9f5256e7..1f6456c35 100644 --- a/include/Nazara/ChipmunkPhysics2D/Systems/ChipmunkPhysics2DSystem.hpp +++ b/include/Nazara/ChipmunkPhysics2D/Systems/ChipmunkPhysics2DSystem.hpp @@ -18,10 +18,19 @@ namespace Nz { class NAZARA_CHIPMUNKPHYSICS2D_API ChipmunkPhysics2DSystem { + using ContactEndCallback = std::function; + using ContactPostSolveCallback = std::function; + using ContactPreSolveCallback = std::function; + using ContactStartCallback = std::function; + public: static constexpr Int64 ExecutionOrder = 0; using Components = TypeList; + struct ContactCallbacks; + struct NearestQueryResult; + struct RaycastHit; + ChipmunkPhysics2DSystem(entt::registry& registry); ChipmunkPhysics2DSystem(const ChipmunkPhysics2DSystem&) = delete; ChipmunkPhysics2DSystem(ChipmunkPhysics2DSystem&&) = delete; @@ -29,18 +38,55 @@ namespace Nz inline ChipmunkPhysWorld2D& GetPhysWorld(); inline const ChipmunkPhysWorld2D& GetPhysWorld() const; + inline entt::handle GetRigidBodyEntity(UInt32 bodyIndex) const; + + inline bool NearestBodyQuery(const Vector2f& from, float maxDistance, UInt32 collisionGroup, UInt32 categoryMask, UInt32 collisionMask, entt::handle* nearestEntity = nullptr); + inline bool NearestBodyQuery(const Vector2f& from, float maxDistance, UInt32 collisionGroup, UInt32 categoryMask, UInt32 collisionMask, NearestQueryResult* result); + + inline void RaycastQuery(const Vector2f& from, const Vector2f& to, float radius, UInt32 collisionGroup, UInt32 categoryMask, UInt32 collisionMask, const FunctionRef& callback); + inline bool RaycastQuery(const Vector2f& from, const Vector2f& to, float radius, UInt32 collisionGroup, UInt32 categoryMask, UInt32 collisionMask, std::vector* hitInfos); + inline bool RaycastQueryFirst(const Vector2f& from, const Vector2f& to, float radius, UInt32 collisionGroup, UInt32 categoryMask, UInt32 collisionMask, RaycastHit* hitInfo = nullptr); + + inline void RegionQuery(const Rectf& boundingBox, UInt32 collisionGroup, UInt32 categoryMask, UInt32 collisionMask, const FunctionRef& callback); + inline void RegionQuery(const Rectf& boundingBox, UInt32 collisionGroup, UInt32 categoryMask, UInt32 collisionMask, std::vector* bodies); + + inline void RegisterCallbacks(unsigned int collisionId, ContactCallbacks callbacks); + inline void RegisterCallbacks(unsigned int collisionIdA, unsigned int collisionIdB, ContactCallbacks callbacks); void Update(Time elapsedTime); ChipmunkPhysics2DSystem& operator=(const ChipmunkPhysics2DSystem&) = delete; ChipmunkPhysics2DSystem& operator=(ChipmunkPhysics2DSystem&&) = delete; + struct ContactCallbacks + { + ContactEndCallback endCallback = nullptr; + ContactPostSolveCallback postSolveCallback = nullptr; + ContactPreSolveCallback preSolveCallback = nullptr; + ContactStartCallback startCallback = nullptr; + void* userdata = nullptr; + }; + + struct NearestQueryResult : ChipmunkPhysWorld2D::NearestQueryResult + { + entt::handle nearestEntity; + }; + + struct RaycastHit : ChipmunkPhysWorld2D::RaycastHit + { + entt::handle nearestEntity; + }; + private: void OnBodyConstruct(entt::registry& registry, entt::entity entity); + void OnBodyDestruct(entt::registry& registry, entt::entity entity); + ChipmunkPhysWorld2D::ContactCallbacks SetupContactCallbacks(ContactCallbacks callbacks); + std::vector m_bodyIndicesToEntity; entt::registry& m_registry; entt::observer m_physicsConstructObserver; entt::scoped_connection m_bodyConstructConnection; + entt::scoped_connection m_bodyDestructConnection; ChipmunkPhysWorld2D m_physWorld; }; } diff --git a/include/Nazara/ChipmunkPhysics2D/Systems/ChipmunkPhysics2DSystem.inl b/include/Nazara/ChipmunkPhysics2D/Systems/ChipmunkPhysics2DSystem.inl index 0cad5bd27..1fb2c9be7 100644 --- a/include/Nazara/ChipmunkPhysics2D/Systems/ChipmunkPhysics2DSystem.inl +++ b/include/Nazara/ChipmunkPhysics2D/Systems/ChipmunkPhysics2DSystem.inl @@ -15,6 +15,117 @@ namespace Nz { return m_physWorld; } + + inline entt::handle ChipmunkPhysics2DSystem::GetRigidBodyEntity(UInt32 bodyIndex) const + { + return entt::handle(m_registry, m_bodyIndicesToEntity[bodyIndex]); + } + + inline bool ChipmunkPhysics2DSystem::NearestBodyQuery(const Vector2f& from, float maxDistance, UInt32 collisionGroup, UInt32 categoryMask, UInt32 collisionMask, entt::handle* nearestEntity) + { + ChipmunkRigidBody2D* nearestBody; + if (!m_physWorld.NearestBodyQuery(from, maxDistance, collisionGroup, categoryMask, collisionMask, &nearestBody)) + return false; + + if (nearestEntity) + { + if (nearestBody) + *nearestEntity = GetRigidBodyEntity(nearestBody->GetBodyIndex()); + else + *nearestEntity = {}; + } + + return true; + } + + inline bool ChipmunkPhysics2DSystem::NearestBodyQuery(const Vector2f& from, float maxDistance, UInt32 collisionGroup, UInt32 categoryMask, UInt32 collisionMask, NearestQueryResult* result) + { + if (!m_physWorld.NearestBodyQuery(from, maxDistance, collisionGroup, categoryMask, collisionMask, result)) + return false; + + if (result) + { + if (result->nearestBody) + result->nearestEntity = GetRigidBodyEntity(result->nearestBody->GetBodyIndex()); + else + result->nearestEntity = {}; + } + + return true; + } + + inline void ChipmunkPhysics2DSystem::RaycastQuery(const Vector2f& from, const Vector2f& to, float radius, UInt32 collisionGroup, UInt32 categoryMask, UInt32 collisionMask, const FunctionRef& callback) + { + return m_physWorld.RaycastQuery(from, to, radius, collisionGroup, categoryMask, collisionMask, [&](const ChipmunkPhysWorld2D::RaycastHit& hitInfo) + { + RaycastHit extendedHitInfo; + static_cast(extendedHitInfo) = hitInfo; + + if (extendedHitInfo.nearestBody) + extendedHitInfo.nearestEntity = GetRigidBodyEntity(extendedHitInfo.nearestBody->GetBodyIndex()); + + callback(extendedHitInfo); + }); + } + + inline bool ChipmunkPhysics2DSystem::RaycastQuery(const Vector2f& from, const Vector2f& to, float radius, UInt32 collisionGroup, UInt32 categoryMask, UInt32 collisionMask, std::vector* hitInfos) + { + NazaraAssert(hitInfos, "invalid output pointer"); + + std::size_t originalSize = hitInfos->size(); + + RaycastQuery(from, to, radius, collisionGroup, categoryMask, collisionMask, [&](const RaycastHit& hitInfo) + { + hitInfos->emplace_back(hitInfo); + }); + + return hitInfos->size() != originalSize; + } + + inline bool ChipmunkPhysics2DSystem::RaycastQueryFirst(const Vector2f& from, const Vector2f& to, float radius, UInt32 collisionGroup, UInt32 categoryMask, UInt32 collisionMask, RaycastHit* hitInfo) + { + if (!m_physWorld.RaycastQueryFirst(from, to, radius, collisionGroup, categoryMask, collisionMask, hitInfo)) + return false; + + if (hitInfo) + { + if (hitInfo->nearestBody) + hitInfo->nearestEntity = GetRigidBodyEntity(hitInfo->nearestBody->GetBodyIndex()); + else + hitInfo->nearestEntity = {}; + } + + return true; + } + + inline void ChipmunkPhysics2DSystem::RegionQuery(const Rectf& boundingBox, UInt32 collisionGroup, UInt32 categoryMask, UInt32 collisionMask, const FunctionRef& callback) + { + return m_physWorld.RegionQuery(boundingBox, collisionGroup, categoryMask, collisionMask, [&](ChipmunkRigidBody2D* body) + { + callback(GetRigidBodyEntity(body->GetBodyIndex())); + }); + } + + inline void ChipmunkPhysics2DSystem::RegionQuery(const Rectf& boundingBox, UInt32 collisionGroup, UInt32 categoryMask, UInt32 collisionMask, std::vector* bodies) + { + NazaraAssert(bodies, "invalid output pointer"); + + return m_physWorld.RegionQuery(boundingBox, collisionGroup, categoryMask, collisionMask, [&](ChipmunkRigidBody2D* body) + { + bodies->emplace_back(GetRigidBodyEntity(body->GetBodyIndex())); + }); + } + + inline void ChipmunkPhysics2DSystem::RegisterCallbacks(unsigned int collisionId, ContactCallbacks callbacks) + { + return m_physWorld.RegisterCallbacks(collisionId, SetupContactCallbacks(std::move(callbacks))); + } + + inline void ChipmunkPhysics2DSystem::RegisterCallbacks(unsigned int collisionIdA, unsigned int collisionIdB, ContactCallbacks callbacks) + { + return m_physWorld.RegisterCallbacks(collisionIdA, collisionIdB, SetupContactCallbacks(std::move(callbacks))); + } } #include + diff --git a/src/Nazara/ChipmunkPhysics2D/ChipmunkPhysWorld2D.cpp b/src/Nazara/ChipmunkPhysics2D/ChipmunkPhysWorld2D.cpp index fb765016d..8bf4da8e8 100644 --- a/src/Nazara/ChipmunkPhysics2D/ChipmunkPhysWorld2D.cpp +++ b/src/Nazara/ChipmunkPhysics2D/ChipmunkPhysWorld2D.cpp @@ -309,12 +309,12 @@ namespace Nz cpSpaceBBQuery(m_handle, cpBBNew(boundingBox.x, boundingBox.y, boundingBox.x + boundingBox.width, boundingBox.y + boundingBox.height), filter, callback, bodies); } - void ChipmunkPhysWorld2D::RegisterCallbacks(unsigned int collisionId, Callback callbacks) + void ChipmunkPhysWorld2D::RegisterCallbacks(unsigned int collisionId, ContactCallbacks callbacks) { InitCallbacks(cpSpaceAddWildcardHandler(m_handle, collisionId), std::move(callbacks)); } - void ChipmunkPhysWorld2D::RegisterCallbacks(unsigned int collisionIdA, unsigned int collisionIdB, Callback callbacks) + void ChipmunkPhysWorld2D::RegisterCallbacks(unsigned int collisionIdA, unsigned int collisionIdB, ContactCallbacks callbacks) { InitCallbacks(cpSpaceAddCollisionHandler(m_handle, collisionIdA, collisionIdB), std::move(callbacks)); } @@ -390,15 +390,15 @@ namespace Nz cpSpaceUseSpatialHash(m_handle, cpFloat(cellSize), int(entityCount)); } - void ChipmunkPhysWorld2D::InitCallbacks(cpCollisionHandler* handler, Callback callbacks) + void ChipmunkPhysWorld2D::InitCallbacks(cpCollisionHandler* handler, ContactCallbacks callbacks) { auto it = m_callbacks.find(handler); if (it == m_callbacks.end()) - it = m_callbacks.emplace(handler, std::make_unique(std::move(callbacks))).first; + it = m_callbacks.emplace(handler, std::make_unique(std::move(callbacks))).first; else - it->second = std::make_unique(std::move(callbacks)); + it->second = std::make_unique(std::move(callbacks)); - Callback* callbackFunctions = it->second.get(); + ContactCallbacks* callbackFunctions = it->second.get(); handler->userData = callbackFunctions; if (callbackFunctions->startCallback) @@ -415,7 +415,7 @@ namespace Nz ChipmunkArbiter2D arbiter(arb); - const Callback* customCallbacks = static_cast(data); + const ContactCallbacks* customCallbacks = static_cast(data); if (customCallbacks->startCallback(*world, arbiter, *firstRigidBody, *secondRigidBody, customCallbacks->userdata)) return cpTrue; else @@ -444,7 +444,7 @@ namespace Nz ChipmunkArbiter2D arbiter(arb); - const Callback* customCallbacks = static_cast(data); + const ContactCallbacks* customCallbacks = static_cast(data); customCallbacks->endCallback(*world, arbiter, *firstRigidBody, *secondRigidBody, customCallbacks->userdata); }; } @@ -469,7 +469,7 @@ namespace Nz ChipmunkArbiter2D arbiter(arb); - const Callback* customCallbacks = static_cast(data); + const ContactCallbacks* customCallbacks = static_cast(data); if (customCallbacks->preSolveCallback(*world, arbiter, *firstRigidBody, *secondRigidBody, customCallbacks->userdata)) return cpTrue; else @@ -498,7 +498,7 @@ namespace Nz ChipmunkArbiter2D arbiter(arb); - const Callback* customCallbacks = static_cast(data); + const ContactCallbacks* customCallbacks = static_cast(data); customCallbacks->postSolveCallback(*world, arbiter, *firstRigidBody, *secondRigidBody, customCallbacks->userdata); }; } diff --git a/src/Nazara/ChipmunkPhysics2D/Systems/ChipmunkPhysics2DSystem.cpp b/src/Nazara/ChipmunkPhysics2D/Systems/ChipmunkPhysics2DSystem.cpp index 1bad2ebd3..32411619b 100644 --- a/src/Nazara/ChipmunkPhysics2D/Systems/ChipmunkPhysics2DSystem.cpp +++ b/src/Nazara/ChipmunkPhysics2D/Systems/ChipmunkPhysics2DSystem.cpp @@ -25,6 +25,7 @@ namespace Nz m_physicsConstructObserver(m_registry, entt::collector.group()) { m_bodyConstructConnection = registry.on_construct().connect<&ChipmunkPhysics2DSystem::OnBodyConstruct>(this); + m_bodyDestructConnection = registry.on_destroy().connect<&ChipmunkPhysics2DSystem::OnBodyDestruct>(this); } ChipmunkPhysics2DSystem::~ChipmunkPhysics2DSystem() @@ -61,9 +62,65 @@ namespace Nz } } + ChipmunkPhysWorld2D::ContactCallbacks ChipmunkPhysics2DSystem::SetupContactCallbacks(ContactCallbacks callbacks) + { + ChipmunkPhysWorld2D::ContactCallbacks trampolineCallbacks; + trampolineCallbacks.userdata = callbacks.userdata; + + if (callbacks.endCallback) + { + trampolineCallbacks.endCallback = [this, cb = std::move(callbacks.endCallback)](ChipmunkPhysWorld2D& world, ChipmunkArbiter2D& arbiter, ChipmunkRigidBody2D& bodyA, ChipmunkRigidBody2D& bodyB, void* userdata) + { + return cb(world, arbiter, GetRigidBodyEntity(bodyA.GetBodyIndex()), GetRigidBodyEntity(bodyB.GetBodyIndex()), userdata); + }; + } + + if (callbacks.preSolveCallback) + { + trampolineCallbacks.preSolveCallback = [this, cb = std::move(callbacks.preSolveCallback)](ChipmunkPhysWorld2D& world, ChipmunkArbiter2D& arbiter, ChipmunkRigidBody2D& bodyA, ChipmunkRigidBody2D& bodyB, void* userdata) + { + return cb(world, arbiter, GetRigidBodyEntity(bodyA.GetBodyIndex()), GetRigidBodyEntity(bodyB.GetBodyIndex()), userdata); + }; + } + + if (callbacks.postSolveCallback) + { + trampolineCallbacks.postSolveCallback = [this, cb = std::move(callbacks.postSolveCallback)](ChipmunkPhysWorld2D& world, ChipmunkArbiter2D& arbiter, ChipmunkRigidBody2D& bodyA, ChipmunkRigidBody2D& bodyB, void* userdata) + { + return cb(world, arbiter, GetRigidBodyEntity(bodyA.GetBodyIndex()), GetRigidBodyEntity(bodyB.GetBodyIndex()), userdata); + }; + } + + if (callbacks.startCallback) + { + trampolineCallbacks.startCallback = [this, cb = std::move(callbacks.startCallback)](ChipmunkPhysWorld2D& world, ChipmunkArbiter2D& arbiter, ChipmunkRigidBody2D& bodyA, ChipmunkRigidBody2D& bodyB, void* userdata) + { + return cb(world, arbiter, GetRigidBodyEntity(bodyA.GetBodyIndex()), GetRigidBodyEntity(bodyB.GetBodyIndex()), userdata); + }; + } + + return trampolineCallbacks; + } + void ChipmunkPhysics2DSystem::OnBodyConstruct(entt::registry& registry, entt::entity entity) { ChipmunkRigidBody2DComponent& rigidBody = registry.get(entity); rigidBody.Construct(m_physWorld); + + UInt32 uniqueIndex = rigidBody.GetBodyIndex(); + if (uniqueIndex >= m_bodyIndicesToEntity.size()) + m_bodyIndicesToEntity.resize(uniqueIndex + 1); + + m_bodyIndicesToEntity[uniqueIndex] = entity; + } + + void ChipmunkPhysics2DSystem::OnBodyDestruct(entt::registry& registry, entt::entity entity) + { + // Unregister owning entity + ChipmunkRigidBody2DComponent& rigidBody = registry.get(entity); + UInt32 uniqueIndex = rigidBody.GetBodyIndex(); + assert(uniqueIndex <= m_bodyIndicesToEntity.size()); + + m_bodyIndicesToEntity[uniqueIndex] = entt::null; } } diff --git a/tests/UnitTests/Engine/Physics2D/PhysWorld2DTest.cpp b/tests/UnitTests/Engine/Physics2D/PhysWorld2DTest.cpp index 7abc043df..e9c69bb5a 100644 --- a/tests/UnitTests/Engine/Physics2D/PhysWorld2DTest.cpp +++ b/tests/UnitTests/Engine/Physics2D/PhysWorld2DTest.cpp @@ -132,7 +132,7 @@ SCENARIO("PhysWorld2D", "[PHYSICS2D][PHYSWORLD2D]") world.Step(Nz::Time::Zero()); - Nz::ChipmunkPhysWorld2D::Callback characterTriggerCallback; + Nz::ChipmunkPhysWorld2D::ContactCallbacks characterTriggerCallback; characterTriggerCallback.startCallback = [&](Nz::ChipmunkPhysWorld2D&, Nz::ChipmunkArbiter2D&, Nz::ChipmunkRigidBody2D&, Nz::ChipmunkRigidBody2D&, void*) -> bool { statusTriggerCollision = statusTriggerCollision | 1 << 0; return true; @@ -150,7 +150,7 @@ SCENARIO("PhysWorld2D", "[PHYSICS2D][PHYSWORLD2D]") world.RegisterCallbacks(CHARACTER_COLLISION_ID, TRIGGER_COLLISION_ID, characterTriggerCallback); int statusWallCollision = 0; - Nz::ChipmunkPhysWorld2D::Callback characterWallCallback; + Nz::ChipmunkPhysWorld2D::ContactCallbacks characterWallCallback; characterWallCallback.startCallback = [&](Nz::ChipmunkPhysWorld2D&, Nz::ChipmunkArbiter2D&, Nz::ChipmunkRigidBody2D&, Nz::ChipmunkRigidBody2D&, void*) -> bool { statusWallCollision = statusWallCollision | 1 << 0; return true;