Merge pull request #201 from DigitalPulseSoftware/physics2d-position-offset

Add possibility to set a position offset to Physics2D bodies
This commit is contained in:
Jérôme Leclercq 2019-03-28 00:03:41 +01:00 committed by GitHub
commit 03e2bfb833
11 changed files with 136 additions and 48 deletions

View File

@ -176,6 +176,7 @@ Nazara Engine:
- ENet DisconnectLater now reflects libenet behavior (and is waiting for unreliable commands to be sent before disconnecting for good)
- ⚠ Collider3D::ForEachPolygon now takes a void(Vector3f\*, std::size_t) callback (instead of void(float\*, std::size_t))
- Added Collider2D::ForEachPolygon method
- Added RigidBody::[Get|Set]PositionOffset allowing set an offset between body logic position and body physics position (center of mass position)
Nazara Development Kit:
- Added ImageWidget (#139)
@ -248,6 +249,7 @@ Nazara Development Kit:
- Add missing `recomputeMoment` parameter to PhysicsComponent2D::SetMass
- Added possibility of disabling synchronization between PhysicsComponent2D and NodeComponent
- Fixed GraphicsComponent not invalidating render queue on material change (causing crashes or visual errors)
- Added CollisionComponent2D::SetGeomOffset and CollisionComponent2D::Recenter
# 0.4:

View File

@ -20,8 +20,9 @@ namespace Ndk
class NDK_API CollisionComponent2D : public Component<CollisionComponent2D>
{
friend class PhysicsSystem2D;
friend class ConstraintComponent2D;
friend class PhysicsComponent2D;
friend class PhysicsSystem2D;
public:
CollisionComponent2D(Nz::Collider2DRef geom = Nz::Collider2DRef());
@ -30,8 +31,12 @@ namespace Ndk
Nz::Rectf GetAABB() const;
const Nz::Collider2DRef& GetGeom() const;
const Nz::Vector2f& GetGeomOffset() const;
void Recenter(const Nz::Vector2f& origin);
void SetGeom(Nz::Collider2DRef geom);
void SetGeomOffset(const Nz::Vector2f& geomOffset);
CollisionComponent2D& operator=(Nz::Collider2DRef geom);
CollisionComponent2D& operator=(CollisionComponent2D&& collision) = default;
@ -40,7 +45,10 @@ namespace Ndk
private:
void InitializeStaticBody();
Nz::RigidBody2D* GetRigidBody();
const Nz::RigidBody2D* GetRigidBody() const;
Nz::RigidBody2D* GetStaticBody();
const Nz::RigidBody2D* GetStaticBody() const;
void OnAttached() override;
void OnComponentAttached(BaseComponent& component) override;

View File

@ -28,16 +28,6 @@ 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
@ -62,13 +52,13 @@ namespace Ndk
return *this;
}
/*!
* \brief Gets the static body used by the entity
* \return A pointer to the entity
*/
inline Nz::RigidBody2D* CollisionComponent2D::GetStaticBody()
{
return m_staticBody.get();
}
inline const Nz::RigidBody2D* CollisionComponent2D::GetStaticBody() const
{
return m_staticBody.get();
}
}

View File

@ -26,7 +26,7 @@ namespace Ndk
private:
Nz::InstancedRenderableRef GenerateBox(Nz::Boxf box);
Nz::InstancedRenderableRef GenerateCollision2DMesh(Entity* entity, Nz::Vector3f* origin);
Nz::InstancedRenderableRef GenerateCollision2DMesh(Entity* entity, Nz::Vector3f* offset);
Nz::InstancedRenderableRef GenerateCollision3DMesh(Entity* entity);
Nz::MaterialRef GetCollisionMaterial();

View File

