From afa874de268fe00d3ea5eaee4a25730a144c32a2 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sun, 10 Dec 2017 22:17:41 +0100 Subject: [PATCH 1/3] WIP on materials --- .../NDK/Components/PhysicsComponent3D.hpp | 2 + .../NDK/Components/PhysicsComponent3D.inl | 29 +++++++- SDK/include/NDK/Systems/PhysicsSystem3D.hpp | 1 - .../NDK/Components/CollisionComponent3D.cpp | 1 + SDK/src/NDK/Components/PhysicsComponent3D.cpp | 1 + SDK/src/NDK/Widgets/CheckboxWidget.cpp | 2 +- include/Nazara/Physics3D/PhysWorld3D.hpp | 21 ++++++ include/Nazara/Physics3D/RigidBody3D.hpp | 6 ++ src/Nazara/Physics3D/PhysWorld3D.cpp | 66 +++++++++++++++++++ src/Nazara/Physics3D/RigidBody3D.cpp | 25 +++++++ 10 files changed, 151 insertions(+), 3 deletions(-) diff --git a/SDK/include/NDK/Components/PhysicsComponent3D.hpp b/SDK/include/NDK/Components/PhysicsComponent3D.hpp index 292c5f9ab..7aa5229fb 100644 --- a/SDK/include/NDK/Components/PhysicsComponent3D.hpp +++ b/SDK/include/NDK/Components/PhysicsComponent3D.hpp @@ -52,6 +52,8 @@ namespace Ndk void SetLinearVelocity(const Nz::Vector3f& velocity); void SetMass(float mass); void SetMassCenter(const Nz::Vector3f& center); + void SetMaterial(const Nz::String& materialName); + void SetMaterial(int materialIndex); void SetPosition(const Nz::Vector3f& position); void SetRotation(const Nz::Quaternionf& rotation); diff --git a/SDK/include/NDK/Components/PhysicsComponent3D.inl b/SDK/include/NDK/Components/PhysicsComponent3D.inl index 741655387..f1d56cd0b 100644 --- a/SDK/include/NDK/Components/PhysicsComponent3D.inl +++ b/SDK/include/NDK/Components/PhysicsComponent3D.inl @@ -365,7 +365,6 @@ namespace Ndk * * \remark Produces a NazaraAssert if the physics object is invalid */ - inline void PhysicsComponent3D::SetMassCenter(const Nz::Vector3f& center) { NazaraAssert(m_object, "Invalid physics object"); @@ -373,6 +372,34 @@ namespace Ndk m_object->SetMassCenter(center); } + /*! + * \brief Sets the material of the object, affecting how object does respond to collisions + * + * \param materialName Name of the material, previously registered to physics world + * + * \remark materialName must exists in PhysWorld before this call + */ + inline void PhysicsComponent3D::SetMaterial(const Nz::String& materialName) + { + NazaraAssert(m_object, "Invalid physics object"); + + m_object->SetMaterial(materialName); + } + + /*! + * \brief Sets the material of the object, affecting how object does respond to collisions + * + * \param materialIndex Id of the material, previously retrieved from a physics world + * + * \remark materialIndex must come from a call to in PhysWorld::CreateMaterial + */ + inline void PhysicsComponent3D::SetMaterial(int materialIndex) + { + NazaraAssert(m_object, "Invalid physics object"); + + m_object->SetMaterial(materialIndex); + } + /*! * \brief Sets the position of the physics object * diff --git a/SDK/include/NDK/Systems/PhysicsSystem3D.hpp b/SDK/include/NDK/Systems/PhysicsSystem3D.hpp index 9d653695a..6d543db7c 100644 --- a/SDK/include/NDK/Systems/PhysicsSystem3D.hpp +++ b/SDK/include/NDK/Systems/PhysicsSystem3D.hpp @@ -18,7 +18,6 @@ namespace Ndk { public: PhysicsSystem3D(); - PhysicsSystem3D(const PhysicsSystem3D& system); ~PhysicsSystem3D() = default; Nz::PhysWorld3D& GetWorld(); diff --git a/SDK/src/NDK/Components/CollisionComponent3D.cpp b/SDK/src/NDK/Components/CollisionComponent3D.cpp index 7fab3d9be..633e01ee9 100644 --- a/SDK/src/NDK/Components/CollisionComponent3D.cpp +++ b/SDK/src/NDK/Components/CollisionComponent3D.cpp @@ -59,6 +59,7 @@ namespace Ndk m_staticBody = std::make_unique(&physWorld, m_geom); m_staticBody->EnableAutoSleep(false); + m_staticBody->SetUserdata(reinterpret_cast(static_cast(m_entity->GetId()))); } /*! diff --git a/SDK/src/NDK/Components/PhysicsComponent3D.cpp b/SDK/src/NDK/Components/PhysicsComponent3D.cpp index be7ea9f86..78871442b 100644 --- a/SDK/src/NDK/Components/PhysicsComponent3D.cpp +++ b/SDK/src/NDK/Components/PhysicsComponent3D.cpp @@ -42,6 +42,7 @@ namespace Ndk m_object = std::make_unique(&world, geom, matrix); m_object->SetMass(1.f); + m_object->SetUserdata(reinterpret_cast(static_cast(m_entity->GetId()))); } /*! diff --git a/SDK/src/NDK/Widgets/CheckboxWidget.cpp b/SDK/src/NDK/Widgets/CheckboxWidget.cpp index ab513c7f8..3dcdb682c 100644 --- a/SDK/src/NDK/Widgets/CheckboxWidget.cpp +++ b/SDK/src/NDK/Widgets/CheckboxWidget.cpp @@ -134,7 +134,7 @@ namespace Ndk Nz::Vector3f textBox = m_textSprite->GetBoundingVolume().obb.localBox.GetLengths(); m_textEntity->GetComponent().SetPosition(origin.x + checkboxSize.x + (m_adaptativeMargin ? checkboxSize.x / 2.f : m_textMargin), - origin.y + checkboxSize.y / 2.f - textBox.y / 2.f); + origin.y + checkboxSize.y / 2.f - textBox.y / 2.f); } void CheckboxWidget::OnMouseButtonRelease(int x, int y, Nz::Mouse::Button button) diff --git a/include/Nazara/Physics3D/PhysWorld3D.hpp b/include/Nazara/Physics3D/PhysWorld3D.hpp index db6c5af00..427aaeb69 100644 --- a/include/Nazara/Physics3D/PhysWorld3D.hpp +++ b/include/Nazara/Physics3D/PhysWorld3D.hpp @@ -8,35 +8,56 @@ #define NAZARA_PHYSWORLD_HPP #include +#include #include #include +#include +class NewtonJoint; class NewtonWorld; namespace Nz { + class RigidBody3D; + class NAZARA_PHYSICS3D_API PhysWorld3D { public: + using CollisionCallback = std::function; + PhysWorld3D(); PhysWorld3D(const PhysWorld3D&) = delete; PhysWorld3D(PhysWorld3D&&) = delete; ///TODO ~PhysWorld3D(); + int CreateMaterial(Nz::String name = Nz::String()); + Vector3f GetGravity() const; NewtonWorld* GetHandle() const; + int GetMaterial(const Nz::String& name); float GetStepSize() const; void SetGravity(const Vector3f& gravity); void SetSolverModel(unsigned int model); void SetStepSize(float stepSize); + void SetMaterialCollisionCallback(int firstMaterial, int secondMaterial, CollisionCallback callback); + void Step(float timestep); PhysWorld3D& operator=(const PhysWorld3D&) = delete; PhysWorld3D& operator=(PhysWorld3D&&) = delete; ///TODO private: + struct Callback + { + CollisionCallback collisionCallback; + }; + + static void ProcessContact(const NewtonJoint* const contact, float timestep, int threadIndex); + + std::unordered_map> m_callbacks; + std::unordered_map m_materialIds; Vector3f m_gravity; NewtonWorld* m_world; float m_stepSize; diff --git a/include/Nazara/Physics3D/RigidBody3D.hpp b/include/Nazara/Physics3D/RigidBody3D.hpp index cb0128994..44fe48247 100644 --- a/include/Nazara/Physics3D/RigidBody3D.hpp +++ b/include/Nazara/Physics3D/RigidBody3D.hpp @@ -47,9 +47,11 @@ namespace Nz Vector3f GetLinearVelocity() const; float GetMass() const; Vector3f GetMassCenter(CoordSys coordSys = CoordSys_Local) const; + int GetMaterial() const; const Matrix4f& GetMatrix() const; Vector3f GetPosition() const; Quaternionf GetRotation() const; + void* GetUserdata() const; PhysWorld3D* GetWorld() const; bool IsAutoSleepEnabled() const; @@ -65,8 +67,11 @@ namespace Nz void SetLinearVelocity(const Vector3f& velocity); void SetMass(float mass); void SetMassCenter(const Vector3f& center); + void SetMaterial(const Nz::String& materialName); + void SetMaterial(int materialIndex); void SetPosition(const Vector3f& position); void SetRotation(const Quaternionf& rotation); + void SetUserdata(void* ud); RigidBody3D& operator=(const RigidBody3D& object); RigidBody3D& operator=(RigidBody3D&& object); @@ -82,6 +87,7 @@ namespace Nz Vector3f m_torqueAccumulator; NewtonBody* m_body; PhysWorld3D* m_world; + void* m_userdata; float m_gravityFactor; float m_mass; }; diff --git a/src/Nazara/Physics3D/PhysWorld3D.cpp b/src/Nazara/Physics3D/PhysWorld3D.cpp index be2677ef6..0b2634be1 100644 --- a/src/Nazara/Physics3D/PhysWorld3D.cpp +++ b/src/Nazara/Physics3D/PhysWorld3D.cpp @@ -3,7 +3,9 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include +#include #include +#include #include namespace Nz @@ -15,6 +17,8 @@ namespace Nz { m_world = NewtonCreate(); NewtonWorldSetUserData(m_world, this); + + m_materialIds.emplace("default", NewtonMaterialGetDefaultGroupID(m_world)); } PhysWorld3D::~PhysWorld3D() @@ -22,6 +26,16 @@ namespace Nz NewtonDestroy(m_world); } + int PhysWorld3D::CreateMaterial(Nz::String name) + { + NazaraAssert(m_materialIds.find(name) == m_materialIds.end(), "Material \"" + name + "\" already exists"); + + int materialId = NewtonMaterialCreateGroupID(m_world); + m_materialIds.emplace(std::move(name), materialId); + + return materialId; + } + Vector3f PhysWorld3D::GetGravity() const { return m_gravity; @@ -32,6 +46,14 @@ namespace Nz return m_world; } + int PhysWorld3D::GetMaterial(const Nz::String& name) + { + auto it = m_materialIds.find(name); + NazaraAssert(it != m_materialIds.end(), "Material \"" + name + "\" does not exists"); + + return it->second; + } + float PhysWorld3D::GetStepSize() const { return m_stepSize; @@ -52,6 +74,22 @@ namespace Nz m_stepSize = stepSize; } + void PhysWorld3D::SetMaterialCollisionCallback(int firstMaterial, int secondMaterial, CollisionCallback callback) + { + static_assert(sizeof(Nz::UInt64) >= 2 * sizeof(int), "Oops"); + + auto callbackPtr = std::make_unique(); + callbackPtr->collisionCallback = std::move(callback); + + NewtonMaterialSetCollisionCallback(m_world, firstMaterial, secondMaterial, callbackPtr.get(), nullptr, ProcessContact); + + Nz::UInt64 firstMaterialId(firstMaterial); + Nz::UInt64 secondMaterialId(secondMaterial); + + Nz::UInt64 callbackIndex = firstMaterialId << 32 | secondMaterialId; + m_callbacks[callbackIndex] = std::move(callbackPtr); + } + void PhysWorld3D::Step(float timestep) { m_timestepAccumulator += timestep; @@ -62,4 +100,32 @@ namespace Nz m_timestepAccumulator -= m_stepSize; } } + + void PhysWorld3D::ProcessContact(const NewtonJoint* const contactJoint, float timestep, int threadIndex) + { + Nz::RigidBody3D* bodyA = static_cast(NewtonBodyGetUserData(NewtonJointGetBody0(contactJoint))); + Nz::RigidBody3D* bodyB = static_cast(NewtonBodyGetUserData(NewtonJointGetBody1(contactJoint))); + assert(bodyA && bodyB); + + using ContactJoint = void*; + + // Query all joints first, to prevent removing a joint from the list while iterating on it + Nz::StackArray contacts = NazaraStackAllocationNoInit(ContactJoint, NewtonContactJointGetContactCount(contactJoint)); + std::size_t contactIndex = 0; + for (ContactJoint contact = NewtonContactJointGetFirstContact(contactJoint); contact; contact = NewtonContactJointGetNextContact(contactJoint, contact)) + { + assert(contactIndex < contacts.size()); + contacts[contactIndex++] = contact; + } + + for (ContactJoint contact : contacts) + { + NewtonMaterial* material = NewtonContactGetMaterial(contact); + Callback* callbackData = static_cast(NewtonMaterialGetMaterialPairUserData(material)); + assert(callbackData); + + if (!callbackData->collisionCallback(*bodyA, *bodyB)) + NewtonContactJointRemoveContact(contactJoint, contact); + } + } } diff --git a/src/Nazara/Physics3D/RigidBody3D.cpp b/src/Nazara/Physics3D/RigidBody3D.cpp index 60db49240..69090d16e 100644 --- a/src/Nazara/Physics3D/RigidBody3D.cpp +++ b/src/Nazara/Physics3D/RigidBody3D.cpp @@ -213,6 +213,11 @@ namespace Nz return center; } + int RigidBody3D::GetMaterial() const + { + return NewtonBodyGetMaterialGroupID(m_body); + } + const Matrix4f& RigidBody3D::GetMatrix() const { return m_matrix; @@ -228,6 +233,11 @@ namespace Nz return m_matrix.GetRotation(); } + void* RigidBody3D::GetUserdata() const + { + return m_userdata; + } + PhysWorld3D* RigidBody3D::GetWorld() const { return m_world; @@ -322,6 +332,16 @@ namespace Nz NewtonBodySetCentreOfMass(m_body, center); } + void RigidBody3D::SetMaterial(const Nz::String& materialName) + { + SetMaterial(m_world->GetMaterial(materialName)); + } + + void RigidBody3D::SetMaterial(int materialIndex) + { + NewtonBodySetMaterialGroupID(m_body, materialIndex); + } + void RigidBody3D::SetPosition(const Vector3f& position) { m_matrix.SetTranslation(position); @@ -336,6 +356,11 @@ namespace Nz UpdateBody(); } + void RigidBody3D::SetUserdata(void* ud) + { + m_userdata = ud; + } + RigidBody3D& RigidBody3D::operator=(const RigidBody3D& object) { RigidBody3D physObj(object); From 84a3fc1a9145abfee31c73cfaea576d6619b3a4d Mon Sep 17 00:00:00 2001 From: Lynix Date: Sun, 17 Dec 2017 19:08:45 +0100 Subject: [PATCH 2/3] Physics3D/World: Add more control on materials --- include/Nazara/Physics3D/PhysWorld3D.hpp | 12 ++++++- src/Nazara/Physics3D/PhysWorld3D.cpp | 46 ++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/include/Nazara/Physics3D/PhysWorld3D.hpp b/include/Nazara/Physics3D/PhysWorld3D.hpp index 427aaeb69..411d3d4e7 100644 --- a/include/Nazara/Physics3D/PhysWorld3D.hpp +++ b/include/Nazara/Physics3D/PhysWorld3D.hpp @@ -13,7 +13,9 @@ #include #include +class NewtonBody; class NewtonJoint; +class NewtonMaterial; class NewtonWorld; namespace Nz @@ -23,6 +25,7 @@ namespace Nz class NAZARA_PHYSICS3D_API PhysWorld3D { public: + using AABBOverlapCallback = std::function; using CollisionCallback = std::function; PhysWorld3D(); @@ -41,7 +44,12 @@ namespace Nz void SetSolverModel(unsigned int model); void SetStepSize(float stepSize); - void SetMaterialCollisionCallback(int firstMaterial, int secondMaterial, CollisionCallback callback); + void SetMaterialCollisionCallback(int firstMaterial, int secondMaterial, AABBOverlapCallback aabbOverlapCallback, CollisionCallback collisionCallback); + void SetMaterialDefaultCollidable(int firstMaterial, int secondMaterial, bool collidable); + void SetMaterialDefaultElasticity(int firstMaterial, int secondMaterial, float elasticCoef); + void SetMaterialDefaultFriction(int firstMaterial, int secondMaterial, float staticFriction, float kineticFriction); + void SetMaterialDefaultSoftness(int firstMaterial, int secondMaterial, float softness); + void SetMaterialSurfaceThickness(int firstMaterial, int secondMaterial, float thickness); void Step(float timestep); @@ -51,9 +59,11 @@ namespace Nz private: struct Callback { + AABBOverlapCallback aabbOverlapCallback; CollisionCallback collisionCallback; }; + static int OnAABBOverlap(const NewtonMaterial* const material, const NewtonBody* const body0, const NewtonBody* const body1, int threadIndex); static void ProcessContact(const NewtonJoint* const contact, float timestep, int threadIndex); std::unordered_map> m_callbacks; diff --git a/src/Nazara/Physics3D/PhysWorld3D.cpp b/src/Nazara/Physics3D/PhysWorld3D.cpp index 0b2634be1..4613b7f32 100644 --- a/src/Nazara/Physics3D/PhysWorld3D.cpp +++ b/src/Nazara/Physics3D/PhysWorld3D.cpp @@ -74,14 +74,15 @@ namespace Nz m_stepSize = stepSize; } - void PhysWorld3D::SetMaterialCollisionCallback(int firstMaterial, int secondMaterial, CollisionCallback callback) + void PhysWorld3D::SetMaterialCollisionCallback(int firstMaterial, int secondMaterial, AABBOverlapCallback aabbOverlapCallback, CollisionCallback collisionCallback) { static_assert(sizeof(Nz::UInt64) >= 2 * sizeof(int), "Oops"); auto callbackPtr = std::make_unique(); - callbackPtr->collisionCallback = std::move(callback); + callbackPtr->aabbOverlapCallback = std::move(aabbOverlapCallback); + callbackPtr->collisionCallback = std::move(collisionCallback); - NewtonMaterialSetCollisionCallback(m_world, firstMaterial, secondMaterial, callbackPtr.get(), nullptr, ProcessContact); + NewtonMaterialSetCollisionCallback(m_world, firstMaterial, secondMaterial, callbackPtr.get(), (callbackPtr->aabbOverlapCallback) ? OnAABBOverlap : nullptr, (callbackPtr->collisionCallback) ? ProcessContact : nullptr); Nz::UInt64 firstMaterialId(firstMaterial); Nz::UInt64 secondMaterialId(secondMaterial); @@ -90,6 +91,31 @@ namespace Nz m_callbacks[callbackIndex] = std::move(callbackPtr); } + void PhysWorld3D::SetMaterialDefaultCollidable(int firstMaterial, int secondMaterial, bool collidable) + { + NewtonMaterialSetDefaultCollidable(m_world, firstMaterial, secondMaterial, collidable); + } + + void PhysWorld3D::SetMaterialDefaultElasticity(int firstMaterial, int secondMaterial, float elasticCoef) + { + NewtonMaterialSetDefaultElasticity(m_world, firstMaterial, secondMaterial, elasticCoef); + } + + void PhysWorld3D::SetMaterialDefaultFriction(int firstMaterial, int secondMaterial, float staticFriction, float kineticFriction) + { + NewtonMaterialSetDefaultFriction(m_world, firstMaterial, secondMaterial, staticFriction, kineticFriction); + } + + void PhysWorld3D::SetMaterialDefaultSoftness(int firstMaterial, int secondMaterial, float softness) + { + NewtonMaterialSetDefaultSoftness(m_world, firstMaterial, secondMaterial, softness); + } + + void PhysWorld3D::SetMaterialSurfaceThickness(int firstMaterial, int secondMaterial, float thickness) + { + NewtonMaterialSetSurfaceThickness(m_world, firstMaterial, secondMaterial, thickness); + } + void PhysWorld3D::Step(float timestep) { m_timestepAccumulator += timestep; @@ -101,6 +127,19 @@ namespace Nz } } + int PhysWorld3D::OnAABBOverlap(const NewtonMaterial* const material, const NewtonBody* const body0, const NewtonBody* const body1, int threadIndex) + { + Nz::RigidBody3D* bodyA = static_cast(NewtonBodyGetUserData(body0)); + Nz::RigidBody3D* bodyB = static_cast(NewtonBodyGetUserData(body1)); + assert(bodyA && bodyB); + + Callback* callbackData = static_cast(NewtonMaterialGetMaterialPairUserData(material)); + assert(callbackData); + assert(callbackData->aabbOverlapCallback); + + return callbackData->aabbOverlapCallback(*bodyA, *bodyB); + } + void PhysWorld3D::ProcessContact(const NewtonJoint* const contactJoint, float timestep, int threadIndex) { Nz::RigidBody3D* bodyA = static_cast(NewtonBodyGetUserData(NewtonJointGetBody0(contactJoint))); @@ -123,6 +162,7 @@ namespace Nz NewtonMaterial* material = NewtonContactGetMaterial(contact); Callback* callbackData = static_cast(NewtonMaterialGetMaterialPairUserData(material)); assert(callbackData); + assert(callbackData->collisionCallback); if (!callbackData->collisionCallback(*bodyA, *bodyB)) NewtonContactJointRemoveContact(contactJoint, contact); From b47f5210e0bab8cca412450e44feec3860297730 Mon Sep 17 00:00:00 2001 From: Lynix Date: Sat, 20 Jan 2018 19:36:21 +0100 Subject: [PATCH 3/3] Physics3D/PhysWorld3D: Add ForEachBodyInAABB method --- include/Nazara/Physics3D/PhysWorld3D.hpp | 4 ++++ src/Nazara/Physics3D/PhysWorld3D.cpp | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/include/Nazara/Physics3D/PhysWorld3D.hpp b/include/Nazara/Physics3D/PhysWorld3D.hpp index 887982c98..428685b3f 100644 --- a/include/Nazara/Physics3D/PhysWorld3D.hpp +++ b/include/Nazara/Physics3D/PhysWorld3D.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -25,6 +26,7 @@ namespace Nz class NAZARA_PHYSICS3D_API PhysWorld3D { public: + using BodyIterator = std::function; using AABBOverlapCallback = std::function; using CollisionCallback = std::function; @@ -35,6 +37,8 @@ namespace Nz int CreateMaterial(Nz::String name = Nz::String()); + void ForEachBodyInAABB(const Nz::Boxf& box, BodyIterator iterator); + Vector3f GetGravity() const; NewtonWorld* GetHandle() const; int GetMaterial(const Nz::String& name); diff --git a/src/Nazara/Physics3D/PhysWorld3D.cpp b/src/Nazara/Physics3D/PhysWorld3D.cpp index 4613b7f32..84264112b 100644 --- a/src/Nazara/Physics3D/PhysWorld3D.cpp +++ b/src/Nazara/Physics3D/PhysWorld3D.cpp @@ -36,6 +36,18 @@ namespace Nz return materialId; } + void PhysWorld3D::ForEachBodyInAABB(const Nz::Boxf& box, BodyIterator iterator) + { + auto NewtonCallback = [](const NewtonBody* const body, void* const userdata) -> int + { + BodyIterator& iterator = *static_cast(userdata); + RigidBody3D* nzBody = static_cast(NewtonBodyGetUserData(body)); + return iterator(*nzBody); + }; + + NewtonWorldForEachBodyInAABBDo(m_world, box.GetMinimum(), box.GetMaximum(), NewtonCallback, &iterator); + } + Vector3f PhysWorld3D::GetGravity() const { return m_gravity;