Skip to content

Commit

Permalink
Physics2D tests (#129)
Browse files Browse the repository at this point in the history
* Quaternion: Fix singularity on Z axis when converting to euler angles

* CollisionComponent2D: Add method to retrieve AABB

* Collider2D: Fix constructor for Box with Vector2

* Physics2D: Fix rotation (Chipmunk works with radian and Nazara degrees) and copy constructor of RigidBody2D

* Colider2D: Add New for convex and tests for the new classes
  • Loading branch information
Gawaboumga authored and SirLynix committed Aug 20, 2017
1 parent 72abf66 commit c938e62
Show file tree
Hide file tree
Showing 12 changed files with 860 additions and 37 deletions.
1 change: 1 addition & 0 deletions SDK/include/NDK/Components/CollisionComponent2D.hpp
Expand Up @@ -29,6 +29,7 @@ namespace Ndk
CollisionComponent2D(const CollisionComponent2D& collision);
~CollisionComponent2D() = default;

Nz::Rectf GetAABB() const;
const Nz::Collider2DRef& GetGeom() const;

void SetGeom(Nz::Collider2DRef geom);
Expand Down
10 changes: 10 additions & 0 deletions SDK/include/NDK/Components/CollisionComponent2D.inl
Expand Up @@ -34,6 +34,16 @@ namespace Ndk
{
}

/*!
* \brief Gets the collision box representing the entity
* \return The physics collision box
*/

inline Nz::Rectf CollisionComponent2D::GetAABB() const
{
return m_staticBody->GetAABB();
}

/*!
* \brief Gets the geometry representing the entity
* \return A constant reference to the physics geometry
Expand Down
4 changes: 2 additions & 2 deletions include/Nazara/Math/Quaternion.inl
Expand Up @@ -481,11 +481,11 @@ namespace Nz
T test = x * y + z * w;
if (test > F(0.499))
// singularity at north pole
return EulerAngles<T>(FromDegrees(F(90.0)), FromRadians(F(2.0) * std::atan2(x, w)), F(0.0));
return EulerAngles<T>(F(0.0), FromRadians(F(2.0) * std::atan2(x, w)), FromDegrees(F(90.0)));

if (test < F(-0.499))
// singularity at south pole
return EulerAngles<T>(FromDegrees(F(-90.0)), FromRadians(F(-2.0) * std::atan2(x, w)), F(0.0));
return EulerAngles<T>(F(0.0), FromRadians(F(-2.0) * std::atan2(x, w)), FromDegrees(F(-90.0)));

return EulerAngles<T>(FromRadians(std::atan2(F(2.0) * x * w - F(2.0) * y * z, F(1.0) - F(2.0) * x * x - F(2.0) * z * z)),
FromRadians(std::atan2(F(2.0) * y * w - F(2.0) * x * z, F(1.0) - F(2.0) * y * y - F(2.0) * z * z)),
Expand Down
9 changes: 9 additions & 0 deletions include/Nazara/Physics2D/Collider2D.inl
Expand Up @@ -114,6 +114,15 @@ namespace Nz
return object.release();
}

template<typename... Args>
ConvexCollider2DRef ConvexCollider2D::New(Args&&... args)
{
std::unique_ptr<ConvexCollider2D> object(new ConvexCollider2D(std::forward<Args>(args)...));
object->SetPersistent(false);

return object.release();
}

template<typename... Args>
NullCollider2DRef NullCollider2D::New(Args&&... args)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Nazara/Physics2D/Collider2D.cpp
Expand Up @@ -31,7 +31,7 @@ namespace Nz
/******************************** BoxCollider2D *********************************/

BoxCollider2D::BoxCollider2D(const Vector2f& size, float radius) :
BoxCollider2D(Rectf(-size.x / 2.f, -size.y / 2.f, size.x / 2.f, size.y / 2.f), radius)
BoxCollider2D(Rectf(-size.x / 2.f, -size.y / 2.f, size.x, size.y), radius)
{
}

Expand Down
42 changes: 28 additions & 14 deletions src/Nazara/Physics2D/RigidBody2D.cpp
Expand Up @@ -45,8 +45,28 @@ namespace Nz

Create();

cpBodySetMass(m_handle, cpBodyGetMass(object.GetHandle()));
cpBodySetMoment(m_handle, cpBodyGetMoment(object.GetHandle()));

SetGeom(object.GetGeom());
SetMass(object.GetMass());

cpBodySetForce(m_handle, cpBodyGetForce(object.GetHandle()));
cpBodySetTorque(m_handle, cpBodyGetTorque(object.GetHandle()));

cpBodySetAngle(m_handle, cpBodyGetAngle(object.GetHandle()));
cpBodySetAngularVelocity(m_handle, cpBodyGetAngularVelocity(object.GetHandle()));
cpBodySetCenterOfGravity(m_handle, cpBodyGetCenterOfGravity(object.GetHandle()));
cpBodySetPosition(m_handle, cpBodyGetPosition(object.GetHandle()));
cpBodySetVelocity(m_handle, cpBodyGetVelocity(object.GetHandle()));

for (int i = 0; i != m_shapes.size(); ++i)
m_shapes[i]->bb = cpShapeCacheBB(object.m_shapes[i]);

cpBodySetMass(m_handle, cpBodyGetMass(object.GetHandle()));
cpBodySetMoment(m_handle, cpBodyGetMoment(object.GetHandle()));

m_handle->m = object.GetHandle()->m;
}