@ -17,28 +17,57 @@ namespace Ndk
* \brief NDK class that represents a two-dimensional collision geometry
*/
/*!
* \brief Gets the collision box representing the entity
* \return The physics collision box
*/
Nz::Rectf CollisionComponent2D::GetAABB() const
{
return GetRigidBody()->GetAABB();
}
/*!
* \brief Gets the position offset between the actual rigid body center of mass position and the origin of the geometry
* \return Position offset
*/
const Nz::Vector2f& CollisionComponent2D::GetGeomOffset() const
{
return GetRigidBody()->GetPositionOffset();
}
/*!
* \brief Convenience function to align center of geometry to a specific point
*
* \param geomOffset Position offset
*
* \remark This does not change the center of mass
*/
void CollisionComponent2D::Recenter(const Nz::Vector2f& origin)
{
const Nz::RigidBody2D* rigidBody = GetRigidBody();
SetGeomOffset(origin - rigidBody->GetAABB().GetCenter() + rigidBody->GetPositionOffset());
}
/*!
* \brief Sets geometry for the entity
*
* \param geom Geometry used for collisions
*
* \remark Produces a NazaraAssert if the entity has no physics component and has no static body
*/
void CollisionComponent2D::SetGeom(Nz::Collider2DRef geom)
{
m_geom = std::move(geom);
if (m_entity->HasComponent<PhysicsComponent2D>())
{
// We update the geometry of the PhysiscsObject linked to the PhysicsComponent2D
PhysicsComponent2D& physComponent = m_entity->GetComponent<PhysicsComponent2D>();
physComponent.GetRigidBody()->SetGeom(m_geom);
}
else
{
NazaraAssert(m_staticBody, "An entity without physics component should have a static body");
m_staticBody->SetGeom(m_geom);
}
GetRigidBody()->SetGeom(m_geom);
}
/*!
* \brief Sets the position offset between the actual rigid body center of mass position and the origin of the geometry
*
* \param geomOffset Position offset
*/
void CollisionComponent2D::SetGeomOffset(const Nz::Vector2f& geomOffset)
{
GetRigidBody()->SetPositionOffset(geomOffset);
}
/*!
@ -47,7 +76,6 @@ namespace Ndk
* \remark Produces a NazaraAssert if entity is invalid
* \remark Produces a NazaraAssert if entity is not linked to a world, or the world has no physics system
*/
void CollisionComponent2D::InitializeStaticBody()
{
NazaraAssert(m_entity, "Invalid entity");
@ -67,7 +95,34 @@ namespace Ndk
matrix.MakeIdentity();
m_staticBody->SetPosition(Nz::Vector2f(matrix.GetTranslation()));
}
Nz::RigidBody2D* CollisionComponent2D::GetRigidBody()
{
if (m_entity->HasComponent<PhysicsComponent2D>())
{
PhysicsComponent2D& physComponent = m_entity->GetComponent<PhysicsComponent2D>();
return physComponent.GetRigidBody();
}
else
{
NazaraAssert(m_staticBody, "An entity without physics component should have a static body");
return m_staticBody.get();
}
}
const Nz::RigidBody2D* CollisionComponent2D::GetRigidBody() const
{
if (m_entity->HasComponent<PhysicsComponent2D>())
{
PhysicsComponent2D& physComponent = m_entity->GetComponent<PhysicsComponent2D>();
return physComponent.GetRigidBody();
}
else
{
NazaraAssert(m_staticBody, "An entity without physics component should have a static body");
return m_staticBody.get();
}
}
/*!

View File

@ -31,9 +31,17 @@ namespace Ndk
Nz::PhysWorld2D& world = entityWorld->GetSystem<PhysicsSystem2D>().GetPhysWorld();
Nz::Vector2f positionOffset;
Nz::Collider2DRef geom;
if (m_entity->HasComponent<CollisionComponent2D>())
geom = m_entity->GetComponent<CollisionComponent2D>().GetGeom();
{
const CollisionComponent2D& entityCollision = m_entity->GetComponent<CollisionComponent2D>();
geom = entityCollision.GetGeom();
positionOffset = entityCollision.GetStaticBody()->GetPositionOffset(); //< Calling GetGeomOffset would retrieve current component which is not yet initialized
}
else
positionOffset = Nz::Vector2f::Zero();
Nz::Matrix4f matrix;
if (m_entity->HasComponent<NodeComponent>())
@ -42,6 +50,7 @@ namespace Ndk
matrix.MakeIdentity();
m_object = std::make_unique<Nz::RigidBody2D>(&world, 1.f, geom);
m_object->SetPositionOffset(positionOffset);
m_object->SetPosition(Nz::Vector2f(matrix.GetTranslation()));
m_object->SetUserdata(reinterpret_cast<void*>(static_cast<std::ptrdiff_t>(m_entity->GetId())));
}

View File

@ -232,11 +232,12 @@ namespace Ndk
case DebugDraw::Collider2D:
{
const Nz::Boxf& obb = entityGfx.GetAABB();
CollisionComponent2D& entityCollision2D = entity->GetComponent<CollisionComponent2D>();
Nz::Vector3f origin;
Nz::InstancedRenderableRef renderable = GenerateCollision2DMesh(entity, &origin);
Nz::Vector3f offset;
Nz::InstancedRenderableRef renderable = GenerateCollision2DMesh(entity, &offset);
if (renderable)
entityGfx.Attach(renderable, Nz::Matrix4f::Translate(origin - entityNode.GetPosition()), DebugDrawOrder);
entityGfx.Attach(renderable, Nz::Matrix4f::Translate(offset), DebugDrawOrder);
entityDebug.UpdateDebugRenderable(option, std::move(renderable));
break;
@ -317,7 +318,7 @@ namespace Ndk
return model;
}
Nz::InstancedRenderableRef DebugSystem::GenerateCollision2DMesh(Entity* entity, Nz::Vector3f* origin)
Nz::InstancedRenderableRef DebugSystem::GenerateCollision2DMesh(Entity* entity, Nz::Vector3f* offset)
{
if (entity->HasComponent<CollisionComponent2D>())
{
@ -374,9 +375,12 @@ namespace Ndk
// Find center of mass
if (entity->HasComponent<PhysicsComponent2D>())
*origin = entity->GetComponent<PhysicsComponent2D>().GetMassCenter(Nz::CoordSys_Global);
{
const PhysicsComponent2D& entityPhys = entity->GetComponent<PhysicsComponent2D>();
*offset = entityPhys.GetMassCenter(Nz::CoordSys_Local) + entityCollision.GetGeomOffset();
}
else
*origin = entity->GetComponent<NodeComponent>().GetPosition(Nz::CoordSys_Global);
*offset = entityCollision.GetGeomOffset();
return model;
}

View File

@ -213,7 +213,7 @@ namespace Ndk
Nz::Vector2f newPosition = Nz::Vector2f(node.GetPosition(Nz::CoordSys_Global));
// To move static objects and ensure their collisions, we have to specify them a velocity
// (/!\: the physical motor does not apply the speed on static objects)
// (/!\: the physical engine does not apply the speed on static objects)
if (newPosition != oldPosition)
{
body->SetPosition(newPosition);
@ -222,8 +222,7 @@ namespace Ndk
else
body->SetVelocity(Nz::Vector2f::Zero());
/*
if (newRotation != oldRotation)
/*if (newRotation != oldRotation)
{
Nz::Quaternionf transition = newRotation * oldRotation.GetConjugate();
Nz::EulerAnglesf angles = transition.ToEulerAngles();
@ -235,8 +234,7 @@ namespace Ndk
physObj->SetAngularVelocity(angularVelocity);
}
else
physObj->SetAngularVelocity(Nz::Vector3f::Zero());
*/
physObj->SetAngularVelocity(Nz::Vector3f::Zero());*/
}
}

