Path: blob/master/modules/jolt_physics/joints/jolt_hinge_joint_3d.cpp
10278 views
/**************************************************************************/1/* jolt_hinge_joint_3d.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "jolt_hinge_joint_3d.h"3132#include "../misc/jolt_type_conversions.h"33#include "../objects/jolt_body_3d.h"34#include "../spaces/jolt_space_3d.h"3536#include "core/config/engine.h"3738#include "Jolt/Physics/Constraints/FixedConstraint.h"39#include "Jolt/Physics/Constraints/HingeConstraint.h"4041namespace {4243constexpr double HINGE_DEFAULT_BIAS = 0.3;44constexpr double HINGE_DEFAULT_LIMIT_BIAS = 0.3;45constexpr double HINGE_DEFAULT_SOFTNESS = 0.9;46constexpr double HINGE_DEFAULT_RELAXATION = 1.0;4748double estimate_physics_step() {49Engine *engine = Engine::get_singleton();5051const double step = 1.0 / engine->get_physics_ticks_per_second();52const double step_scaled = step * engine->get_time_scale();5354return step_scaled;55}5657} // namespace5859JPH::Constraint *JoltHingeJoint3D::_build_hinge(JPH::Body *p_jolt_body_a, JPH::Body *p_jolt_body_b, const Transform3D &p_shifted_ref_a, const Transform3D &p_shifted_ref_b, float p_limit) const {60JPH::HingeConstraintSettings constraint_settings;6162constraint_settings.mSpace = JPH::EConstraintSpace::LocalToBodyCOM;63constraint_settings.mPoint1 = to_jolt_r(p_shifted_ref_a.origin);64constraint_settings.mHingeAxis1 = to_jolt(p_shifted_ref_a.basis.get_column(Vector3::AXIS_Z));65constraint_settings.mNormalAxis1 = to_jolt(p_shifted_ref_a.basis.get_column(Vector3::AXIS_X));66constraint_settings.mPoint2 = to_jolt_r(p_shifted_ref_b.origin);67constraint_settings.mHingeAxis2 = to_jolt(p_shifted_ref_b.basis.get_column(Vector3::AXIS_Z));68constraint_settings.mNormalAxis2 = to_jolt(p_shifted_ref_b.basis.get_column(Vector3::AXIS_X));69constraint_settings.mLimitsMin = -p_limit;70constraint_settings.mLimitsMax = p_limit;7172if (limit_spring_enabled) {73constraint_settings.mLimitsSpringSettings.mFrequency = (float)limit_spring_frequency;74constraint_settings.mLimitsSpringSettings.mDamping = (float)limit_spring_damping;75}7677if (p_jolt_body_a == nullptr) {78return constraint_settings.Create(JPH::Body::sFixedToWorld, *p_jolt_body_b);79} else if (p_jolt_body_b == nullptr) {80return constraint_settings.Create(*p_jolt_body_a, JPH::Body::sFixedToWorld);81} else {82return constraint_settings.Create(*p_jolt_body_a, *p_jolt_body_b);83}84}8586JPH::Constraint *JoltHingeJoint3D::_build_fixed(JPH::Body *p_jolt_body_a, JPH::Body *p_jolt_body_b, const Transform3D &p_shifted_ref_a, const Transform3D &p_shifted_ref_b) const {87JPH::FixedConstraintSettings constraint_settings;8889constraint_settings.mSpace = JPH::EConstraintSpace::LocalToBodyCOM;90constraint_settings.mAutoDetectPoint = false;91constraint_settings.mPoint1 = to_jolt_r(p_shifted_ref_a.origin);92constraint_settings.mAxisX1 = to_jolt(p_shifted_ref_a.basis.get_column(Vector3::AXIS_X));93constraint_settings.mAxisY1 = to_jolt(p_shifted_ref_a.basis.get_column(Vector3::AXIS_Y));94constraint_settings.mPoint2 = to_jolt_r(p_shifted_ref_b.origin);95constraint_settings.mAxisX2 = to_jolt(p_shifted_ref_b.basis.get_column(Vector3::AXIS_X));96constraint_settings.mAxisY2 = to_jolt(p_shifted_ref_b.basis.get_column(Vector3::AXIS_Y));9798if (p_jolt_body_a == nullptr) {99return constraint_settings.Create(JPH::Body::sFixedToWorld, *p_jolt_body_b);100} else if (p_jolt_body_b == nullptr) {101return constraint_settings.Create(*p_jolt_body_a, JPH::Body::sFixedToWorld);102} else {103return constraint_settings.Create(*p_jolt_body_a, *p_jolt_body_b);104}105}106107void JoltHingeJoint3D::_update_motor_state() {108if (unlikely(_is_fixed())) {109return;110}111112if (JPH::HingeConstraint *constraint = static_cast<JPH::HingeConstraint *>(jolt_ref.GetPtr())) {113constraint->SetMotorState(motor_enabled ? JPH::EMotorState::Velocity : JPH::EMotorState::Off);114}115}116117void JoltHingeJoint3D::_update_motor_velocity() {118if (unlikely(_is_fixed())) {119return;120}121122if (JPH::HingeConstraint *constraint = static_cast<JPH::HingeConstraint *>(jolt_ref.GetPtr())) {123// We flip the direction since Jolt is CCW but Godot is CW.124constraint->SetTargetAngularVelocity((float)-motor_target_speed);125}126}127128void JoltHingeJoint3D::_update_motor_limit() {129if (unlikely(_is_fixed())) {130return;131}132133if (JPH::HingeConstraint *constraint = static_cast<JPH::HingeConstraint *>(jolt_ref.GetPtr())) {134JPH::MotorSettings &motor_settings = constraint->GetMotorSettings();135motor_settings.mMinTorqueLimit = (float)-motor_max_torque;136motor_settings.mMaxTorqueLimit = (float)motor_max_torque;137}138}139140void JoltHingeJoint3D::_limits_changed() {141rebuild();142_wake_up_bodies();143}144145void JoltHingeJoint3D::_limit_spring_changed() {146rebuild();147_wake_up_bodies();148}149150void JoltHingeJoint3D::_motor_state_changed() {151_update_motor_state();152_wake_up_bodies();153}154155void JoltHingeJoint3D::_motor_speed_changed() {156_update_motor_velocity();157_wake_up_bodies();158}159160void JoltHingeJoint3D::_motor_limit_changed() {161_update_motor_limit();162_wake_up_bodies();163}164165JoltHingeJoint3D::JoltHingeJoint3D(const JoltJoint3D &p_old_joint, JoltBody3D *p_body_a, JoltBody3D *p_body_b, const Transform3D &p_local_ref_a, const Transform3D &p_local_ref_b) :166JoltJoint3D(p_old_joint, p_body_a, p_body_b, p_local_ref_a, p_local_ref_b) {167rebuild();168}169170double JoltHingeJoint3D::get_param(Parameter p_param) const {171switch (p_param) {172case PhysicsServer3D::HINGE_JOINT_BIAS: {173return HINGE_DEFAULT_BIAS;174}175case PhysicsServer3D::HINGE_JOINT_LIMIT_UPPER: {176return limit_upper;177}178case PhysicsServer3D::HINGE_JOINT_LIMIT_LOWER: {179return limit_lower;180}181case PhysicsServer3D::HINGE_JOINT_LIMIT_BIAS: {182return HINGE_DEFAULT_LIMIT_BIAS;183}184case PhysicsServer3D::HINGE_JOINT_LIMIT_SOFTNESS: {185return HINGE_DEFAULT_SOFTNESS;186}187case PhysicsServer3D::HINGE_JOINT_LIMIT_RELAXATION: {188return HINGE_DEFAULT_RELAXATION;189}190case PhysicsServer3D::HINGE_JOINT_MOTOR_TARGET_VELOCITY: {191return motor_target_speed;192}193case PhysicsServer3D::HINGE_JOINT_MOTOR_MAX_IMPULSE: {194// With Godot using max impulse instead of max torque we don't have much choice but to calculate this and hope the timestep doesn't change.195return motor_max_torque * estimate_physics_step();196}197default: {198ERR_FAIL_V_MSG(0.0, vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param));199}200}201}202203void JoltHingeJoint3D::set_param(Parameter p_param, double p_value) {204switch (p_param) {205case PhysicsServer3D::HINGE_JOINT_BIAS: {206if (!Math::is_equal_approx(p_value, HINGE_DEFAULT_BIAS)) {207WARN_PRINT(vformat("Hinge joint bias is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));208}209} break;210case PhysicsServer3D::HINGE_JOINT_LIMIT_UPPER: {211limit_upper = p_value;212_limits_changed();213} break;214case PhysicsServer3D::HINGE_JOINT_LIMIT_LOWER: {215limit_lower = p_value;216_limits_changed();217} break;218case PhysicsServer3D::HINGE_JOINT_LIMIT_BIAS: {219if (!Math::is_equal_approx(p_value, HINGE_DEFAULT_LIMIT_BIAS)) {220WARN_PRINT(vformat("Hinge joint bias limit is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));221}222} break;223case PhysicsServer3D::HINGE_JOINT_LIMIT_SOFTNESS: {224if (!Math::is_equal_approx(p_value, HINGE_DEFAULT_SOFTNESS)) {225WARN_PRINT(vformat("Hinge joint softness is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));226}227} break;228case PhysicsServer3D::HINGE_JOINT_LIMIT_RELAXATION: {229if (!Math::is_equal_approx(p_value, HINGE_DEFAULT_RELAXATION)) {230WARN_PRINT(vformat("Hinge joint relaxation is not supported when using Jolt Physics. Any such value will be ignored. This joint connects %s.", _bodies_to_string()));231}232} break;233case PhysicsServer3D::HINGE_JOINT_MOTOR_TARGET_VELOCITY: {234motor_target_speed = p_value;235_motor_speed_changed();236} break;237case PhysicsServer3D::HINGE_JOINT_MOTOR_MAX_IMPULSE: {238// With Godot using max impulse instead of max torque we don't have much choice but to calculate this and hope the timestep doesn't change.239motor_max_torque = p_value / estimate_physics_step();240_motor_limit_changed();241} break;242default: {243ERR_FAIL_MSG(vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param));244} break;245}246}247248double JoltHingeJoint3D::get_jolt_param(JoltParameter p_param) const {249switch (p_param) {250case JoltPhysicsServer3D::HINGE_JOINT_LIMIT_SPRING_FREQUENCY: {251return limit_spring_frequency;252}253case JoltPhysicsServer3D::HINGE_JOINT_LIMIT_SPRING_DAMPING: {254return limit_spring_damping;255}256case JoltPhysicsServer3D::HINGE_JOINT_MOTOR_MAX_TORQUE: {257return motor_max_torque;258}259default: {260ERR_FAIL_V_MSG(0.0, vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param));261}262}263}264265void JoltHingeJoint3D::set_jolt_param(JoltParameter p_param, double p_value) {266switch (p_param) {267case JoltPhysicsServer3D::HINGE_JOINT_LIMIT_SPRING_FREQUENCY: {268limit_spring_frequency = p_value;269_limit_spring_changed();270} break;271case JoltPhysicsServer3D::HINGE_JOINT_LIMIT_SPRING_DAMPING: {272limit_spring_damping = p_value;273_limit_spring_changed();274} break;275case JoltPhysicsServer3D::HINGE_JOINT_MOTOR_MAX_TORQUE: {276motor_max_torque = p_value;277_motor_limit_changed();278} break;279default: {280ERR_FAIL_MSG(vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param));281} break;282}283}284285bool JoltHingeJoint3D::get_flag(Flag p_flag) const {286switch (p_flag) {287case PhysicsServer3D::HINGE_JOINT_FLAG_USE_LIMIT: {288return limits_enabled;289}290case PhysicsServer3D::HINGE_JOINT_FLAG_ENABLE_MOTOR: {291return motor_enabled;292}293default: {294ERR_FAIL_V_MSG(false, vformat("Unhandled flag: '%d'. This should not happen. Please report this.", p_flag));295}296}297}298299void JoltHingeJoint3D::set_flag(Flag p_flag, bool p_enabled) {300switch (p_flag) {301case PhysicsServer3D::HINGE_JOINT_FLAG_USE_LIMIT: {302limits_enabled = p_enabled;303_limits_changed();304} break;305case PhysicsServer3D::HINGE_JOINT_FLAG_ENABLE_MOTOR: {306motor_enabled = p_enabled;307_motor_state_changed();308} break;309default: {310ERR_FAIL_MSG(vformat("Unhandled flag: '%d'. This should not happen. Please report this.", p_flag));311} break;312}313}314315bool JoltHingeJoint3D::get_jolt_flag(JoltFlag p_flag) const {316switch (p_flag) {317case JoltPhysicsServer3D::HINGE_JOINT_FLAG_USE_LIMIT_SPRING: {318return limit_spring_enabled;319}320default: {321ERR_FAIL_V_MSG(false, vformat("Unhandled flag: '%d'. This should not happen. Please report this.", p_flag));322}323}324}325326void JoltHingeJoint3D::set_jolt_flag(JoltFlag p_flag, bool p_enabled) {327switch (p_flag) {328case JoltPhysicsServer3D::HINGE_JOINT_FLAG_USE_LIMIT_SPRING: {329limit_spring_enabled = p_enabled;330_limit_spring_changed();331} break;332default: {333ERR_FAIL_MSG(vformat("Unhandled flag: '%d'. This should not happen. Please report this.", p_flag));334} break;335}336}337338float JoltHingeJoint3D::get_applied_force() const {339ERR_FAIL_NULL_V(jolt_ref, 0.0f);340341JoltSpace3D *space = get_space();342ERR_FAIL_NULL_V(space, 0.0f);343344const float last_step = space->get_last_step();345if (unlikely(last_step == 0.0f)) {346return 0.0f;347}348349if (_is_fixed()) {350JPH::FixedConstraint *constraint = static_cast<JPH::FixedConstraint *>(jolt_ref.GetPtr());351return constraint->GetTotalLambdaPosition().Length() / last_step;352} else {353JPH::HingeConstraint *constraint = static_cast<JPH::HingeConstraint *>(jolt_ref.GetPtr());354const JPH::Vec3 total_lambda = JPH::Vec3(constraint->GetTotalLambdaRotation()[0], constraint->GetTotalLambdaRotation()[1], constraint->GetTotalLambdaRotationLimits() + constraint->GetTotalLambdaMotor());355return total_lambda.Length() / last_step;356}357}358359float JoltHingeJoint3D::get_applied_torque() const {360ERR_FAIL_NULL_V(jolt_ref, 0.0f);361362JoltSpace3D *space = get_space();363ERR_FAIL_NULL_V(space, 0.0f);364365const float last_step = space->get_last_step();366if (unlikely(last_step == 0.0f)) {367return 0.0f;368}369370if (_is_fixed()) {371JPH::FixedConstraint *constraint = static_cast<JPH::FixedConstraint *>(jolt_ref.GetPtr());372return constraint->GetTotalLambdaRotation().Length() / last_step;373} else {374JPH::HingeConstraint *constraint = static_cast<JPH::HingeConstraint *>(jolt_ref.GetPtr());375return constraint->GetTotalLambdaRotation().Length() / last_step;376}377}378379void JoltHingeJoint3D::rebuild() {380destroy();381382JoltSpace3D *space = get_space();383if (space == nullptr) {384return;385}386387JPH::Body *jolt_body_a = body_a != nullptr ? body_a->get_jolt_body() : nullptr;388JPH::Body *jolt_body_b = body_b != nullptr ? body_b->get_jolt_body() : nullptr;389ERR_FAIL_COND(jolt_body_a == nullptr && jolt_body_b == nullptr);390391float ref_shift = 0.0f;392float limit = JPH::JPH_PI;393394if (limits_enabled && limit_lower <= limit_upper) {395const double limit_midpoint = (limit_lower + limit_upper) / 2.0f;396397ref_shift = float(-limit_midpoint);398limit = float(limit_upper - limit_midpoint);399}400401Transform3D shifted_ref_a;402Transform3D shifted_ref_b;403404_shift_reference_frames(Vector3(), Vector3(0.0f, 0.0f, ref_shift), shifted_ref_a, shifted_ref_b);405406if (_is_fixed()) {407jolt_ref = _build_fixed(jolt_body_a, jolt_body_b, shifted_ref_a, shifted_ref_b);408} else {409jolt_ref = _build_hinge(jolt_body_a, jolt_body_b, shifted_ref_a, shifted_ref_b, limit);410}411412space->add_joint(this);413414_update_enabled();415_update_iterations();416_update_motor_state();417_update_motor_velocity();418_update_motor_limit();419}420421422