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);