RigidBody2D::RigidBody2D(RigidBody2D&& object) :
Expand Down Expand Up @@ -93,7 +113,7 @@ namespace Nz
cpBodyApplyForceAtLocalPoint(m_handle, cpv(force.x, force.y), cpv(point.x, point.y));
break;
}
}
}

void RigidBody2D::AddImpulse(const Vector2f& impulse, CoordSys coordSys)
{
Expand All @@ -116,7 +136,7 @@ namespace Nz

void RigidBody2D::AddTorque(float torque)
{
cpBodySetTorque(m_handle, cpBodyGetTorque(m_handle) + torque);
cpBodySetTorque(m_handle, cpBodyGetTorque(m_handle) + ToRadians(torque));
}

Rectf RigidBody2D::GetAABB() const
Expand All @@ -134,7 +154,7 @@ namespace Nz

float RigidBody2D::GetAngularVelocity() const
{
return static_cast<float>(cpBodyGetAngularVelocity(m_handle));
return FromRadians(static_cast<float>(cpBodyGetAngularVelocity(m_handle)));
}

const Collider2DRef& RigidBody2D::GetGeom() const
Expand Down Expand Up @@ -177,7 +197,7 @@ namespace Nz

float RigidBody2D::GetRotation() const
{
return static_cast<float>(cpBodyGetAngle(m_handle));
return FromRadians(static_cast<float>(cpBodyGetAngle(m_handle)));
}

void* RigidBody2D::GetUserdata() const
Expand Down Expand Up @@ -208,7 +228,7 @@ namespace Nz

void RigidBody2D::SetAngularVelocity(float angularVelocity)
{
cpBodySetAngularVelocity(m_handle, angularVelocity);
cpBodySetAngularVelocity(m_handle, ToRadians(angularVelocity));
}

void RigidBody2D::SetGeom(Collider2DRef geom)
Expand All @@ -217,18 +237,11 @@ namespace Nz
// So let's save some attributes of the body, destroy it and rebuild it
if (m_geom)
{
cpVect pos = cpBodyGetPosition(m_handle);
cpFloat mass = cpBodyGetMass(m_handle);
cpFloat moment = cpBodyGetMoment(m_handle);
cpFloat rot = cpBodyGetAngle(m_handle);
cpVect vel = cpBodyGetVelocity(m_handle);

Destroy();
Create(float(mass), float(moment));

cpBodySetAngle(m_handle, rot);
cpBodySetPosition(m_handle, pos);
cpBodySetVelocity(m_handle, vel);
Create(static_cast<float>(mass), static_cast<float>(moment));
}

if (geom)
Expand Down Expand Up @@ -302,7 +315,7 @@ namespace Nz

void RigidBody2D::SetRotation(float rotation)
{
cpBodySetAngle(m_handle, rotation);
cpBodySetAngle(m_handle, ToRadians(rotation));
}

void RigidBody2D::SetUserdata(void* ud)
Expand Down Expand Up @@ -369,5 +382,6 @@ namespace Nz
cpSpaceRemoveBody(space, m_handle);
cpBodyFree(m_handle);
}
m_shapes.clear();
}
}
47 changes: 42 additions & 5 deletions tests/Engine/Math/Quaternion.cpp
Expand Up @@ -173,15 +173,52 @@ SCENARIO("Quaternion", "[MATH][QUATERNION]")

