Physics2D: Fix the way newly physics entities are setup

This commit is contained in:
SirLynix 2023-03-25 14:47:35 +01:00
parent 3e99ce6be5
commit 883b54e6d3
4 changed files with 51 additions and 38 deletions

View File

@ -98,6 +98,8 @@ namespace Nz
void SetVelocity(const Vector2f& velocity); void SetVelocity(const Vector2f& velocity);
void SetVelocityFunction(VelocityFunc velocityFunc); void SetVelocityFunction(VelocityFunc velocityFunc);
void TeleportTo(const Vector2f& position, const RadianAnglef& rotation);
void UpdateVelocity(const Vector2f& gravity, float damping, float deltaTime); void UpdateVelocity(const Vector2f& gravity, float damping, float deltaTime);
void Wakeup(); void Wakeup();

View File

@ -38,9 +38,8 @@ namespace Nz
Physics2DSystem& operator=(Physics2DSystem&&) = delete; Physics2DSystem& operator=(Physics2DSystem&&) = delete;
private: private:
static void OnConstruct(entt::registry& registry, entt::entity entity);
entt::registry& m_registry; entt::registry& m_registry;
entt::observer m_physicsConstructObserver;
entt::scoped_connection m_constructConnection; entt::scoped_connection m_constructConnection;
PhysWorld2D m_physWorld; PhysWorld2D m_physWorld;
}; };

View File