View File

@ -32,7 +32,7 @@ namespace Nz
RigidBody2D(PhysWorld2D* world, float mass);
RigidBody2D(PhysWorld2D* world, float mass, Collider2DRef geom);
RigidBody2D(const RigidBody2D& object);
RigidBody2D(RigidBody2D&& object);
RigidBody2D(RigidBody2D&& object) noexcept;
~RigidBody2D();
void AddForce(const Vector2f& force, CoordSys coordSys = CoordSys_Global);
@ -60,6 +60,7 @@ namespace Nz
Vector2f GetMassCenter(CoordSys coordSys = CoordSys_Local) const;
float GetMomentOfInertia() const;
Vector2f GetPosition() const;
inline const Vector2f& GetPositionOffset() const;
RadianAnglef GetRotation() const;
inline std::size_t GetShapeCount() const;
std::size_t GetShapeIndex(cpShape* shape) const;
@ -87,6 +88,7 @@ namespace Nz
void SetMassCenter(const Vector2f& center, CoordSys coordSys = CoordSys_Local);
void SetMomentOfInertia(float moment);
void SetPosition(const Vector2f& position);
void SetPositionOffset(const Vector2f& offset);
void SetRotation(const RadianAnglef& rotation);
void SetSurfaceVelocity(const Vector2f& surfaceVelocity);
void SetSurfaceVelocity(std::size_t shapeIndex, const Vector2f& surfaceVelocity);
@ -114,6 +116,7 @@ namespace Nz
static void CopyBodyData(cpBody* from, cpBody* to);
static void CopyShapeData(cpShape* from, cpShape* to);
Vector2f m_positionOffset;
VelocityFunc m_velocityFunc;
std::vector<cpShape*> m_shapes;
Collider2DRef m_geom;