WHEN("We get the rotation between two vectors")
{
/*TODO
* Nz::Quaternionf rotationBetweenXY = Nz::Quaternionf::RotationBetween(Nz::Vector3f::UnitX(), Nz::Vector3f::UnitY());
Nz::Quaternionf rotationBetweenXY = Nz::Quaternionf::RotationBetween(Nz::Vector3f::UnitX(), Nz::Vector3f::UnitY());

THEN("The rotation in left-handed is 270 degree on z")
THEN("The rotation in right-handed is 90 degree on z")
{
Nz::Quaternionf rotation270Z(Nz::FromDegrees(270.f), Nz::Vector3f::UnitZ());
Nz::Quaternionf rotation90Z(Nz::FromDegrees(90.f), Nz::Vector3f::UnitZ());
REQUIRE(rotation90Z == rotationBetweenXY);
}*/
}
}
}

GIVEN("Different angles")
{
Nz::Quaternionf rotation90X(0.707f, 0.707f, 0.f, 0.f);
Nz::Quaternionf rotation90Y(0.707f, 0.f, 0.707f, 0.f);
Nz::Quaternionf rotation90Z(0.707f, 0.f, 0.f, 0.707f);

Nz::Quaternionf rotation180X(0.f, 1.f, 0.f, 0.f);
Nz::Quaternionf rotation180Y(0.f, 0.f, 1.f, 0.f);
Nz::Quaternionf rotation180Z(0.f, 0.f, 0.f, 1.f);

Nz::Quaternionf rotation270X(-0.707f, 0.707f, 0.f, 0.f);
Nz::Quaternionf rotation270Y(-0.707f, 0.f, 0.707f, 0.f);
Nz::Quaternionf rotation270Z(-0.707f, 0.f, 0.f, 0.707f);

Nz::Quaternionf special(0.707f, 0.006f, 0.006f, 0.707f);

WHEN("We convert them to euler angles")
{
THEN("Those are equal to")
{
CHECK(Nz::NumberEquals(rotation90X.ToEulerAngles().pitch, Nz::FromDegrees(90.f), 0.1f));
CHECK(Nz::NumberEquals(rotation90Y.ToEulerAngles().yaw, Nz::FromDegrees(90.f), 0.1f));
CHECK(Nz::NumberEquals(rotation90Z.ToEulerAngles().roll, Nz::FromDegrees(90.f), 0.1f));

CHECK(rotation180X == Nz::EulerAnglesf(180.f, 0.f, 0.f));
CHECK(rotation180Y == Nz::EulerAnglesf(0.f, 180.f, 0.f));
CHECK(rotation180Z == Nz::EulerAnglesf(0.f, 0.f, 180.f));

CHECK(Nz::NumberEquals(rotation270X.ToEulerAngles().pitch, Nz::FromDegrees(-90.f), 0.1f));
CHECK(Nz::NumberEquals(rotation270Y.ToEulerAngles().yaw, Nz::FromDegrees(-90.f), 0.1f));
CHECK(Nz::NumberEquals(rotation270Z.ToEulerAngles().roll, Nz::FromDegrees(-90.f), 0.1f));

CHECK(Nz::NumberEquals(special.ToEulerAngles().pitch, Nz::FromDegrees(0.f), 0.1f));
CHECK(Nz::NumberEquals(special.ToEulerAngles().yaw, Nz::FromDegrees(1.f), 0.1f));
CHECK(Nz::NumberEquals(special.ToEulerAngles().roll, Nz::FromDegrees(90.f), 0.1f));
}
}
}
}
133 changes: 133 additions & 0 deletions tests/Engine/Physics2D/Collider2D.cpp
@@ -0,0 +1,133 @@
#include <Nazara/Physics2D/Collider2D.hpp>
#include <Catch/catch.hpp>

