Skip to content

Commit

Permalink
#283: 6DOF and basic joint improvements
Browse files Browse the repository at this point in the history
* fixes for the 6DOF joint
* every joint can now define the anchor point for the child actor (optionally), which allows to create joints that are not in the starting state
* this would also allow to create joints at runtime to 'snap' the actor towards the joint
  • Loading branch information
jankrassnigg committed Jul 20, 2020
1 parent 45e6bb8 commit a863756
Show file tree
Hide file tree
Showing 5 changed files with 303 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,21 @@ void ezPx6DOFJointComponent::ApplySettings()
}
else
{
pJoint->setMotion(PxD6Axis::eX, m_FreeLinearAxis.IsSet(ezPxAxis::X) ? PxD6Motion::eLIMITED : PxD6Motion::eLOCKED);
pJoint->setMotion(PxD6Axis::eY, m_FreeLinearAxis.IsSet(ezPxAxis::Y) ? PxD6Motion::eLIMITED : PxD6Motion::eLOCKED);
pJoint->setMotion(PxD6Axis::eZ, m_FreeLinearAxis.IsSet(ezPxAxis::Z) ? PxD6Motion::eLIMITED : PxD6Motion::eLOCKED);
auto freeAxis = m_FreeLinearAxis;

if (m_LinearLimitMode == ezPxJointLimitMode::HardLimit)
{
if (ezMath::IsEqual(m_vLinearRangeX.x, m_vLinearRangeX.y, 0.05f))
freeAxis.Remove(ezPxAxis::X);
if (ezMath::IsEqual(m_vLinearRangeY.x, m_vLinearRangeY.y, 0.05f))
freeAxis.Remove(ezPxAxis::Y);
if (ezMath::IsEqual(m_vLinearRangeZ.x, m_vLinearRangeZ.y, 0.05f))
freeAxis.Remove(ezPxAxis::Z);
}

pJoint->setMotion(PxD6Axis::eX, freeAxis.IsSet(ezPxAxis::X) ? PxD6Motion::eLIMITED : PxD6Motion::eLOCKED);
pJoint->setMotion(PxD6Axis::eY, freeAxis.IsSet(ezPxAxis::Y) ? PxD6Motion::eLIMITED : PxD6Motion::eLOCKED);
pJoint->setMotion(PxD6Axis::eZ, freeAxis.IsSet(ezPxAxis::Z) ? PxD6Motion::eLIMITED : PxD6Motion::eLOCKED);

PxJointLinearLimitPair l(0, 0, PxSpring(0, 0));

Expand All @@ -146,17 +158,38 @@ void ezPx6DOFJointComponent::ApplySettings()
l.bounceThreshold = m_fLinearDamping;
}

l.lower = m_vLinearRangeX.x;
l.upper = m_vLinearRangeX.y;
pJoint->setLinearLimit(PxD6Axis::eX, l);
if (freeAxis.IsSet(ezPxAxis::X))
{
l.lower = m_vLinearRangeX.x;
l.upper = m_vLinearRangeX.y;

if (l.lower > l.upper)
ezMath::Swap(l.lower, l.upper);

pJoint->setLinearLimit(PxD6Axis::eX, l);
}

if (freeAxis.IsSet(ezPxAxis::Y))
{
l.lower = m_vLinearRangeY.x;
l.upper = m_vLinearRangeY.y;

if (l.lower > l.upper)
ezMath::Swap(l.lower, l.upper);

l.lower = m_vLinearRangeY.x;
l.upper = m_vLinearRangeY.y;
pJoint->setLinearLimit(PxD6Axis::eY, l);
pJoint->setLinearLimit(PxD6Axis::eY, l);
}

if (freeAxis.IsSet(ezPxAxis::Z))
{
l.lower = m_vLinearRangeZ.x;
l.upper = m_vLinearRangeZ.y;

l.lower = m_vLinearRangeZ.x;
l.upper = m_vLinearRangeZ.y;
pJoint->setLinearLimit(PxD6Axis::eZ, l);
if (l.lower > l.upper)
ezMath::Swap(l.lower, l.upper);

pJoint->setLinearLimit(PxD6Axis::eZ, l);
}
}


