Files
FlaxEngine/Source/Engine/Physics/Joints/Joint.cpp
NoriteSC 6ab2e540a3 Bug Fixes
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 ?
2023-08-25 14:51:49 +02:00

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);
}