@ -135,12 +135,12 @@ namespace Nz
cpBodySetTorque(m_handle, cpBodyGetTorque(m_handle) + torque.value); cpBodySetTorque(m_handle, cpBodyGetTorque(m_handle) + torque.value);
} }
bool RigidBody2D::ClosestPointQuery(const Nz::Vector2f& position, Nz::Vector2f* closestPoint, float* closestDistance) const bool RigidBody2D::ClosestPointQuery(const Vector2f& position, Vector2f* closestPoint, float* closestDistance) const
{ {
cpVect pos = cpv(cpFloat(position.x), cpFloat(position.y)); cpVect pos = cpv(cpFloat(position.x), cpFloat(position.y));
float minDistance = std::numeric_limits<float>::infinity(); float minDistance = std::numeric_limits<float>::infinity();
Nz::Vector2f closest; Vector2f closest;
for (cpShape* shape : m_shapes) for (cpShape* shape : m_shapes)
{ {
cpPointQueryInfo result; cpPointQueryInfo result;
@ -179,7 +179,7 @@ namespace Nz
} }
} }
void RigidBody2D::ForEachArbiter(std::function<void(Nz::Arbiter2D&)> callback) void RigidBody2D::ForEachArbiter(std::function<void(Arbiter2D&)> callback)
{ {
using CallbackType = decltype(callback); using CallbackType = decltype(callback);
@ -196,7 +196,7 @@ namespace Nz
void RigidBody2D::ForceSleep() void RigidBody2D::ForceSleep()
{ {
m_world->RegisterPostStep(this, [](Nz::RigidBody2D* body) m_world->RegisterPostStep(this, [](RigidBody2D* body)
{ {
if (cpBodyGetType(body->GetHandle()) == CP_BODY_TYPE_DYNAMIC) if (cpBodyGetType(body->GetHandle()) == CP_BODY_TYPE_DYNAMIC)
cpBodySleep(body->GetHandle()); cpBodySleep(body->GetHandle());
@ -221,13 +221,13 @@ namespace Nz
return float(cpBodyGetAngularVelocity(m_handle)); return float(cpBodyGetAngularVelocity(m_handle));
} }
float Nz::RigidBody2D::GetElasticity(std::size_t shapeIndex) const float RigidBody2D::GetElasticity(std::size_t shapeIndex) const
{ {
assert(shapeIndex < m_shapes.size()); assert(shapeIndex < m_shapes.size());
return float(cpShapeGetElasticity(m_shapes[shapeIndex])); return float(cpShapeGetElasticity(m_shapes[shapeIndex]));
} }
float Nz::RigidBody2D::GetFriction(std::size_t shapeIndex) const float RigidBody2D::GetFriction(std::size_t shapeIndex) const
{ {
assert(shapeIndex < m_shapes.size()); assert(shapeIndex < m_shapes.size());
return float(cpShapeGetFriction(m_shapes[shapeIndex])); return float(cpShapeGetFriction(m_shapes[shapeIndex]));
@ -290,7 +290,7 @@ namespace Nz
return std::distance(m_shapes.begin(), it); return std::distance(m_shapes.begin(), it);
} }
Vector2f Nz::RigidBody2D::GetSurfaceVelocity(std::size_t shapeIndex) const Vector2f RigidBody2D::GetSurfaceVelocity(std::size_t shapeIndex) const
{ {
assert(shapeIndex < m_shapes.size()); assert(shapeIndex < m_shapes.size());
cpVect vel = cpShapeGetSurfaceVelocity(m_shapes[shapeIndex]); cpVect vel = cpShapeGetSurfaceVelocity(m_shapes[shapeIndex]);
@ -421,7 +421,7 @@ namespace Nz
{ {
if (mass > 0.f) if (mass > 0.f)
{ {
m_world->RegisterPostStep(this, [mass, recomputeMoment](Nz::RigidBody2D* body) m_world->RegisterPostStep(this, [mass, recomputeMoment](RigidBody2D* body)
{ {
cpBodySetMass(body->GetHandle(), mass); cpBodySetMass(body->GetHandle(), mass);
@ -430,11 +430,11 @@ namespace Nz
}); });
} }
else else
m_world->RegisterPostStep(this, [](Nz::RigidBody2D* body) { cpBodySetType(body->GetHandle(), (body->IsStatic()) ? CP_BODY_TYPE_STATIC : CP_BODY_TYPE_KINEMATIC); } ); m_world->RegisterPostStep(this, [](RigidBody2D* body) { cpBodySetType(body->GetHandle(), (body->IsStatic()) ? CP_BODY_TYPE_STATIC : CP_BODY_TYPE_KINEMATIC); } );
} }
else if (mass > 0.f) else if (mass > 0.f)
{ {
m_world->RegisterPostStep(this, [mass, recomputeMoment](Nz::RigidBody2D* body) m_world->RegisterPostStep(this, [mass, recomputeMoment](RigidBody2D* body)
{ {
if (cpBodyGetType(body->GetHandle()) != CP_BODY_TYPE_DYNAMIC) if (cpBodyGetType(body->GetHandle()) != CP_BODY_TYPE_DYNAMIC)
{ {
@ -470,7 +470,7 @@ namespace Nz
void RigidBody2D::SetMomentOfInertia(float moment) void RigidBody2D::SetMomentOfInertia(float moment)
{ {
// Even though Chipmunk allows us to change this anytime, we need to do it in a post-step to prevent other post-steps to override this // Even though Chipmunk allows us to change this anytime, we need to do it in a post-step to prevent other post-steps to override this
m_world->RegisterPostStep(this, [moment] (Nz::RigidBody2D* body) m_world->RegisterPostStep(this, [moment] (RigidBody2D* body)
{ {
cpBodySetMoment(body->GetHandle(), moment); cpBodySetMoment(body->GetHandle(), moment);
}); });
@ -482,7 +482,7 @@ namespace Nz
cpBodySetPosition(m_handle, cpvadd(cpv(position.x, position.y), cpTransformVect(m_handle->transform, cpv(m_positionOffset.x, m_positionOffset.y)))); cpBodySetPosition(m_handle, cpvadd(cpv(position.x, position.y), cpTransformVect(m_handle->transform, cpv(m_positionOffset.x, m_positionOffset.y))));
if (m_isStatic) if (m_isStatic)
{ {
m_world->RegisterPostStep(this, [](Nz::RigidBody2D* body) m_world->RegisterPostStep(this, [](RigidBody2D* body)
{ {
cpSpaceReindexShapesForBody(body->GetWorld()->GetHandle(), body->GetHandle()); cpSpaceReindexShapesForBody(body->GetWorld()->GetHandle(), body->GetHandle());
}); });
@ -491,7 +491,7 @@ namespace Nz
void RigidBody2D::SetPositionOffset(const Vector2f& offset) void RigidBody2D::SetPositionOffset(const Vector2f& offset)
{ {
Nz::Vector2f position = GetPosition(); Vector2f position = GetPosition();
m_positionOffset = offset; m_positionOffset = offset;
SetPosition(position); SetPosition(position);
} }
@ -501,7 +501,7 @@ namespace Nz
cpBodySetAngle(m_handle, rotation.value); cpBodySetAngle(m_handle, rotation.value);
if (m_isStatic) if (m_isStatic)
{ {
m_world->RegisterPostStep(this, [](Nz::RigidBody2D* body) m_world->RegisterPostStep(this, [](RigidBody2D* body)
{ {
cpSpaceReindexShapesForBody(body->GetWorld()->GetHandle(), body->GetHandle()); cpSpaceReindexShapesForBody(body->GetWorld()->GetHandle(), body->GetHandle());
}); });
@ -524,7 +524,7 @@ namespace Nz
void RigidBody2D::SetStatic(bool setStaticBody) void RigidBody2D::SetStatic(bool setStaticBody)
{ {
m_isStatic = setStaticBody; m_isStatic = setStaticBody;
m_world->RegisterPostStep(this, [](Nz::RigidBody2D* body) m_world->RegisterPostStep(this, [](RigidBody2D* body)
{ {
if (body->IsStatic()) if (body->IsStatic())
{ {
@ -560,21 +560,35 @@ namespace Nz
const auto& callback = rigidBody->GetVelocityFunction(); const auto& callback = rigidBody->GetVelocityFunction();
assert(callback); assert(callback);
callback(*rigidBody, Nz::Vector2f(float(gravity.x), float(gravity.y)), float(damping), float(dt)); callback(*rigidBody, Vector2f(float(gravity.x), float(gravity.y)), float(damping), float(dt));
}; };
} }
else else
m_handle->velocity_func = cpBodyUpdateVelocity; m_handle->velocity_func = cpBodyUpdateVelocity;
} }
void RigidBody2D::UpdateVelocity(const Nz::Vector2f & gravity, float damping, float deltaTime) void RigidBody2D::TeleportTo(const Vector2f& position, const RadianAnglef& rotation)
{
// Use cpTransformVect to rotate/scale the position offset
cpBodySetPosition(m_handle, cpvadd(cpv(position.x, position.y), cpTransformVect(m_handle->transform, cpv(m_positionOffset.x, m_positionOffset.y))));
cpBodySetAngle(m_handle, rotation.value);
if (m_isStatic)
{
m_world->RegisterPostStep(this, [](RigidBody2D* body)
{
cpSpaceReindexShapesForBody(body->GetWorld()->GetHandle(), body->GetHandle());
});
}
}
void RigidBody2D::UpdateVelocity(const Vector2f & gravity, float damping, float deltaTime)
{ {
cpBodyUpdateVelocity(m_handle, cpv(gravity.x, gravity.y), damping, deltaTime); cpBodyUpdateVelocity(m_handle, cpv(gravity.x, gravity.y), damping, deltaTime);
} }
void RigidBody2D::Wakeup() void RigidBody2D::Wakeup()
{ {
m_world->RegisterPostStep(this, [](Nz::RigidBody2D* body) m_world->RegisterPostStep(this, [](RigidBody2D* body)
{ {
if (cpBodyGetType(body->GetHandle()) != CP_BODY_TYPE_STATIC) if (cpBodyGetType(body->GetHandle()) != CP_BODY_TYPE_STATIC)
cpBodyActivate(body->GetHandle()); cpBodyActivate(body->GetHandle());

View File

@ -10,7 +10,7 @@ namespace Nz
{ {
namespace namespace
{ {
inline Nz::RadianAnglef AngleFromQuaternion(const Nz::Quaternionf& quat) inline RadianAnglef AngleFromQuaternion(const Quaternionf& quat)
{ {
float siny_cosp = 2.f * (quat.w * quat.z + quat.x * quat.y); float siny_cosp = 2.f * (quat.w * quat.z + quat.x * quat.y);
float cosy_cosp = 1.f - 2.f * (quat.y * quat.y + quat.z * quat.z); float cosy_cosp = 1.f - 2.f * (quat.y * quat.y + quat.z * quat.z);
@ -20,14 +20,16 @@ namespace Nz
} }
Physics2DSystem::Physics2DSystem(entt::registry& registry) : Physics2DSystem::Physics2DSystem(entt::registry& registry) :
m_registry(registry) m_registry(registry),
m_physicsConstructObserver(m_registry, entt::collector.group<RigidBody2DComponent, NodeComponent>())
{ {
m_constructConnection = registry.on_construct<RigidBody2DComponent>().connect<OnConstruct>();
} }
Physics2DSystem::~Physics2DSystem() Physics2DSystem::~Physics2DSystem()
{ {
// Ensure every NewtonBody is destroyed before world is m_physicsConstructObserver.disconnect();
// Ensure every body is destroyed before world is
auto rigidBodyView = m_registry.view<RigidBody2DComponent>(); auto rigidBodyView = m_registry.view<RigidBody2DComponent>();
for (auto [entity, rigidBodyComponent] : rigidBodyView.each()) for (auto [entity, rigidBodyComponent] : rigidBodyView.each())
rigidBodyComponent.Destroy(); rigidBodyComponent.Destroy();
@ -35,6 +37,15 @@ namespace Nz
void Physics2DSystem::Update(Time elapsedTime) void Physics2DSystem::Update(Time elapsedTime)
{ {
// Move newly-created physics entities to their node position/rotation
m_physicsConstructObserver.each([&](entt::entity entity)
{
RigidBody2DComponent& entityPhysics = m_registry.get<RigidBody2DComponent>(entity);
NodeComponent& entityNode = m_registry.get<NodeComponent>(entity);
entityPhysics.TeleportTo(Vector2f(entityNode.GetPosition()), AngleFromQuaternion(entityNode.GetRotation()));
});
m_physWorld.Step(elapsedTime); m_physWorld.Step(elapsedTime);
// Replicate rigid body position to their node components // Replicate rigid body position to their node components
@ -44,20 +55,7 @@ namespace Nz
if (rigidBodyComponent.IsSleeping()) if (rigidBodyComponent.IsSleeping())
continue; continue;
nodeComponent.SetPosition(rigidBodyComponent.GetPosition(), CoordSys::Global); nodeComponent.SetTransform(rigidBodyComponent.GetPosition(), rigidBodyComponent.GetRotation());
nodeComponent.SetRotation(rigidBodyComponent.GetRotation(), CoordSys::Global);
}
}
void Physics2DSystem::OnConstruct(entt::registry& registry, entt::entity entity)
{
// If our entity already has a node component when adding a rigid body, initialize it with its position/rotation
NodeComponent* node = registry.try_get<NodeComponent>(entity);
if (node)
{
RigidBody2DComponent& rigidBody = registry.get<RigidBody2DComponent>(entity);
rigidBody.SetPosition(Vector2f(node->GetPosition()));
rigidBody.SetRotation(AngleFromQuaternion(node->GetRotation()));
} }
} }
} }