Expand All @@ -167,23 +200,37 @@ void ezPx6DOFJointComponent::ApplySettings()
}
else
{
pJoint->setMotion(PxD6Axis::eSWING1, m_FreeAngularAxis.IsSet(ezPxAxis::Y) ? PxD6Motion::eLIMITED : PxD6Motion::eLOCKED);
pJoint->setMotion(PxD6Axis::eSWING2, m_FreeAngularAxis.IsSet(ezPxAxis::Z) ? PxD6Motion::eLIMITED : PxD6Motion::eLOCKED);
auto freeAxis = m_FreeAngularAxis;

PxJointLimitCone l(m_SwingLimit.GetRadian(), m_SwingLimit.GetRadian());

if (m_SwingLimitMode == ezPxJointLimitMode::SoftLimit)
if (m_SwingLimitMode == ezPxJointLimitMode::HardLimit)
{
l.stiffness = m_fSwingStiffness;
l.damping = m_fSwingDamping;
if (ezMath::IsZero(m_SwingLimit.GetDegree(), 1.0f))
{
freeAxis.Remove(ezPxAxis::Y);
freeAxis.Remove(ezPxAxis::Z);
}
}
else

pJoint->setMotion(PxD6Axis::eSWING1, freeAxis.IsSet(ezPxAxis::Y) ? PxD6Motion::eLIMITED : PxD6Motion::eLOCKED);
pJoint->setMotion(PxD6Axis::eSWING2, freeAxis.IsSet(ezPxAxis::Z) ? PxD6Motion::eLIMITED : PxD6Motion::eLOCKED);

if (freeAxis.IsAnySet(ezPxAxis::Y | ezPxAxis::Z))
{
l.restitution = m_fSwingStiffness;
l.bounceThreshold = m_fSwingDamping;
PxJointLimitCone l(m_SwingLimit.GetRadian(), m_SwingLimit.GetRadian());

if (m_SwingLimitMode == ezPxJointLimitMode::SoftLimit)
{
l.stiffness = m_fSwingStiffness;
l.damping = m_fSwingDamping;
}
else
{
l.restitution = m_fSwingStiffness;
l.bounceThreshold = m_fSwingDamping;
}

pJoint->setSwingLimit(l);
}

pJoint->setSwingLimit(l);
}

if (m_TwistLimitMode == ezPxJointLimitMode::NoLimit)
Expand All @@ -192,27 +239,40 @@ void ezPx6DOFJointComponent::ApplySettings()
}
else
{
pJoint->setMotion(PxD6Axis::eTWIST, m_FreeAngularAxis.IsSet(ezPxAxis::X) ? PxD6Motion::eLIMITED : PxD6Motion::eLOCKED);
auto freeAxis = m_FreeAngularAxis;

PxJointAngularLimitPair l(m_LowerTwistLimit.GetRadian(), m_UpperTwistLimit.GetRadian());

if (m_LowerTwistLimit > m_UpperTwistLimit)
if (m_SwingLimitMode == ezPxJointLimitMode::HardLimit)
{
ezMath::Swap(l.lower, l.upper);
if (ezMath::IsEqual(m_LowerTwistLimit.GetDegree(), m_UpperTwistLimit.GetDegree(), 1.0f))
{
freeAxis.Remove(ezPxAxis::X);
}
}

if (m_TwistLimitMode == ezPxJointLimitMode::SoftLimit)
{
l.stiffness = m_fTwistStiffness;
l.damping = m_fTwistDamping;
}
else
pJoint->setMotion(PxD6Axis::eTWIST, freeAxis.IsSet(ezPxAxis::X) ? PxD6Motion::eLIMITED : PxD6Motion::eLOCKED);

if (freeAxis.IsSet(ezPxAxis::X))
{
l.restitution = m_fTwistStiffness;
l.bounceThreshold = m_fTwistDamping;
PxJointAngularLimitPair l(m_LowerTwistLimit.GetRadian(), m_UpperTwistLimit.GetRadian());

if (l.lower > l.upper)
{
ezMath::Swap(l.lower, l.upper);
}

if (m_TwistLimitMode == ezPxJointLimitMode::SoftLimit)
{
l.stiffness = m_fTwistStiffness;
l.damping = m_fTwistDamping;
}
else
{
l.restitution = m_fTwistStiffness;
l.bounceThreshold = m_fTwistDamping;
}

pJoint->setTwistLimit(l);
}

pJoint->setTwistLimit(l);
}

