script and actors not having connect transform in on awake and in on start join auto anchor having wrong anchor because was including scale fixed selection do to changes to transform accessibility ?
380 lines
9.7 KiB
C++
380 lines
9.7 KiB
C++
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
|
|
|
#include "Joint.h"
|
|
#include "Engine/Serialization/Serialization.h"
|
|
#include "Engine/Core/Log.h"
|
|
#include "Engine/Physics/Physics.h"
|
|
#include "Engine/Physics/PhysicsBackend.h"
|
|
#include "Engine/Physics/PhysicsScene.h"
|
|
#include "Engine/Physics/Actors/IPhysicsActor.h"
|
|
#if USE_EDITOR
|
|
#include "Engine/Level/Scene/SceneRendering.h"
|
|
#endif
|
|
|
|
Joint::Joint(const SpawnParams& params)
|
|
: Actor(params)
|
|
, _joint(nullptr)
|
|
, _breakForce(MAX_float)
|
|
, _breakTorque(MAX_float)
|
|
, _targetAnchor(Vector3::Zero)
|
|
, _targetAnchorRotation(Quaternion::Identity)
|
|
{
|
|
Target.Changed.Bind<Joint, &Joint::OnTargetChanged>(this);
|
|
}
|
|
|
|
void Joint::SetBreakForce(float value)
|
|
{
|
|
if (Math::NearEqual(value, _breakForce))
|
|
return;
|
|
_breakForce = value;
|
|
if (_joint)
|
|
PhysicsBackend::SetJointBreakForce(_joint, _breakForce, _breakTorque);
|
|
}
|
|
|
|
void Joint::SetBreakTorque(float value)
|
|
{
|
|
if (Math::NearEqual(value, _breakTorque))
|
|
return;
|
|
_breakTorque = value;
|
|
if (_joint)
|
|
PhysicsBackend::SetJointBreakForce(_joint, _breakForce, _breakTorque);
|
|
}
|
|
|
|
void Joint::SetEnableCollision(bool value)
|
|
{
|
|
if (value == GetEnableCollision())
|
|
return;
|
|
_enableCollision = value;
|
|
if (_joint)
|
|
PhysicsBackend::SetJointFlags(_joint, _enableCollision ? PhysicsBackend::JointFlags::Collision : PhysicsBackend::JointFlags::None);
|
|
}
|
|
|
|
bool Joint::GetEnableAutoAnchor() const
|
|
{
|
|
return _enableAutoAnchor;
|
|
}
|
|
|
|
void Joint::SetEnableAutoAnchor(bool value)
|
|
{
|
|
_enableAutoAnchor = value;
|
|
}
|
|
|
|
void Joint::SetTargetAnchor(const Vector3& value)
|
|
{
|
|
if (Vector3::NearEqual(value, _targetAnchor))
|
|
return;
|
|
_targetAnchor = value;
|
|
if (_joint && !_enableAutoAnchor)
|
|
PhysicsBackend::SetJointActorPose(_joint, _targetAnchor, _targetAnchorRotation, 1);
|
|
}
|
|
|
|
void Joint::SetTargetAnchorRotation(const Quaternion& value)
|
|
{
|
|
if (Quaternion::NearEqual(value, _targetAnchorRotation))
|
|
return;
|
|
_targetAnchorRotation = value;
|
|
if (_joint && !_enableAutoAnchor)
|
|
PhysicsBackend::SetJointActorPose(_joint, _targetAnchor, _targetAnchorRotation, 1);
|
|
}
|
|
|
|
void* Joint::GetPhysicsImpl() const
|
|
{
|
|
return _joint;
|
|
}
|
|
|
|
void Joint::SetJointLocation(const Vector3& location)
|
|
{
|
|
if (GetParent())
|
|
{
|
|
SetLocalPosition(GetParent()->GetTransform().WorldToLocal(location));
|
|
}
|
|
if (Target)
|
|
{
|
|
SetTargetAnchor(Target->GetTransform().WorldToLocal(location));
|
|
}
|
|
}
|
|
|
|
FORCE_INLINE Quaternion WorldToLocal(const Quaternion& world, const Quaternion& orientation)
|
|
{
|
|
Quaternion rot;
|
|
const Quaternion invRotation = world.Conjugated();
|
|
Quaternion::Multiply(invRotation, orientation, rot);
|
|
rot.Normalize();
|
|
return rot;
|
|
}
|
|
|
|
void Joint::SetJointOrientation(const Quaternion& orientation)
|
|
{
|
|
if (GetParent())
|
|
{
|
|
SetLocalOrientation(WorldToLocal(GetParent()->GetOrientation(), orientation));
|
|
}
|
|
if (Target)
|
|
{
|
|
SetTargetAnchorRotation(WorldToLocal(Target->GetOrientation(), orientation));
|
|
}
|
|
}
|
|
|
|
void Joint::GetCurrentForce(Vector3& linear, Vector3& angular) const
|
|
{
|
|
linear = angular = Vector3::Zero;
|
|
if (_joint)
|
|
PhysicsBackend::GetJointForce(_joint, linear, angular);
|
|
}
|
|
|
|
void Joint::Create()
|
|
{
|
|
ASSERT(_joint == nullptr);
|
|
|
|
auto parent = dynamic_cast<IPhysicsActor*>(GetParent());
|
|
auto target = dynamic_cast<IPhysicsActor*>(Target.Get());
|
|
if (parent == nullptr)
|
|
{
|
|
// Skip creation if joint is link to the not supported actor
|
|
return;
|
|
}
|
|
|
|
// Create joint object
|
|
PhysicsJointDesc desc;
|
|
desc.Joint = this;
|
|
desc.Actor0 = parent->GetPhysicsActor();
|
|
desc.Actor1 = target ? target->GetPhysicsActor() : nullptr;
|
|
desc.Pos0 = _localTransform.Translation;
|
|
desc.Rot0 = _localTransform.Orientation;
|
|
desc.Pos1 = _targetAnchor;
|
|
desc.Rot1 = _targetAnchorRotation;
|
|
if (_enableAutoAnchor && target)
|
|
{
|
|
// Place target anchor at the joint location
|
|
desc.Pos1 = (Target->GetOrientation() * (GetPosition() - Target->GetPosition())) + Target->GetPosition();
|
|
desc.Rot1 = WorldToLocal(Target->GetOrientation(), GetOrientation());
|
|
}
|
|
_joint = CreateJoint(desc);
|
|
|
|
// Setup joint properties
|
|
PhysicsBackend::SetJointBreakForce(_joint, _breakForce, _breakTorque);
|
|
PhysicsBackend::SetJointFlags(_joint, _enableCollision ? PhysicsBackend::JointFlags::Collision : PhysicsBackend::JointFlags::None);
|
|
}
|
|
|
|
void Joint::OnJointBreak()
|
|
{
|
|
JointBreak(this);
|
|
}
|
|
|
|
void Joint::Delete()
|
|
{
|
|
PhysicsBackend::RemoveJoint(this);
|
|
PhysicsBackend::DestroyJoint(_joint);
|
|
_joint = nullptr;
|
|
}
|
|
|
|
void Joint::SetActors()
|
|
{
|
|
auto parent = dynamic_cast<IPhysicsActor*>(GetParent());
|
|
auto target = dynamic_cast<IPhysicsActor*>(Target.Get());
|
|
ASSERT(parent != nullptr);
|
|
PhysicsBackend::SetJointActors(_joint, parent->GetPhysicsActor(), target ? target->GetPhysicsActor() : nullptr);
|
|
}
|
|
|
|
void Joint::OnTargetChanged()
|
|
{
|
|
// Validate type
|
|
const auto target = dynamic_cast<IPhysicsActor*>(Target.Get());
|
|
if (Target && target == nullptr)
|
|
{
|
|
LOG(Error, "Invalid actor. Cannot use it as joint target. Rigidbodies and character controllers are supported. Object: {0}", Target.Get()->ToString());
|
|
Target = nullptr;
|
|
}
|
|
else if (_joint)
|
|
{
|
|
SetActors();
|
|
}
|
|
}
|
|
|
|
Vector3 Joint::GetTargetPosition() const
|
|
{
|
|
Vector3 position = _targetAnchor;
|
|
if (Target)
|
|
{
|
|
if (_enableAutoAnchor)
|
|
position = (Target->GetOrientation() * (GetPosition() - Target->GetPosition())) + Target->GetPosition();
|
|
else
|
|
position = Target->GetOrientation() * position + Target->GetPosition();
|
|
}
|
|
return position;
|
|
}
|
|
|
|
Quaternion Joint::GetTargetOrientation() const
|
|
{
|
|
Quaternion rotation = _targetAnchorRotation;
|
|
if (Target)
|
|
{
|
|
if (_enableAutoAnchor)
|
|
rotation = WorldToLocal(Target->GetOrientation(), GetOrientation());
|
|
rotation = Target->GetOrientation() * rotation;
|
|
}
|
|
return rotation;
|
|
}
|
|
|
|
#if USE_EDITOR
|
|
|
|
#include "Engine/Debug/DebugDraw.h"
|
|
#include "Engine/Graphics/RenderView.h"
|
|
|
|
void Joint::DrawPhysicsDebug(RenderView& view)
|
|
{
|
|
if (view.Mode == ViewMode::PhysicsColliders)
|
|
{
|
|
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(GetPosition(), 3.0f), Color::BlueViolet * 0.8f, 0, true);
|
|
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(GetTargetPosition(), 4.0f), Color::AliceBlue * 0.8f, 0, true);
|
|
}
|
|
}
|
|
|
|
void Joint::OnDebugDrawSelected()
|
|
{
|
|
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(GetPosition(), 3.0f), Color::BlueViolet * 0.8f, 0, false);
|
|
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(GetTargetPosition(), 4.0f), Color::AliceBlue * 0.8f, 0, false);
|
|
|
|
// Base
|
|
Actor::OnDebugDrawSelected();
|
|
}
|
|
|
|
#endif
|
|
|
|
void Joint::Serialize(SerializeStream& stream, const void* otherObj)
|
|
{
|
|
// Base
|
|
Actor::Serialize(stream, otherObj);
|
|
|
|
SERIALIZE_GET_OTHER_OBJ(Joint);
|
|
|
|
SERIALIZE(Target);
|
|
SERIALIZE_MEMBER(BreakForce, _breakForce);
|
|
SERIALIZE_MEMBER(BreakTorque, _breakTorque);
|
|
SERIALIZE_MEMBER(TargetAnchor, _targetAnchor);
|
|
SERIALIZE_MEMBER(TargetAnchorRotation, _targetAnchorRotation);
|
|
SERIALIZE_MEMBER(EnableCollision, _enableCollision);
|
|
SERIALIZE_MEMBER(EnableAutoAnchor, _enableAutoAnchor);
|
|
}
|
|
|
|
void Joint::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
|
{
|
|
// Base
|
|
Actor::Deserialize(stream, modifier);
|
|
|
|
DESERIALIZE(Target);
|
|
DESERIALIZE_MEMBER(BreakForce, _breakForce);
|
|
DESERIALIZE_MEMBER(BreakTorque, _breakTorque);
|
|
DESERIALIZE_MEMBER(TargetAnchor, _targetAnchor);
|
|
DESERIALIZE_MEMBER(TargetAnchorRotation, _targetAnchorRotation);
|
|
DESERIALIZE_MEMBER(EnableCollision, _enableCollision);
|
|
DESERIALIZE_MEMBER(EnableAutoAnchor, _enableAutoAnchor);
|
|
}
|
|
|
|
void Joint::BeginPlay(SceneBeginData* data)
|
|
{
|
|
// Base
|
|
Actor::BeginPlay(data);
|
|
|
|
// Create joint object only if it's enabled (otherwise it will be created in OnActiveInTreeChanged)
|
|
if (IsActiveInHierarchy() && !_joint)
|
|
{
|
|
// Register for later init (joints are created after full beginPlay so all rigidbodies are created and can be linked)
|
|
data->JointsToCreate.Add(this);
|
|
}
|
|
}
|
|
|
|
void Joint::EndPlay()
|
|
{
|
|
if (_joint)
|
|
{
|
|
Delete();
|
|
}
|
|
|
|
// Base
|
|
Actor::EndPlay();
|
|
}
|
|
|
|
#if USE_EDITOR
|
|
|
|
void Joint::OnEnable()
|
|
{
|
|
GetSceneRendering()->AddPhysicsDebug<Joint, &Joint::DrawPhysicsDebug>(this);
|
|
|
|
// Base
|
|
Actor::OnEnable();
|
|
}
|
|
|
|
void Joint::OnDisable()
|
|
{
|
|
GetSceneRendering()->RemovePhysicsDebug<Joint, &Joint::DrawPhysicsDebug>(this);
|
|
|
|
// Base
|
|
Actor::OnDisable();
|
|
}
|
|
|
|
#endif
|
|
|
|
void Joint::OnActiveInTreeChanged()
|
|
{
|
|
// Base
|
|
Actor::OnActiveInTreeChanged();
|
|
|
|
if (_joint)
|
|
{
|
|
// Enable/disable joint
|
|
if (IsActiveInHierarchy())
|
|
SetActors();
|
|
else
|
|
Delete();
|
|
}
|
|
// Joint object may not be created if actor is disabled on play mode begin (late init feature)
|
|
else if (IsDuringPlay())
|
|
{
|
|
Create();
|
|
}
|
|
}
|
|
|
|
void Joint::OnParentChanged()
|
|
{
|
|
// Base
|
|
Actor::OnParentChanged();
|
|
|
|
if (!IsDuringPlay())
|
|
return;
|
|
|
|
// Check reparenting Joint case
|
|
const auto parent = dynamic_cast<IPhysicsActor*>(GetParent());
|
|
if (parent == nullptr)
|
|
{
|
|
if (_joint)
|
|
{
|
|
// Remove joint
|
|
Delete();
|
|
}
|
|
}
|
|
else if (_joint)
|
|
{
|
|
// Change target actor
|
|
SetActors();
|
|
}
|
|
else
|
|
{
|
|
// Create joint
|
|
Create();
|
|
}
|
|
}
|
|
|
|
void Joint::OnTransformChanged()
|
|
{
|
|
// Base
|
|
Actor::OnTransformChanged();
|
|
|
|
// TODO: this could track only local transform changed
|
|
|
|
_box = BoundingBox(_transform.Translation);
|
|
_sphere = BoundingSphere(_transform.Translation, 0.0f);
|
|
if (_joint)
|
|
PhysicsBackend::SetJointActorPose(_joint, _localTransform.Translation, _localTransform.Orientation, 0);
|
|
}
|