From afdc0187787670b7a5d62c9c0a6a0ab0b345ef4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Thu, 27 Jul 2017 11:39:23 +0200 Subject: [PATCH] Physics2D/Collider2D: Implement CompoundCollider2D and ConvexCollider2D --- include/Nazara/Physics2D/Collider2D.hpp | 68 ++++++++++++++++--- include/Nazara/Physics2D/Collider2D.inl | 14 ++++ include/Nazara/Physics2D/Enums.hpp | 1 + src/Nazara/Physics2D/Collider2D.cpp | 86 +++++++++++++++++++------ src/Nazara/Physics2D/RigidBody2D.cpp | 6 +- 5 files changed, 143 insertions(+), 32 deletions(-) diff --git a/include/Nazara/Physics2D/Collider2D.hpp b/include/Nazara/Physics2D/Collider2D.hpp index b09e8781f..06d55300b 100644 --- a/include/Nazara/Physics2D/Collider2D.hpp +++ b/include/Nazara/Physics2D/Collider2D.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ namespace Nz { friend Collider2DLibrary; friend RigidBody2D; + friend class CompoundCollider2D; //< See CompoundCollider2D::CreateShapes public: inline Collider2D(); @@ -40,7 +42,7 @@ namespace Nz Collider2D(Collider2D&&) = delete; virtual ~Collider2D(); - virtual float ComputeInertialMatrix(float mass) const = 0; + virtual float ComputeMomentOfInertia(float mass) const = 0; inline Nz::UInt32 GetCategoryMask() const; inline Nz::UInt32 GetCollisionGroup() const; @@ -64,7 +66,7 @@ namespace Nz NazaraSignal(OnColliderRelease, const Collider2D* /*collider*/); protected: - virtual std::vector CreateShapes(RigidBody2D* body) const = 0; + virtual void CreateShapes(RigidBody2D* body, std::vector& shapes) const = 0; bool m_trigger; Nz::UInt32 m_categoryMask; @@ -89,7 +91,7 @@ namespace Nz BoxCollider2D(const Vector2f& size, float radius = 0.f); BoxCollider2D(const Rectf& rect, float radius = 0.f); - float ComputeInertialMatrix(float mass) const override; + float ComputeMomentOfInertia(float mass) const override; inline const Rectf& GetRect() const; inline Vector2f GetSize() const; @@ -98,7 +100,7 @@ namespace Nz template static BoxCollider2DRef New(Args&&... args); private: - std::vector CreateShapes(RigidBody2D* body) const override; + void CreateShapes(RigidBody2D* body, std::vector& shapes) const override; Rectf m_rect; float m_radius; @@ -114,7 +116,7 @@ namespace Nz public: CircleCollider2D(float radius, const Vector2f& offset = Vector2f::Zero()); - float ComputeInertialMatrix(float mass) const override; + float ComputeMomentOfInertia(float mass) const override; inline float GetRadius() const; ColliderType2D GetType() const override; @@ -122,12 +124,58 @@ namespace Nz template static CircleCollider2DRef New(Args&&... args); private: - std::vector CreateShapes(RigidBody2D* body) const override; + void CreateShapes(RigidBody2D* body, std::vector& shapes) const override; Vector2f m_offset; float m_radius; }; + class CompoundCollider2D; + + using CompoundCollider2DConstRef = ObjectRef; + using CompoundCollider2DRef = ObjectRef; + + class NAZARA_PHYSICS2D_API CompoundCollider2D : public Collider2D + { + public: + CompoundCollider2D(std::vector geoms); + + float ComputeMomentOfInertia(float mass) const override; + + inline const std::vector& GetGeoms() const; + ColliderType2D GetType() const override; + + template static CompoundCollider2DRef New(Args&&... args); + + private: + void CreateShapes(RigidBody2D* body, std::vector& shapes) const override; + + std::vector m_geoms; + }; + + class ConvexCollider2D; + + using ConvexCollider2DConstRef = ObjectRef; + using ConvexCollider2DRef = ObjectRef; + + class NAZARA_PHYSICS2D_API ConvexCollider2D : public Collider2D + { + public: + ConvexCollider2D(SparsePtr vertices, std::size_t vertexCount, float radius = 0.f); + + float ComputeMomentOfInertia(float mass) const override; + + ColliderType2D GetType() const override; + + template static ConvexCollider2DRef New(Args&&... args); + + private: + void CreateShapes(RigidBody2D* body, std::vector& shapes) const override; + + std::vector m_vertices; + float m_radius; + }; + class NullCollider2D; using NullCollider2DConstRef = ObjectRef; @@ -138,14 +186,14 @@ namespace Nz public: NullCollider2D() = default; - float ComputeInertialMatrix(float mass) const override; + float ComputeMomentOfInertia(float mass) const override; ColliderType2D GetType() const override; template static NullCollider2DRef New(Args&&... args); private: - std::vector CreateShapes(RigidBody2D* body) const override; + void CreateShapes(RigidBody2D* body, std::vector& shapes) const override; }; class SegmentCollider2D; @@ -158,7 +206,7 @@ namespace Nz public: inline SegmentCollider2D(const Vector2f& first, const Vector2f& second, float thickness = 1.f); - float ComputeInertialMatrix(float mass) const override; + float ComputeMomentOfInertia(float mass) const override; inline const Vector2f& GetFirstPoint() const; inline float GetLength() const; @@ -168,7 +216,7 @@ namespace Nz template static SegmentCollider2DRef New(Args&&... args); private: - std::vector CreateShapes(RigidBody2D* body) const override; + void CreateShapes(RigidBody2D* body, std::vector& shapes) const override; Vector2f m_first; Vector2f m_second; diff --git a/include/Nazara/Physics2D/Collider2D.inl b/include/Nazara/Physics2D/Collider2D.inl index 60cf8559f..dee95687a 100644 --- a/include/Nazara/Physics2D/Collider2D.inl +++ b/include/Nazara/Physics2D/Collider2D.inl @@ -100,6 +100,20 @@ namespace Nz return object.release(); } + inline const std::vector& Nz::CompoundCollider2D::GetGeoms() const + { + return m_geoms; + } + + template + CompoundCollider2DRef CompoundCollider2D::New(Args&&... args) + { + std::unique_ptr object(new CompoundCollider2D(std::forward(args)...)); + object->SetPersistent(false); + + return object.release(); + } + template NullCollider2DRef NullCollider2D::New(Args&&... args) { diff --git a/include/Nazara/Physics2D/Enums.hpp b/include/Nazara/Physics2D/Enums.hpp index 86c1411f4..8cbb73d39 100644 --- a/include/Nazara/Physics2D/Enums.hpp +++ b/include/Nazara/Physics2D/Enums.hpp @@ -12,6 +12,7 @@ namespace Nz enum ColliderType2D { ColliderType2D_Box, + ColliderType2D_Compound, ColliderType2D_Convex, ColliderType2D_Circle, ColliderType2D_Null, diff --git a/src/Nazara/Physics2D/Collider2D.cpp b/src/Nazara/Physics2D/Collider2D.cpp index 6309b08fd..9c7d964a5 100644 --- a/src/Nazara/Physics2D/Collider2D.cpp +++ b/src/Nazara/Physics2D/Collider2D.cpp @@ -15,7 +15,9 @@ namespace Nz { cpShapeFilter filter = cpShapeFilterNew(m_collisionGroup, m_categoryMask, m_collisionMask); - std::vector shapes = CreateShapes(body); + std::vector shapes; + CreateShapes(body, shapes); + for (cpShape* shape : shapes) { cpShapeSetFilter(shape, filter); @@ -39,7 +41,7 @@ namespace Nz { } - float BoxCollider2D::ComputeInertialMatrix(float mass) const + float BoxCollider2D::ComputeMomentOfInertia(float mass) const { return static_cast(cpMomentForBox2(mass, cpBBNew(m_rect.x, m_rect.y, m_rect.x + m_rect.width, m_rect.y + m_rect.height))); } @@ -49,12 +51,9 @@ namespace Nz return ColliderType2D_Box; } - std::vector BoxCollider2D::CreateShapes(RigidBody2D* body) const + void BoxCollider2D::CreateShapes(RigidBody2D* body, std::vector& shapes) const { - std::vector shapes; shapes.push_back(cpBoxShapeNew2(body->GetHandle(), cpBBNew(m_rect.x, m_rect.y, m_rect.x + m_rect.width, m_rect.y + m_rect.height), m_radius)); - - return shapes; } /******************************** CircleCollider2D *********************************/ @@ -65,7 +64,7 @@ namespace Nz { } - float CircleCollider2D::ComputeInertialMatrix(float mass) const + float CircleCollider2D::ComputeMomentOfInertia(float mass) const { return static_cast(cpMomentForCircle(mass, 0.f, m_radius, cpv(m_offset.x, m_offset.y))); } @@ -75,12 +74,66 @@ namespace Nz return ColliderType2D_Circle; } - std::vector CircleCollider2D::CreateShapes(RigidBody2D* body) const + void CircleCollider2D::CreateShapes(RigidBody2D* body, std::vector& shapes) const { - std::vector shapes; shapes.push_back(cpCircleShapeNew(body->GetHandle(), m_radius, cpv(m_offset.x, m_offset.y))); + } - return shapes; + /******************************** CompoundCollider2D *********************************/ + + CompoundCollider2D::CompoundCollider2D(std::vector geoms) : + m_geoms(std::move(geoms)) + { + } + + float CompoundCollider2D::ComputeMomentOfInertia(float mass) const + { + ///TODO: Correctly compute moment using parallel axis theorem: + /// https://chipmunk-physics.net/forum/viewtopic.php?t=1056 + float momentOfInertia = 0.f; + for (const auto& geom : m_geoms) + momentOfInertia += geom->ComputeMomentOfInertia(mass); //< Eeeer + + return momentOfInertia; + } + + ColliderType2D CompoundCollider2D::GetType() const + { + return ColliderType2D_Compound; + } + + void CompoundCollider2D::CreateShapes(RigidBody2D* body, std::vector& shapes) const + { + // Since C++ does not allow protected call from other objects, we have to be a friend of Collider2D, yay + for (const auto& geom : m_geoms) + geom->CreateShapes(body, shapes); + } + + /******************************** ConvexCollider2D *********************************/ + + ConvexCollider2D::ConvexCollider2D(SparsePtr vertices, std::size_t vertexCount, float radius) : + m_radius(radius) + { + m_vertices.resize(vertexCount); + for (std::size_t i = 0; i < vertexCount; ++i) + m_vertices[i].Set(*vertices++); + } + + float ConvexCollider2D::ComputeMomentOfInertia(float mass) const + { + static_assert(sizeof(cpVect) == sizeof(Vector2d), "Chipmunk vector is not equivalent to Vector2d"); + + return static_cast(cpMomentForPoly(mass, int(m_vertices.size()), reinterpret_cast(m_vertices.data()), cpv(0.0, 0.0), m_radius)); + } + + ColliderType2D ConvexCollider2D::GetType() const + { + return ColliderType2D_Convex; + } + + void ConvexCollider2D::CreateShapes(RigidBody2D* body, std::vector& shapes) const + { + shapes.push_back(cpPolyShapeNew(body->GetHandle(), int(m_vertices.size()), reinterpret_cast(m_vertices.data()), cpTransformIdentity, m_radius)); } /********************************* NullCollider2D **********************************/ @@ -90,19 +143,18 @@ namespace Nz return ColliderType2D_Null; } - float NullCollider2D::ComputeInertialMatrix(float /*mass*/) const + float NullCollider2D::ComputeMomentOfInertia(float /*mass*/) const { return 0.f; } - std::vector NullCollider2D::CreateShapes(RigidBody2D* /*body*/) const + void NullCollider2D::CreateShapes(RigidBody2D* /*body*/, std::vector& /*shapes*/) const { - return std::vector(); } /******************************** SegmentCollider2D *********************************/ - float SegmentCollider2D::ComputeInertialMatrix(float mass) const + float SegmentCollider2D::ComputeMomentOfInertia(float mass) const { return static_cast(cpMomentForSegment(mass, cpv(m_first.x, m_first.y), cpv(m_second.x, m_second.y), m_thickness)); } @@ -112,12 +164,8 @@ namespace Nz return ColliderType2D_Segment; } - std::vector SegmentCollider2D::CreateShapes(RigidBody2D* body) const + void SegmentCollider2D::CreateShapes(RigidBody2D* body, std::vector& shapes) const { - std::vector shapes; shapes.push_back(cpSegmentShapeNew(body->GetHandle(), cpv(m_first.x, m_first.y), cpv(m_second.x, m_second.y), m_thickness)); - - return shapes; } - } diff --git a/src/Nazara/Physics2D/RigidBody2D.cpp b/src/Nazara/Physics2D/RigidBody2D.cpp index 9c25c1cbe..85d43ee76 100644 --- a/src/Nazara/Physics2D/RigidBody2D.cpp +++ b/src/Nazara/Physics2D/RigidBody2D.cpp @@ -245,7 +245,7 @@ namespace Nz cpSpaceAddShape(space, shape); } - cpBodySetMoment(m_handle, m_geom->ComputeInertialMatrix(m_mass)); + cpBodySetMoment(m_handle, m_geom->ComputeMomentOfInertia(m_mass)); } void RigidBody2D::SetMass(float mass) @@ -257,7 +257,7 @@ namespace Nz m_world->RegisterPostStep(this, [mass](Nz::RigidBody2D* body) { cpBodySetMass(body->GetHandle(), mass); - cpBodySetMoment(body->GetHandle(), body->GetGeom()->ComputeInertialMatrix(mass)); + cpBodySetMoment(body->GetHandle(), body->GetGeom()->ComputeMomentOfInertia(mass)); }); } else @@ -271,7 +271,7 @@ namespace Nz { cpBodySetType(body->GetHandle(), CP_BODY_TYPE_DYNAMIC); cpBodySetMass(body->GetHandle(), mass); - cpBodySetMoment(body->GetHandle(), body->GetGeom()->ComputeInertialMatrix(mass)); + cpBodySetMoment(body->GetHandle(), body->GetGeom()->ComputeMomentOfInertia(mass)); } }); }