View File

@ -17,6 +17,11 @@ namespace Nz
return GetMassCenter(coordSys);
}
inline const Vector2f& RigidBody2D::GetPositionOffset() const
{
return m_positionOffset;
}
inline std::size_t RigidBody2D::GetShapeCount() const
{
return m_shapes.size();

View File

@ -19,6 +19,7 @@ namespace Nz
}
RigidBody2D::RigidBody2D(PhysWorld2D* world, float mass, Collider2DRef geom) :
m_positionOffset(Vector2f::Zero()),
m_geom(),
m_userData(nullptr),
m_world(world),
@ -35,6 +36,7 @@ namespace Nz
}
RigidBody2D::RigidBody2D(const RigidBody2D& object) :
m_positionOffset(object.m_positionOffset),
m_geom(object.m_geom),
m_userData(object.m_userData),
m_world(object.m_world),
@ -59,9 +61,10 @@ namespace Nz
}
}
RigidBody2D::RigidBody2D(RigidBody2D&& object) :
RigidBody2D::RigidBody2D(RigidBody2D&& object) noexcept :
OnRigidBody2DMove(std::move(object.OnRigidBody2DMove)),
OnRigidBody2DRelease(std::move(object.OnRigidBody2DRelease)),
m_positionOffset(std::move(object.m_positionOffset)),
m_shapes(std::move(object.m_shapes)),
m_geom(std::move(object.m_geom)),
m_handle(object.m_handle),
@ -260,7 +263,7 @@ namespace Nz
Vector2f RigidBody2D::GetPosition() const
{
cpVect pos = cpBodyGetPosition(m_handle);
cpVect pos = cpBodyLocalToWorld(m_handle, cpv(-m_positionOffset.x, -m_positionOffset.y));
return Vector2f(static_cast<float>(pos.x), static_cast<float>(pos.y));
}
@ -467,7 +470,9 @@ namespace Nz
void RigidBody2D::SetPosition(const Vector2f& position)
{
cpBodySetPosition(m_handle, cpv(position.x, position.y));
cpVect oldPosition = cpBodyGetPosition(m_handle);
cpBodySetPosition(m_handle, cpBodyLocalToWorld(m_handle, cpv(position.x - oldPosition.x + m_positionOffset.x, position.y - oldPosition.y + m_positionOffset.y)));
if (m_isStatic)
{
m_world->RegisterPostStep(this, [](Nz::RigidBody2D* body)
@ -477,6 +482,13 @@ namespace Nz
}
}
void RigidBody2D::SetPositionOffset(const Vector2f& offset)
{
Nz::Vector2f position = GetPosition();
m_positionOffset = offset;
SetPosition(position);
}
void RigidBody2D::SetRotation(const RadianAnglef& rotation)
{
cpBodySetAngle(m_handle, rotation.value);
@ -573,6 +585,7 @@ namespace Nz
m_geom = std::move(object.m_geom);
m_gravityFactor = object.m_gravityFactor;
m_mass = object.m_mass;
m_positionOffset = object.m_positionOffset;
m_shapes = std::move(object.m_shapes);
m_userData = object.m_userData;
m_velocityFunc = std::move(object.m_velocityFunc);
@ -653,9 +666,10 @@ namespace Nz
void RigidBody2D::CopyBodyData(cpBody* from, cpBody* to)
{
cpBodySetCenterOfGravity(to, cpBodyGetCenterOfGravity(from));
cpBodySetAngle(to, cpBodyGetAngle(from));
cpBodySetAngularVelocity(to, cpBodyGetAngularVelocity(from));
cpBodySetCenterOfGravity(to, cpBodyGetCenterOfGravity(from));
cpBodySetForce(to, cpBodyGetForce(from));
cpBodySetPosition(to, cpBodyGetPosition(from));
cpBodySetTorque(to, cpBodyGetTorque(from));