// the linear and angular springs appear to have the exact same effect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ EZ_BEGIN_ABSTRACT_COMPONENT_TYPE(ezPxJointComponent, 2)
EZ_ACCESSOR_PROPERTY("BreakForce", GetBreakForce, SetBreakForce),
EZ_ACCESSOR_PROPERTY("BreakTorque", GetBreakTorque, SetBreakTorque),
EZ_ACCESSOR_PROPERTY("PairCollision", GetPairCollision, SetPairCollision),
EZ_ACCESSOR_PROPERTY("ParentActor", DummyGetter, SetParentActor)->AddAttributes(new ezGameObjectReferenceAttribute()),
EZ_ACCESSOR_PROPERTY("ChildActor", DummyGetter, SetChildActor)->AddAttributes(new ezGameObjectReferenceAttribute()),
EZ_ACCESSOR_PROPERTY("ParentActor", DummyGetter, SetParentActorReference)->AddAttributes(new ezGameObjectReferenceAttribute()),
EZ_ACCESSOR_PROPERTY("ChildActor", DummyGetter, SetChildActorReference)->AddAttributes(new ezGameObjectReferenceAttribute()),
EZ_ACCESSOR_PROPERTY("ChildActorAnchor", DummyGetter, SetChildActorAnchorReference)->AddAttributes(new ezGameObjectReferenceAttribute()),
}
EZ_END_PROPERTIES;
EZ_BEGIN_ATTRIBUTES
Expand Down Expand Up @@ -144,26 +145,47 @@ void ezPxJointComponent::DeserializeComponent(ezWorldReader& stream)
}
}

void ezPxJointComponent::SetParentActor(const char* szReference)
void ezPxJointComponent::SetParentActorReference(const char* szReference)
{
auto resolver = GetWorld()->GetGameObjectReferenceResolver();

if (!resolver.IsValid())
return;

SetUserFlag(0, false);
m_hActorA = resolver(szReference, GetHandle(), "ParentActor");
SetParentActor(resolver(szReference, GetHandle(), "ParentActor"));
}

void ezPxJointComponent::SetChildActor(const char* szReference)
void ezPxJointComponent::SetChildActorReference(const char* szReference)
{
auto resolver = GetWorld()->GetGameObjectReferenceResolver();

if (!resolver.IsValid())
return;

SetUserFlag(1, false);
m_hActorB = resolver(szReference, GetHandle(), "ChildActor");
SetChildActor(resolver(szReference, GetHandle(), "ChildActor"));
}

void ezPxJointComponent::SetChildActorAnchorReference(const char* szReference)
{
auto resolver = GetWorld()->GetGameObjectReferenceResolver();

if (!resolver.IsValid())
return;

SetUserFlag(1, false); // local frame B is not valid
m_hActorBAnchor = resolver(szReference, GetHandle(), "ChildActorAnchor");
}

void ezPxJointComponent::SetParentActor(ezGameObjectHandle hActor)
{
SetUserFlag(0, false); // local frame A is not valid
m_hActorA = hActor;
}

void ezPxJointComponent::SetChildActor(ezGameObjectHandle hActor)
{
SetUserFlag(1, false); // local frame B is not valid
m_hActorB = hActor;
}

