diff --git a/SDK/include/NDK/BaseComponent.hpp b/SDK/include/NDK/BaseComponent.hpp index 8a4d51bfe..e21a1e38f 100644 --- a/SDK/include/NDK/BaseComponent.hpp +++ b/SDK/include/NDK/BaseComponent.hpp @@ -47,6 +47,7 @@ namespace Ndk virtual void OnComponentAttached(BaseComponent& component); virtual void OnComponentDetached(BaseComponent& component); virtual void OnDetached(); + virtual void OnEntityDestruction(); void SetEntity(Entity* entity); diff --git a/SDK/include/NDK/BaseWidget.inl b/SDK/include/NDK/BaseWidget.inl index 17592241f..e84a67a49 100644 --- a/SDK/include/NDK/BaseWidget.inl +++ b/SDK/include/NDK/BaseWidget.inl @@ -33,6 +33,7 @@ namespace Ndk inline void BaseWidget::AddChild(std::unique_ptr&& widget) { widget->Show(m_visible); + widget->SetParent(this); m_children.emplace_back(std::move(widget)); } diff --git a/SDK/include/NDK/Components/PhysicsComponent2D.hpp b/SDK/include/NDK/Components/PhysicsComponent2D.hpp index 1f4cc63c9..2bbe693a1 100644 --- a/SDK/include/NDK/Components/PhysicsComponent2D.hpp +++ b/SDK/include/NDK/Components/PhysicsComponent2D.hpp @@ -55,6 +55,7 @@ namespace Ndk void OnComponentAttached(BaseComponent& component) override; void OnComponentDetached(BaseComponent& component) override; void OnDetached() override; + void OnEntityDestruction() override; std::unique_ptr m_object; }; diff --git a/SDK/include/NDK/Components/PhysicsComponent3D.hpp b/SDK/include/NDK/Components/PhysicsComponent3D.hpp index f6f13ec09..8be86537e 100644 --- a/SDK/include/NDK/Components/PhysicsComponent3D.hpp +++ b/SDK/include/NDK/Components/PhysicsComponent3D.hpp @@ -62,6 +62,7 @@ namespace Ndk void OnComponentAttached(BaseComponent& component) override; void OnComponentDetached(BaseComponent& component) override; void OnDetached() override; + void OnEntityDestruction() override; std::unique_ptr m_object; }; diff --git a/SDK/include/NDK/Entity.hpp b/SDK/include/NDK/Entity.hpp index ecf9fcc62..e386100f8 100644 --- a/SDK/include/NDK/Entity.hpp +++ b/SDK/include/NDK/Entity.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -65,6 +66,8 @@ namespace Ndk Entity& operator=(const Entity&) = delete; Entity& operator=(Entity&&) = delete; + NazaraSignal(OnEntityDestruction, Entity* /*entity*/); + private: Entity(World* world, EntityId id); diff --git a/SDK/src/NDK/BaseComponent.cpp b/SDK/src/NDK/BaseComponent.cpp index 4af8caed2..204076e54 100644 --- a/SDK/src/NDK/BaseComponent.cpp +++ b/SDK/src/NDK/BaseComponent.cpp @@ -54,6 +54,15 @@ namespace Ndk { } + /*! + * \brief Operation to perform when the entity is destroyed and we're still attached to it + * + * \remark This is always called before the entity proper destruction, and thus its components. + */ + void BaseComponent::OnEntityDestruction() + { + } + std::vector BaseComponent::s_entries; std::unordered_map BaseComponent::s_idToIndex; } diff --git a/SDK/src/NDK/Components/PhysicsComponent2D.cpp b/SDK/src/NDK/Components/PhysicsComponent2D.cpp index b64ee37b5..58cab2982 100644 --- a/SDK/src/NDK/Components/PhysicsComponent2D.cpp +++ b/SDK/src/NDK/Components/PhysicsComponent2D.cpp @@ -88,5 +88,11 @@ namespace Ndk m_object.reset(); } + void PhysicsComponent2D::OnEntityDestruction() + { + // Kill rigidbody before entity destruction to force contact callbacks to be called while the entity is still valid + m_object.reset(); + } + ComponentIndex PhysicsComponent2D::componentIndex; } diff --git a/SDK/src/NDK/Components/PhysicsComponent3D.cpp b/SDK/src/NDK/Components/PhysicsComponent3D.cpp index 2581723a7..31b3fb6ac 100644 --- a/SDK/src/NDK/Components/PhysicsComponent3D.cpp +++ b/SDK/src/NDK/Components/PhysicsComponent3D.cpp @@ -88,5 +88,11 @@ namespace Ndk m_object.reset(); } + void PhysicsComponent3D::OnEntityDestruction() + { + // Kill rigid body before entity destruction to force contact callbacks to be called while the entity is still valid + m_object.reset(); + } + ComponentIndex PhysicsComponent3D::componentIndex; } diff --git a/SDK/src/NDK/Entity.cpp b/SDK/src/NDK/Entity.cpp index d88556a31..7cdc87d07 100644 --- a/SDK/src/NDK/Entity.cpp +++ b/SDK/src/NDK/Entity.cpp @@ -30,6 +30,7 @@ namespace Ndk m_enabled(entity.m_enabled), m_valid(entity.m_valid) { + entity.m_world = nullptr; } /*! @@ -53,7 +54,8 @@ namespace Ndk Entity::~Entity() { - Destroy(); + if (m_world) + Destroy(); } /*! @@ -145,6 +147,17 @@ namespace Ndk void Entity::Destroy() { + OnEntityDestruction(this); + OnEntityDestruction.Clear(); + + // We prepare components for entity destruction (some components needs this to handle some final callbacks while the entity is still valid) + for (std::size_t i = m_componentBits.FindFirst(); i != m_componentBits.npos; i = m_componentBits.FindNext(i)) + m_components[i]->OnEntityDestruction(); + + // Detach components while they're still attached to systems + for (std::size_t i = m_componentBits.FindFirst(); i != m_componentBits.npos; i = m_componentBits.FindNext(i)) + m_components[i]->SetEntity(nullptr); + // We alert each system for (std::size_t index = m_systemBits.FindFirst(); index != m_systemBits.npos; index = m_systemBits.FindNext(index)) { @@ -158,10 +171,7 @@ namespace Ndk } m_systemBits.Clear(); - // We properly destroy each component - for (std::size_t i = m_componentBits.FindFirst(); i != m_componentBits.npos; i = m_componentBits.FindNext(i)) - m_components[i]->SetEntity(nullptr); - + // Destroy components m_components.clear(); m_componentBits.Reset(); diff --git a/SDK/src/NDK/World.cpp b/SDK/src/NDK/World.cpp index 545fab499..cad4660d4 100644 --- a/SDK/src/NDK/World.cpp +++ b/SDK/src/NDK/World.cpp @@ -185,12 +185,12 @@ namespace Ndk NazaraAssert(entity.IsValid(), "Entity must be valid"); - // Send back the identifier of the entity to the free queue - m_freeIdList.push_back(entity.GetId()); - // Destruction of the entity (invalidation of handle by the same way) entity.Destroy(); + // Send back the identifier of the entity to the free queue + m_freeIdList.push_back(entity.GetId()); + // We take out the handle from the list of alive entities // With the idiom swap and pop diff --git a/build/config.lua b/build/config.lua index 8ecc7c24e..12a9f8e77 100644 --- a/build/config.lua +++ b/build/config.lua @@ -1,6 +1,9 @@ -- This file contains special configurations values, such as directories to extern libraries (Qt) -- Editing this file is not required to use/compile the engine, as default values should be enough +-- Additionnal compilation options +--AdditionalCompilationOptions = "-fsanitize=address" -- Enable ASan + -- Builds Nazara extern libraries (such as lua/STB) BuildDependencies = true diff --git a/build/scripts/actions/package.lua b/build/scripts/actions/package.lua index 62d4792b3..cbdae329c 100644 --- a/build/scripts/actions/package.lua +++ b/build/scripts/actions/package.lua @@ -86,7 +86,7 @@ ACTION.Function = function () local exeFilterFunc if (os.is("windows")) then binFileMasks = {"**.dll"} - libFileMasks = {"**.lib", "**.a"} + libFileMasks = {"**.lib", "**.a", "**.pdb"} exeFileExt = ".exe" exeFilterFunc = function (filePath) return true end else diff --git a/build/scripts/common.lua b/build/scripts/common.lua index e3ea13d62..ea7097e97 100644 --- a/build/scripts/common.lua +++ b/build/scripts/common.lua @@ -505,11 +505,47 @@ function NazaraBuild:LoadConfig() end end + local AddStringOption = function (option, name, description) + newoption({ + trigger = name, + description = description + }) + + local str = _OPTIONS[name] + if (str) then + configTable[option] = str + end + end + + AddBoolOption("BuildDependencies", "with-extlibs", "Builds the extern libraries") AddBoolOption("BuildExamples", "with-examples", "Builds the examples") AddBoolOption("ServerMode", "server", "Excludes client-only modules/tools/examples") AddBoolOption("UniteModules", "united", "Builds all the modules as one united library") + -- AdditionalCompilationOptions + do + newoption({ + trigger = "compile-options", + description = "Specify additionnal compilation options to be added to every generated project." + }) + + configTable["AdditionalCompilationOptions"] = configTable["AdditionalCompilationOptions"] or "" + if (_OPTIONS["compile-options"] ~= nil) then + configTable["AdditionalCompilationOptions"] = configTable["AdditionalCompilationOptions"] .. ";" .. _OPTIONS["compile-options"] + end + + local configs = {} + local paths = string.explode(configTable["AdditionalCompilationOptions"], ";") + for k,v in pairs(paths) do + if (#v > 0) then + table.insert(configs, v) + end + end + + configTable["AdditionalCompilationOptions"] = configs + end + -- Configurations do newoption({ @@ -792,6 +828,8 @@ function NazaraBuild:PrepareGeneric() buildoptions("-ftree-vectorize") filter({}) + + buildoptions(self.Config["AdditionalCompilationOptions"]) end function NazaraBuild:PrepareMainWorkspace() diff --git a/include/Nazara/Core/Signal.hpp b/include/Nazara/Core/Signal.hpp index c325b40ce..1569c5d0e 100644 --- a/include/Nazara/Core/Signal.hpp +++ b/include/Nazara/Core/Signal.hpp @@ -69,7 +69,7 @@ namespace Nz SlotListIndex index; }; - void Disconnect(const SlotPtr& slot); + void Disconnect(const SlotPtr& slot) noexcept; SlotList m_slots; mutable SlotListIndex m_slotIterator; @@ -84,17 +84,17 @@ namespace Nz public: Connection() = default; Connection(const Connection& connection) = default; - Connection(Connection&& connection) = default; + Connection(Connection&& connection) noexcept; ~Connection() = default; template void Connect(BaseClass& signal, ConnectArgs&&... args); - void Disconnect(); + void Disconnect() noexcept; bool IsConnected() const; Connection& operator=(const Connection& connection) = default; - Connection& operator=(Connection&& connection) = default; + Connection& operator=(Connection&& connection) noexcept; private: Connection(const SlotPtr& slot); @@ -113,12 +113,12 @@ namespace Nz ConnectionGuard(const Connection& connection); ConnectionGuard(const ConnectionGuard& connection) = delete; ConnectionGuard(Connection&& connection); - ConnectionGuard(ConnectionGuard&& connection) = default; + ConnectionGuard(ConnectionGuard&& connection) noexcept = default; ~ConnectionGuard(); template void Connect(BaseClass& signal, ConnectArgs&&... args); - void Disconnect(); + void Disconnect() noexcept; Connection& GetConnection(); @@ -127,7 +127,7 @@ namespace Nz ConnectionGuard& operator=(const Connection& connection); ConnectionGuard& operator=(const ConnectionGuard& connection) = delete; ConnectionGuard& operator=(Connection&& connection); - ConnectionGuard& operator=(ConnectionGuard&& connection); + ConnectionGuard& operator=(ConnectionGuard&& connection) noexcept; private: Connection m_connection; diff --git a/include/Nazara/Core/Signal.inl b/include/Nazara/Core/Signal.inl index 9e711a65f..c4e89bd40 100644 --- a/include/Nazara/Core/Signal.inl +++ b/include/Nazara/Core/Signal.inl @@ -205,7 +205,7 @@ namespace Nz */ template - void Signal::Disconnect(const SlotPtr& slot) + void Signal::Disconnect(const SlotPtr& slot) noexcept { NazaraAssert(slot, "Invalid slot pointer"); NazaraAssert(slot->index < m_slots.size(), "Invalid slot index"); @@ -246,6 +246,18 @@ namespace Nz * \brief Core class that represents a connection attached to a signal */ + /*! + * \brief Constructs a Signal::Connection object with by move semantic + * + * \param connection Connection object to move + */ + template + Signal::Connection::Connection(Connection&& connection) noexcept : + m_ptr(std::move(connection.m_ptr)) + { + connection.m_ptr.reset(); //< Fuck you GCC 4.9 + } + /*! * \brief Constructs a Signal::Connection object with a slot * @@ -277,7 +289,7 @@ namespace Nz */ template - void Signal::Connection::Disconnect() + void Signal::Connection::Disconnect() noexcept { if (SlotPtr ptr = m_ptr.lock()) ptr->signal->Disconnect(ptr); @@ -294,6 +306,20 @@ namespace Nz return !m_ptr.expired(); } + /*! + * \brief Constructs a Signal::ConnectionGuard object by move semantic + * + * \param connection Connection to move + */ + template + typename Signal::Connection& Signal::Connection::operator=(Connection&& connection) noexcept + { + m_ptr = std::move(connection.m_ptr); + connection.m_ptr.reset(); //< Fuck you GCC 4.9 + + return *this; + } + /*! * \class Nz::Signal::ConnectionGuard * \brief Core class that represents a RAII for a connection attached to a signal @@ -353,7 +379,7 @@ namespace Nz */ template - void Signal::ConnectionGuard::Disconnect() + void Signal::ConnectionGuard::Disconnect() noexcept { m_connection.Disconnect(); } @@ -406,8 +432,11 @@ namespace Nz template typename Signal::ConnectionGuard& Signal::ConnectionGuard::operator=(Connection&& connection) { - m_connection.Disconnect(); - m_connection = std::move(connection); + if (&connection != this) + { + m_connection.Disconnect(); + m_connection = std::move(connection); + } return *this; } @@ -420,10 +449,13 @@ namespace Nz */ template - typename Signal::ConnectionGuard& Signal::ConnectionGuard::operator=(ConnectionGuard&& connection) + typename Signal::ConnectionGuard& Signal::ConnectionGuard::operator=(ConnectionGuard&& connection) noexcept { - m_connection.Disconnect(); - m_connection = std::move(connection.m_connection); + if (&connection != this) + { + m_connection.Disconnect(); + m_connection = std::move(connection.m_connection); + } return *this; } diff --git a/include/Nazara/Physics2D/Collider2D.hpp b/include/Nazara/Physics2D/Collider2D.hpp index e5b988756..b09e8781f 100644 --- a/include/Nazara/Physics2D/Collider2D.hpp +++ b/include/Nazara/Physics2D/Collider2D.hpp @@ -42,13 +42,19 @@ namespace Nz virtual float ComputeInertialMatrix(float mass) const = 0; + inline Nz::UInt32 GetCategoryMask() const; + inline Nz::UInt32 GetCollisionGroup() const; inline unsigned int GetCollisionId() const; + inline Nz::UInt32 GetCollisionMask() const; virtual ColliderType2D GetType() const = 0; inline bool IsTrigger() const; - inline void SetCollisionId(unsigned long typeId); + inline void SetCategoryMask(Nz::UInt32 categoryMask); + inline void SetCollisionGroup(Nz::UInt32 groupId); + inline void SetCollisionId(unsigned int typeId); + inline void SetCollisionMask(Nz::UInt32 mask); inline void SetTrigger(bool trigger); Collider2D& operator=(const Collider2D&) = delete; @@ -61,7 +67,10 @@ namespace Nz virtual std::vector CreateShapes(RigidBody2D* body) const = 0; bool m_trigger; + Nz::UInt32 m_categoryMask; + Nz::UInt32 m_collisionGroup; unsigned int m_collisionId; + Nz::UInt32 m_collisionMask; private: virtual std::vector GenerateShapes(RigidBody2D* body) const; diff --git a/include/Nazara/Physics2D/Collider2D.inl b/include/Nazara/Physics2D/Collider2D.inl index 71b909a2c..60cf8559f 100644 --- a/include/Nazara/Physics2D/Collider2D.inl +++ b/include/Nazara/Physics2D/Collider2D.inl @@ -2,32 +2,66 @@ // This file is part of the "Nazara Engine - Physics 2D module" // For conditions of distribution and use, see copyright notice in Config.hpp +#include #include #include namespace Nz { inline Collider2D::Collider2D() : + m_trigger(false), + m_categoryMask(0xFFFFFFFF), + m_collisionGroup(0), m_collisionId(0), - m_trigger(false) + m_collisionMask(0xFFFFFFFF) { } + inline Nz::UInt32 Collider2D::GetCategoryMask() const + { + return m_categoryMask; + } + + inline Nz::UInt32 Collider2D::GetCollisionGroup() const + { + return m_collisionGroup; + } + inline unsigned int Collider2D::GetCollisionId() const { return m_collisionId; } + inline Nz::UInt32 Collider2D::GetCollisionMask() const + { + return m_collisionMask; + } + inline bool Collider2D::IsTrigger() const { return m_trigger; } - inline void Collider2D::SetCollisionId(unsigned long typeId) + inline void Collider2D::SetCategoryMask(Nz::UInt32 categoryMask) + { + m_categoryMask = categoryMask; + } + + inline void Collider2D::SetCollisionGroup(Nz::UInt32 groupId) + { + m_collisionGroup = groupId; + } + + inline void Collider2D::SetCollisionId(unsigned int typeId) { m_collisionId = typeId; } + inline void Collider2D::SetCollisionMask(Nz::UInt32 mask) + { + m_collisionMask = mask; + } + inline void Collider2D::SetTrigger(bool trigger) { m_trigger = trigger; @@ -108,4 +142,3 @@ namespace Nz } #include -#include "Collider2D.hpp" diff --git a/include/Nazara/Physics2D/PhysWorld2D.hpp b/include/Nazara/Physics2D/PhysWorld2D.hpp index 760773c7d..52db98189 100644 --- a/include/Nazara/Physics2D/PhysWorld2D.hpp +++ b/include/Nazara/Physics2D/PhysWorld2D.hpp @@ -8,8 +8,10 @@ #define NAZARA_PHYSWORLD2D_HPP #include +#include #include #include +#include #include #include @@ -18,10 +20,10 @@ struct cpSpace; namespace Nz { - class RigidBody2D; - class NAZARA_PHYSICS2D_API PhysWorld2D { + friend RigidBody2D; + using ContactEndCallback = void(*)(PhysWorld2D& world, RigidBody2D& bodyA, RigidBody2D& bodyB, void* userdata); using ContactPreSolveCallback = bool(*)(PhysWorld2D& world, RigidBody2D& bodyA, RigidBody2D& bodyB, void* userdata); using ContactPostSolveCallback = void(*)(PhysWorld2D& world, RigidBody2D& bodyA, RigidBody2D& bodyB, void* userdata); @@ -29,6 +31,8 @@ namespace Nz public: struct Callback; + struct NearestQueryResult; + struct RaycastHit; PhysWorld2D(); PhysWorld2D(const PhysWorld2D&) = delete; @@ -39,6 +43,12 @@ namespace Nz cpSpace* GetHandle() const; float GetStepSize() const; + bool NearestBodyQuery(const Vector2f& from, float maxDistance, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, RigidBody2D** nearestBody = nullptr); + bool NearestBodyQuery(const Vector2f& from, float maxDistance, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, NearestQueryResult* result); + + bool RaycastQuery(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector* hitInfos); + bool RaycastQueryFirst(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, RaycastHit* hitInfo = nullptr); + void RegisterCallbacks(unsigned int collisionId, const Callback& callbacks); void RegisterCallbacks(unsigned int collisionIdA, unsigned int collisionIdB, const Callback& callbacks); @@ -59,10 +69,47 @@ namespace Nz void* userdata; }; + struct NearestQueryResult + { + Nz::RigidBody2D* nearestBody; + Nz::Vector2f closestPoint; + Nz::Vector2f fraction; + float distance; + }; + + struct RaycastHit + { + Nz::RigidBody2D* nearestBody; + Nz::Vector2f hitPos; + Nz::Vector2f hitNormal; + float fraction; + }; + + NazaraSignal(OnPhysWorld2DPreStep, const PhysWorld2D* /*physWorld*/); + NazaraSignal(OnPhysWorld2DPostStep, const PhysWorld2D* /*physWorld*/); + private: void InitCallbacks(cpCollisionHandler* handler, const Callback& callbacks); + using PostStep = std::function; + + void OnRigidBodyMoved(RigidBody2D* oldPointer, RigidBody2D* newPointer); + void OnRigidBodyRelease(RigidBody2D* rigidBody); + + void RegisterPostStep(RigidBody2D* rigidBody, PostStep&& func); + + struct PostStepContainer + { + NazaraSlot(RigidBody2D, OnRigidBody2DMove, onMovedSlot); + NazaraSlot(RigidBody2D, OnRigidBody2DRelease, onReleaseSlot); + + std::vector funcs; + }; + + static_assert(std::is_nothrow_move_constructible::value, "PostStepContainer should be noexcept MoveConstructible"); + std::unordered_map> m_callbacks; + std::unordered_map m_rigidPostSteps; cpSpace* m_handle; float m_stepSize; float m_timestepAccumulator; diff --git a/include/Nazara/Physics2D/RigidBody2D.hpp b/include/Nazara/Physics2D/RigidBody2D.hpp index f57ce6a48..9763aba47 100644 --- a/include/Nazara/Physics2D/RigidBody2D.hpp +++ b/include/Nazara/Physics2D/RigidBody2D.hpp @@ -34,6 +34,8 @@ namespace Nz void AddForce(const Vector2f& force, CoordSys coordSys = CoordSys_Global); void AddForce(const Vector2f& force, const Vector2f& point, CoordSys coordSys = CoordSys_Global); + void AddImpulse(const Vector2f& impulse, CoordSys coordSys = CoordSys_Global); + void AddImpulse(const Vector2f& impulse, const Vector2f& point, CoordSys coordSys = CoordSys_Global); void AddTorque(float torque); Rectf GetAABB() const; diff --git a/src/Nazara/Network/SocketPoller.cpp b/src/Nazara/Network/SocketPoller.cpp index fa5aa837e..43250a84f 100644 --- a/src/Nazara/Network/SocketPoller.cpp +++ b/src/Nazara/Network/SocketPoller.cpp @@ -142,13 +142,15 @@ namespace Nz } /*! - * \brief Wait until any registered socket switches to a ready state + * \brief Wait until any registered socket switches to a ready state. * * Waits a specific/undetermined amount of time until at least one socket part of the SocketPoller becomes ready. * To query the ready state of the registered socket, use the IsReady function. * * \param msTimeout Maximum time to wait in milliseconds, 0 for infinity * + * \return True if at least one socket registered to the poller is ready. + * * \remark It is an error to try to unregister a non-registered socket from a SocketPoller. * * \see IsReady diff --git a/src/Nazara/Physics2D/Collider2D.cpp b/src/Nazara/Physics2D/Collider2D.cpp index cce3301aa..3a679fad9 100644 --- a/src/Nazara/Physics2D/Collider2D.cpp +++ b/src/Nazara/Physics2D/Collider2D.cpp @@ -13,9 +13,12 @@ namespace Nz std::vector Collider2D::GenerateShapes(RigidBody2D* body) const { + cpShapeFilter filter = cpShapeFilterNew(m_collisionGroup, m_categoryMask, m_collisionMask); + std::vector shapes = CreateShapes(body); for (cpShape* shape : shapes) { + cpShapeSetFilter(shape, filter); cpShapeSetCollisionType(shape, m_collisionId); cpShapeSetSensor(shape, (m_trigger) ? cpTrue : cpFalse); } diff --git a/src/Nazara/Physics2D/PhysWorld2D.cpp b/src/Nazara/Physics2D/PhysWorld2D.cpp index 7c15beee3..e56749d66 100644 --- a/src/Nazara/Physics2D/PhysWorld2D.cpp +++ b/src/Nazara/Physics2D/PhysWorld2D.cpp @@ -37,6 +37,102 @@ namespace Nz return m_stepSize; } + bool PhysWorld2D::NearestBodyQuery(const Vector2f & from, float maxDistance, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, RigidBody2D** nearestBody) + { + cpShapeFilter filter = cpShapeFilterNew(collisionGroup, categoryMask, collisionMask); + + if (cpShape* shape = cpSpacePointQueryNearest(m_handle, {from.x, from.y}, maxDistance, filter, nullptr)) + { + if (nearestBody) + *nearestBody = static_cast(cpShapeGetUserData(shape)); + + return true; + } + else + return false; + } + + bool PhysWorld2D::NearestBodyQuery(const Vector2f& from, float maxDistance, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, NearestQueryResult* result) + { + cpShapeFilter filter = cpShapeFilterNew(collisionGroup, categoryMask, collisionMask); + + if (result) + { + cpPointQueryInfo queryInfo; + + if (cpShape* shape = cpSpacePointQueryNearest(m_handle, { from.x, from.y }, maxDistance, filter, &queryInfo)) + { + result->closestPoint.Set(queryInfo.point.x, queryInfo.point.y); + result->distance = queryInfo.distance; + result->fraction.Set(queryInfo.gradient.x, queryInfo.gradient.y); + result->nearestBody = static_cast(cpShapeGetUserData(shape)); + + return true; + } + else + return false; + } + else + { + if (cpShape* shape = cpSpacePointQueryNearest(m_handle, { from.x, from.y }, maxDistance, filter, nullptr)) + return true; + else + return false; + } + } + + bool PhysWorld2D::RaycastQuery(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, std::vector* hitInfos) + { + auto callback = [](cpShape* shape, cpVect point, cpVect normal, cpFloat alpha, void* data) + { + std::vector* results = reinterpret_cast*>(data); + + RaycastHit hitInfo; + hitInfo.fraction = alpha; + hitInfo.hitNormal.Set(normal.x, normal.y); + hitInfo.hitPos.Set(point.x, point.y); + hitInfo.nearestBody = static_cast(cpShapeGetUserData(shape)); + + results->emplace_back(std::move(hitInfo)); + }; + + cpShapeFilter filter = cpShapeFilterNew(collisionGroup, categoryMask, collisionMask); + + std::size_t previousSize = hitInfos->size(); + cpSpaceSegmentQuery(m_handle, { from.x, from.y }, { to.x, to.y }, radius, filter, callback, hitInfos); + + return hitInfos->size() != previousSize; + } + + bool PhysWorld2D::RaycastQueryFirst(const Nz::Vector2f& from, const Nz::Vector2f& to, float radius, Nz::UInt32 collisionGroup, Nz::UInt32 categoryMask, Nz::UInt32 collisionMask, RaycastHit* hitInfo) + { + cpShapeFilter filter = cpShapeFilterNew(collisionGroup, categoryMask, collisionMask); + + if (hitInfo) + { + cpSegmentQueryInfo queryInfo; + + if (cpShape* shape = cpSpaceSegmentQueryFirst(m_handle, { from.x, from.y }, { to.x, to.y }, radius, filter, &queryInfo)) + { + hitInfo->fraction = queryInfo.alpha; + hitInfo->hitNormal.Set(queryInfo.normal.x, queryInfo.normal.y); + hitInfo->hitPos.Set(queryInfo.point.x, queryInfo.point.y); + hitInfo->nearestBody = static_cast(cpShapeGetUserData(queryInfo.shape)); + + return true; + } + else + return false; + } + else + { + if (cpShape* shape = cpSpaceSegmentQueryFirst(m_handle, { from.x, from.y }, { to.x, to.y }, radius, filter, nullptr)) + return true; + else + return false; + } + } + void PhysWorld2D::RegisterCallbacks(unsigned int collisionId, const Callback& callbacks) { InitCallbacks(cpSpaceAddWildcardHandler(m_handle, collisionId), callbacks); @@ -63,7 +159,22 @@ namespace Nz while (m_timestepAccumulator >= m_stepSize) { + OnPhysWorld2DPreStep(this); + cpSpaceStep(m_handle, m_stepSize); + + OnPhysWorld2DPostStep(this); + if (!m_rigidPostSteps.empty()) + { + for (const auto& pair : m_rigidPostSteps) + { + for (const auto& step : pair.second.funcs) + step(pair.first); + } + + m_rigidPostSteps.clear(); + } + m_timestepAccumulator -= m_stepSize; } } @@ -162,4 +273,41 @@ namespace Nz }; } } + + void PhysWorld2D::OnRigidBodyMoved(RigidBody2D* oldPointer, RigidBody2D* newPointer) + { + auto it = m_rigidPostSteps.find(oldPointer); + if (it == m_rigidPostSteps.end()) + return; //< Shouldn't happen + + m_rigidPostSteps.emplace(std::make_pair(newPointer, std::move(it->second))); + m_rigidPostSteps.erase(oldPointer); + } + + void PhysWorld2D::OnRigidBodyRelease(RigidBody2D* rigidBody) + { + m_rigidPostSteps.erase(rigidBody); + } + + void PhysWorld2D::RegisterPostStep(RigidBody2D* rigidBody, PostStep&& func) + { + // If space isn't locked, no need to wait + if (!cpSpaceIsLocked(m_handle)) + { + func(rigidBody); + return; + } + + auto it = m_rigidPostSteps.find(rigidBody); + if (it == m_rigidPostSteps.end()) + { + PostStepContainer postStep; + postStep.onMovedSlot.Connect(rigidBody->OnRigidBody2DMove, this, &PhysWorld2D::OnRigidBodyMoved); + postStep.onReleaseSlot.Connect(rigidBody->OnRigidBody2DRelease, this, &PhysWorld2D::OnRigidBodyRelease); + + it = m_rigidPostSteps.insert(std::make_pair(rigidBody, std::move(postStep))).first; + } + + it->second.funcs.emplace_back(std::move(func)); + } } diff --git a/src/Nazara/Physics2D/RigidBody2D.cpp b/src/Nazara/Physics2D/RigidBody2D.cpp index 8993fe398..f31e9ef54 100644 --- a/src/Nazara/Physics2D/RigidBody2D.cpp +++ b/src/Nazara/Physics2D/RigidBody2D.cpp @@ -61,6 +61,8 @@ namespace Nz m_mass(object.m_mass) { cpBodySetUserData(m_handle, this); + for (cpShape* shape : m_shapes) + cpShapeSetUserData(shape, this); object.m_handle = nullptr; @@ -93,6 +95,25 @@ namespace Nz } } + void RigidBody2D::AddImpulse(const Vector2f& impulse, CoordSys coordSys) + { + return AddImpulse(impulse, GetCenterOfGravity(coordSys), coordSys); + } + + void RigidBody2D::AddImpulse(const Vector2f& impulse, const Vector2f& point, CoordSys coordSys) + { + switch (coordSys) + { + case CoordSys_Global: + cpBodyApplyImpulseAtWorldPoint(m_handle, cpv(impulse.x, impulse.y), cpv(point.x, point.y)); + break; + + case CoordSys_Local: + cpBodyApplyImpulseAtLocalPoint(m_handle, cpv(impulse.x, impulse.y), cpv(point.x, point.y)); + break; + } + } + void RigidBody2D::AddTorque(float torque) { cpBodySetTorque(m_handle, cpBodyGetTorque(m_handle) + torque); @@ -210,7 +231,10 @@ namespace Nz cpSpace* space = m_world->GetHandle(); for (cpShape* shape : m_shapes) + { + cpShapeSetUserData(shape, this); cpSpaceAddShape(space, shape); + } cpBodySetMoment(m_handle, m_geom->ComputeInertialMatrix(m_mass)); } @@ -221,20 +245,26 @@ namespace Nz { if (mass > 0.f) { - cpBodySetMass(m_handle, mass); - cpBodySetMoment(m_handle, m_geom->ComputeInertialMatrix(m_mass)); + m_world->RegisterPostStep(this, [mass](Nz::RigidBody2D* body) + { + cpBodySetMass(body->GetHandle(), mass); + cpBodySetMoment(body->GetHandle(), body->GetGeom()->ComputeInertialMatrix(mass)); + }); } else - cpBodySetType(m_handle, CP_BODY_TYPE_STATIC); + m_world->RegisterPostStep(this, [](Nz::RigidBody2D* body) { cpBodySetType(body->GetHandle(), CP_BODY_TYPE_STATIC); } ); } else if (mass > 0.f) { - if (cpBodyGetType(m_handle) == CP_BODY_TYPE_STATIC) + m_world->RegisterPostStep(this, [mass](Nz::RigidBody2D* body) { - cpBodySetType(m_handle, CP_BODY_TYPE_DYNAMIC); - cpBodySetMass(m_handle, mass); - cpBodySetMoment(m_handle, m_geom->ComputeInertialMatrix(m_mass)); - } + if (cpBodyGetType(body->GetHandle()) == CP_BODY_TYPE_STATIC) + { + cpBodySetType(body->GetHandle(), CP_BODY_TYPE_DYNAMIC); + cpBodySetMass(body->GetHandle(), mass); + cpBodySetMoment(body->GetHandle(), body->GetGeom()->ComputeInertialMatrix(mass)); + } + }); } m_mass = mass; @@ -290,6 +320,8 @@ namespace Nz m_world = object.m_world; cpBodySetUserData(m_handle, this); + for (cpShape* shape : m_shapes) + cpShapeSetUserData(shape, this); object.m_handle = nullptr; @@ -302,6 +334,7 @@ namespace Nz { m_handle = cpBodyNew(mass, moment); cpBodySetUserData(m_handle, this); + cpSpaceAddBody(m_world->GetHandle(), m_handle); }