SCENARIO("Collider2D", "[PHYSICS2D][COLLIDER2D]")
{
GIVEN("No particular elements")
{
WHEN("We construct a box with Rect")
{
Nz::Rectf aabb(5.f, 3.f, 10.f, 6.f);
Nz::BoxCollider2D box(aabb);

THEN("We expect those to be true")
{
CHECK(box.GetRect() == aabb);
CHECK(box.GetSize() == aabb.GetLengths());
CHECK(box.GetType() == Nz::ColliderType2D_Box);
}
}

WHEN("We construct a box with Vector2D")
{
Nz::Vector2f vec(5.f, 3.f);
Nz::Rectf aabb(-2.5f, -1.5f, 5.f, 3.f);
Nz::BoxCollider2D box(vec);

THEN("We expect those to be true")
{
CHECK(box.GetRect() == aabb);
CHECK(box.GetSize() == vec);
CHECK(box.GetType() == Nz::ColliderType2D_Box);
}
}

WHEN("We construct a circle")
{
Nz::Vector2f position(5.f, 3.f);
float radius = 7.f;
Nz::CircleCollider2D circle(radius, position);

THEN("We expect those to be true")
{
CHECK(circle.GetRadius() == Approx(radius));
CHECK(circle.GetType() == Nz::ColliderType2D_Circle);
}
}

WHEN("We construct a compound")
{
Nz::Rectf aabb(0.f, 0.f, 1.f, 1.f);
Nz::BoxCollider2DRef box1 = Nz::BoxCollider2D::New(aabb);
aabb.Translate(Nz::Vector2f::Unit());
Nz::BoxCollider2DRef box2 = Nz::BoxCollider2D::New(aabb);

std::vector<Nz::Collider2DRef> colliders;
colliders.push_back(box1);
colliders.push_back(box2);
Nz::CompoundCollider2D compound(colliders);

THEN("We expect those to be true")
{
CHECK(compound.GetType() == Nz::ColliderType2D_Compound);
}
}

WHEN("We construct a convex")
{
std::vector<Nz::Vector2f> vertices;
vertices.push_back(Nz::Vector2f(0.f, 0.f));
vertices.push_back(Nz::Vector2f(0.f, 1.f));
vertices.push_back(Nz::Vector2f(1.f, 1.f));
vertices.push_back(Nz::Vector2f(1.f, 0.f));

Nz::ConvexCollider2D convex(Nz::SparsePtr<const Nz::Vector2f>(vertices.data()), vertices.size());

THEN("We expect those to be true")
{
CHECK(convex.GetType() == Nz::ColliderType2D_Convex);
}
}

WHEN("We construct a null")
{
Nz::NullCollider2D null;

THEN("We expect those to be true")
{
CHECK(null.GetType() == Nz::ColliderType2D_Null);
}
}

WHEN("We construct a segment")
{
Nz::Vector2f firstPoint(2.f, 1.f);
Nz::Vector2f secondPoint(-4.f, -3.f);
Nz::SegmentCollider2D segment(firstPoint, secondPoint);

THEN("We expect those to be true")
{
CHECK(segment.GetFirstPoint() == firstPoint);
CHECK(segment.GetLength() == firstPoint.Distance(secondPoint));
CHECK(segment.GetSecondPoint() == secondPoint);
CHECK(segment.GetType() == Nz::ColliderType2D_Segment);
}
}

WHEN("We verify general purpose methods")
{
Nz::Rectf aabb(5.f, 3.f, 10.f, 6.f);
Nz::BoxCollider2D box(aabb);

Nz::UInt32 categoryMask = 1;
Nz::UInt32 groupId = 2;
Nz::UInt32 typeId = 3;
Nz::UInt32 mask = 4;
bool trigger = true;
box.SetCategoryMask(categoryMask);
box.SetCollisionGroup(groupId);
box.SetCollisionId(typeId);
box.SetCollisionMask(mask);
box.SetTrigger(trigger);

THEN("We expect those to be true")
{
CHECK(box.GetCategoryMask() == categoryMask);
CHECK(box.GetCollisionGroup() == groupId);
CHECK(box.GetCollisionId() == typeId);
CHECK(box.GetCollisionMask() == mask);
CHECK(box.IsTrigger() == trigger);
}
}
}
}

0 comments on commit c938e62

Please sign in to comment.