void ezPxJointComponent::SetActors(ezGameObjectHandle hActorA, const ezTransform& localFrameA, ezGameObjectHandle hActorB, const ezTransform& localFrameB)
Expand All @@ -172,6 +194,7 @@ void ezPxJointComponent::SetActors(ezGameObjectHandle hActorA, const ezTransform
m_hActorB = hActorB;

// prevent FindParentBody() and FindChildBody() from overwriting the local frames
// local frame A and B are already valid
SetUserFlag(0, true);
SetUserFlag(1, true);

Expand All @@ -181,6 +204,8 @@ void ezPxJointComponent::SetActors(ezGameObjectHandle hActorA, const ezTransform

void ezPxJointComponent::ApplySettings()
{
SetUserFlag(2, false);

const float fBreakForce = m_fBreakForce <= 0.0f ? ezMath::MaxValue<float>() : m_fBreakForce;
const float fBreakTorque = m_fBreakTorque <= 0.0f ? ezMath::MaxValue<float>() : m_fBreakTorque;
m_pJoint->setBreakForce(fBreakForce, fBreakTorque);
Expand Down Expand Up @@ -215,9 +240,17 @@ ezResult ezPxJointComponent::FindParentBody(physx::PxRigidActor*& pActor)
}
else
{
pObject = GetOwner()->GetParent();
pObject = GetOwner();

if (pObject == nullptr || !pObject->TryGetComponentOfBaseType(pRbComp))
while (pObject != nullptr)
{
if (pObject->TryGetComponentOfBaseType(pRbComp))
break;

pObject = pObject->GetParent();
}

if (pRbComp == nullptr)
{
pActor = nullptr;

Expand Down Expand Up @@ -260,13 +293,13 @@ ezResult ezPxJointComponent::FindChildBody(physx::PxRigidActor*& pActor)

if (m_hActorB.IsInvalidated())
{
ezLog::Error("{0} '{1}' has no child actor reference. Joint is ignored.", GetDynamicRTTI()->GetTypeName(), GetOwner()->GetName());
ezLog::Error("{0} '{1}' has no child reference. Joint is ignored.", GetDynamicRTTI()->GetTypeName(), GetOwner()->GetName());
return EZ_FAILURE;
}

if (!GetWorld()->TryGetObject(m_hActorB, pObject))
{
ezLog::Error("{0} '{1}' references a non-existing object as its child actor. Joint is ignored.", GetDynamicRTTI()->GetTypeName(), GetOwner()->GetName());
ezLog::Error("{0} '{1}' child reference is a non-existing object. Joint is ignored.", GetDynamicRTTI()->GetTypeName(), GetOwner()->GetName());
return EZ_FAILURE;
}

Expand All @@ -277,7 +310,7 @@ ezResult ezPxJointComponent::FindChildBody(physx::PxRigidActor*& pActor)

if (pObject == nullptr)
{
ezLog::Error("{0} '{1}' child actor reference is an object without a ezPxDynamicActorComponent. Joint is ignored.", GetDynamicRTTI()->GetTypeName(), GetOwner()->GetName());
ezLog::Error("{0} '{1}' child reference is an object without a ezPxDynamicActorComponent. Joint is ignored.", GetDynamicRTTI()->GetTypeName(), GetOwner()->GetName());
return EZ_FAILURE;
}

Expand All @@ -295,17 +328,28 @@ ezResult ezPxJointComponent::FindChildBody(physx::PxRigidActor*& pActor)

if (pActor == nullptr)
{
ezLog::Error("{0} '{1}' child actor reference is an object with an invalid ezPxDynamicActorComponent. Joint is ignored.", GetDynamicRTTI()->GetTypeName(), GetOwner()->GetName());
ezLog::Error("{0} '{1}' child reference is an object with an invalid ezPxDynamicActorComponent. Joint is ignored.", GetDynamicRTTI()->GetTypeName(), GetOwner()->GetName());
return EZ_FAILURE;
}

m_hActorB = pObject->GetHandle();

if (GetUserFlag(1) == false)
{
ezGameObject* pAnchorObject = GetOwner();

if (!m_hActorBAnchor.IsInvalidated())
{
if (!GetWorld()->TryGetObject(m_hActorBAnchor, pAnchorObject))
{
ezLog::Error("{0} '{1}' anchor reference is a non-existing object. Joint is ignored.", GetDynamicRTTI()->GetTypeName(), GetOwner()->GetName());
return EZ_FAILURE;
}
}

// m_localFrameB is now valid
SetUserFlag(0, true);
m_localFrameB.SetLocalTransform(pObject->GetGlobalTransform(), GetOwner()->GetGlobalTransform());
SetUserFlag(1, true);
m_localFrameB.SetLocalTransform(pObject->GetGlobalTransform(), pAnchorObject->GetGlobalTransform());
m_localFrameB.m_vPosition = m_localFrameB.m_vPosition.CompMul(pObject->GetGlobalScaling());
}

Expand All @@ -317,6 +361,12 @@ void ezPxJointComponent::QueueApplySettings()
if (m_pJoint == nullptr)
return;

// already in queue ?
if (GetUserFlag(2))
return;

SetUserFlag(2, true);

if (ezPhysXWorldModule* pModule = GetWorld()->GetModule<ezPhysXWorldModule>())
{
pModule->m_RequireUpdate.PushBack(GetHandle());
Expand Down
9 changes: 7 additions & 2 deletions Code/EnginePlugins/PhysXPlugin/Joints/PxJointComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,12 @@ class EZ_PHYSXPLUGIN_DLL ezPxJointComponent : public ezPxComponent
void SetPairCollision(bool value); // [ property ]
bool GetPairCollision() const { return m_bPairCollision; } // [ property ]

void SetParentActor(const char* szReference); // [ property ]
void SetChildActor(const char* szReference); // [ property ]
void SetParentActorReference(const char* szReference); // [ property ]
void SetChildActorReference(const char* szReference); // [ property ]
void SetChildActorAnchorReference(const char* szReference); // [ property ]

void SetParentActor(ezGameObjectHandle hActor);
void SetChildActor(ezGameObjectHandle hActor);

void SetActors(ezGameObjectHandle hActorA, const ezTransform& localFrameA, ezGameObjectHandle hActorB, const ezTransform& localFrameB);

Expand All @@ -85,6 +89,7 @@ class EZ_PHYSXPLUGIN_DLL ezPxJointComponent : public ezPxComponent

ezGameObjectHandle m_hActorA;
ezGameObjectHandle m_hActorB;
ezGameObjectHandle m_hActorBAnchor;

// UserFlag0 specifies whether m_localFrameA is already set
ezTransform m_localFrameA;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ namespace
m_pTask = nullptr;
}

PxBaseTask* m_pTask;
PxBaseTask* m_pTask = nullptr;
};

class ezPxCpuDispatcher : public PxCpuDispatcher
Expand Down

0 comments on commit a863756

Please sign in to comment.