Path: blob/master/modules/godot_physics_3d/joints/godot_hinge_joint_3d.cpp
10278 views
/**************************************************************************/1/* godot_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/*31Adapted to Godot from the Bullet library.32*/3334/*35Bullet Continuous Collision Detection and Physics Library36Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/3738This software is provided 'as-is', without any express or implied warranty.39In no event will the authors be held liable for any damages arising from the use of this software.40Permission is granted to anyone to use this software for any purpose,41including commercial applications, and to alter it and redistribute it freely,42subject to the following restrictions:43441. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.452. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.463. This notice may not be removed or altered from any source distribution.47*/4849#include "godot_hinge_joint_3d.h"5051GodotHingeJoint3D::GodotHingeJoint3D(GodotBody3D *rbA, GodotBody3D *rbB, const Transform3D &frameA, const Transform3D &frameB) :52GodotJoint3D(_arr, 2) {53A = rbA;54B = rbB;5556m_rbAFrame = frameA;57m_rbBFrame = frameB;58// flip axis59m_rbBFrame.basis[0][2] *= real_t(-1.);60m_rbBFrame.basis[1][2] *= real_t(-1.);61m_rbBFrame.basis[2][2] *= real_t(-1.);6263A->add_constraint(this, 0);64B->add_constraint(this, 1);65}6667GodotHingeJoint3D::GodotHingeJoint3D(GodotBody3D *rbA, GodotBody3D *rbB, const Vector3 &pivotInA, const Vector3 &pivotInB,68const Vector3 &axisInA, const Vector3 &axisInB) :69GodotJoint3D(_arr, 2) {70A = rbA;71B = rbB;7273m_rbAFrame.origin = pivotInA;7475// since no frame is given, assume this to be zero angle and just pick rb transform axis76Vector3 rbAxisA1 = rbA->get_transform().basis.get_column(0);7778Vector3 rbAxisA2;79real_t projection = axisInA.dot(rbAxisA1);80if (projection >= 1.0f - CMP_EPSILON) {81rbAxisA1 = -rbA->get_transform().basis.get_column(2);82rbAxisA2 = rbA->get_transform().basis.get_column(1);83} else if (projection <= -1.0f + CMP_EPSILON) {84rbAxisA1 = rbA->get_transform().basis.get_column(2);85rbAxisA2 = rbA->get_transform().basis.get_column(1);86} else {87rbAxisA2 = axisInA.cross(rbAxisA1);88rbAxisA1 = rbAxisA2.cross(axisInA);89}9091m_rbAFrame.basis = Basis(rbAxisA1.x, rbAxisA2.x, axisInA.x,92rbAxisA1.y, rbAxisA2.y, axisInA.y,93rbAxisA1.z, rbAxisA2.z, axisInA.z);9495Quaternion rotationArc = Quaternion(axisInA, axisInB);96Vector3 rbAxisB1 = rotationArc.xform(rbAxisA1);97Vector3 rbAxisB2 = axisInB.cross(rbAxisB1);9899m_rbBFrame.origin = pivotInB;100m_rbBFrame.basis = Basis(rbAxisB1.x, rbAxisB2.x, -axisInB.x,101rbAxisB1.y, rbAxisB2.y, -axisInB.y,102rbAxisB1.z, rbAxisB2.z, -axisInB.z);103104A->add_constraint(this, 0);105B->add_constraint(this, 1);106}107108bool GodotHingeJoint3D::setup(real_t p_step) {109dynamic_A = (A->get_mode() > PhysicsServer3D::BODY_MODE_KINEMATIC);110dynamic_B = (B->get_mode() > PhysicsServer3D::BODY_MODE_KINEMATIC);111112if (!dynamic_A && !dynamic_B) {113return false;114}115116m_appliedImpulse = real_t(0.);117118if (!m_angularOnly) {119Vector3 pivotAInW = A->get_transform().xform(m_rbAFrame.origin);120Vector3 pivotBInW = B->get_transform().xform(m_rbBFrame.origin);121Vector3 relPos = pivotBInW - pivotAInW;122123Vector3 normal[3];124if (Math::is_zero_approx(relPos.length_squared())) {125normal[0] = Vector3(real_t(1.0), 0, 0);126} else {127normal[0] = relPos.normalized();128}129130plane_space(normal[0], normal[1], normal[2]);131132for (int i = 0; i < 3; i++) {133memnew_placement(134&m_jac[i],135GodotJacobianEntry3D(136A->get_principal_inertia_axes().transposed(),137B->get_principal_inertia_axes().transposed(),138pivotAInW - A->get_transform().origin - A->get_center_of_mass(),139pivotBInW - B->get_transform().origin - B->get_center_of_mass(),140normal[i],141A->get_inv_inertia(),142A->get_inv_mass(),143B->get_inv_inertia(),144B->get_inv_mass()));145}146}147148//calculate two perpendicular jointAxis, orthogonal to hingeAxis149//these two jointAxis require equal angular velocities for both bodies150151//this is unused for now, it's a todo152Vector3 jointAxis0local;153Vector3 jointAxis1local;154155plane_space(m_rbAFrame.basis.get_column(2), jointAxis0local, jointAxis1local);156157Vector3 jointAxis0 = A->get_transform().basis.xform(jointAxis0local);158Vector3 jointAxis1 = A->get_transform().basis.xform(jointAxis1local);159Vector3 hingeAxisWorld = A->get_transform().basis.xform(m_rbAFrame.basis.get_column(2));160161memnew_placement(162&m_jacAng[0],163GodotJacobianEntry3D(164jointAxis0,165A->get_principal_inertia_axes().transposed(),166B->get_principal_inertia_axes().transposed(),167A->get_inv_inertia(),168B->get_inv_inertia()));169170memnew_placement(171&m_jacAng[1],172GodotJacobianEntry3D(173jointAxis1,174A->get_principal_inertia_axes().transposed(),175B->get_principal_inertia_axes().transposed(),176A->get_inv_inertia(),177B->get_inv_inertia()));178179memnew_placement(180&m_jacAng[2],181GodotJacobianEntry3D(182hingeAxisWorld,183A->get_principal_inertia_axes().transposed(),184B->get_principal_inertia_axes().transposed(),185A->get_inv_inertia(),186B->get_inv_inertia()));187188// Compute limit information189real_t hingeAngle = get_hinge_angle();190191//set bias, sign, clear accumulator192m_correction = real_t(0.);193m_limitSign = real_t(0.);194m_solveLimit = false;195m_accLimitImpulse = real_t(0.);196197if (m_useLimit && m_lowerLimit <= m_upperLimit) {198if (hingeAngle <= m_lowerLimit) {199m_correction = (m_lowerLimit - hingeAngle);200m_limitSign = 1.0f;201m_solveLimit = true;202} else if (hingeAngle >= m_upperLimit) {203m_correction = m_upperLimit - hingeAngle;204m_limitSign = -1.0f;205m_solveLimit = true;206}207}208209//Compute K = J*W*J' for hinge axis210Vector3 axisA = A->get_transform().basis.xform(m_rbAFrame.basis.get_column(2));211m_kHinge = 1.0f / (A->compute_angular_impulse_denominator(axisA) + B->compute_angular_impulse_denominator(axisA));212213return true;214}215216void GodotHingeJoint3D::solve(real_t p_step) {217Vector3 pivotAInW = A->get_transform().xform(m_rbAFrame.origin);218Vector3 pivotBInW = B->get_transform().xform(m_rbBFrame.origin);219220//real_t tau = real_t(0.3);221222//linear part223if (!m_angularOnly) {224Vector3 rel_pos1 = pivotAInW - A->get_transform().origin;225Vector3 rel_pos2 = pivotBInW - B->get_transform().origin;226227Vector3 vel1 = A->get_velocity_in_local_point(rel_pos1);228Vector3 vel2 = B->get_velocity_in_local_point(rel_pos2);229Vector3 vel = vel1 - vel2;230231for (int i = 0; i < 3; i++) {232const Vector3 &normal = m_jac[i].m_linearJointAxis;233real_t jacDiagABInv = real_t(1.) / m_jac[i].getDiagonal();234235real_t rel_vel;236rel_vel = normal.dot(vel);237//positional error (zeroth order error)238real_t depth = -(pivotAInW - pivotBInW).dot(normal); //this is the error projected on the normal239real_t impulse = depth * tau / p_step * jacDiagABInv - rel_vel * jacDiagABInv;240m_appliedImpulse += impulse;241Vector3 impulse_vector = normal * impulse;242if (dynamic_A) {243A->apply_impulse(impulse_vector, pivotAInW - A->get_transform().origin);244}245if (dynamic_B) {246B->apply_impulse(-impulse_vector, pivotBInW - B->get_transform().origin);247}248}249}250251{252///solve angular part253254// get axes in world space255Vector3 axisA = A->get_transform().basis.xform(m_rbAFrame.basis.get_column(2));256Vector3 axisB = B->get_transform().basis.xform(m_rbBFrame.basis.get_column(2));257258const Vector3 &angVelA = A->get_angular_velocity();259const Vector3 &angVelB = B->get_angular_velocity();260261Vector3 angVelAroundHingeAxisA = axisA * axisA.dot(angVelA);262Vector3 angVelAroundHingeAxisB = axisB * axisB.dot(angVelB);263264Vector3 angAorthog = angVelA - angVelAroundHingeAxisA;265Vector3 angBorthog = angVelB - angVelAroundHingeAxisB;266Vector3 velrelOrthog = angAorthog - angBorthog;267{268//solve orthogonal angular velocity correction269real_t relaxation = real_t(1.);270real_t len = velrelOrthog.length();271if (len > real_t(0.00001)) {272Vector3 normal = velrelOrthog.normalized();273real_t denom = A->compute_angular_impulse_denominator(normal) +274B->compute_angular_impulse_denominator(normal);275// scale for mass and relaxation276velrelOrthog *= (real_t(1.) / denom) * m_relaxationFactor;277}278279//solve angular positional correction280Vector3 angularError = -axisA.cross(axisB) * (real_t(1.) / p_step);281real_t len2 = angularError.length();282if (len2 > real_t(0.00001)) {283Vector3 normal2 = angularError.normalized();284real_t denom2 = A->compute_angular_impulse_denominator(normal2) +285B->compute_angular_impulse_denominator(normal2);286angularError *= (real_t(1.) / denom2) * relaxation;287}288289if (dynamic_A) {290A->apply_torque_impulse(-velrelOrthog + angularError);291}292if (dynamic_B) {293B->apply_torque_impulse(velrelOrthog - angularError);294}295296// solve limit297if (m_solveLimit) {298real_t amplitude = ((angVelB - angVelA).dot(axisA) * m_relaxationFactor + m_correction * (real_t(1.) / p_step) * m_biasFactor) * m_limitSign;299300real_t impulseMag = amplitude * m_kHinge;301302// Clamp the accumulated impulse303real_t temp = m_accLimitImpulse;304m_accLimitImpulse = MAX(m_accLimitImpulse + impulseMag, real_t(0));305impulseMag = m_accLimitImpulse - temp;306307Vector3 impulse = axisA * impulseMag * m_limitSign;308if (dynamic_A) {309A->apply_torque_impulse(impulse);310}311if (dynamic_B) {312B->apply_torque_impulse(-impulse);313}314}315}316317//apply motor318if (m_enableAngularMotor) {319//todo: add limits too320Vector3 angularLimit(0, 0, 0);321322Vector3 velrel = angVelAroundHingeAxisA - angVelAroundHingeAxisB;323real_t projRelVel = velrel.dot(axisA);324325real_t desiredMotorVel = m_motorTargetVelocity;326real_t motor_relvel = desiredMotorVel - projRelVel;327328real_t unclippedMotorImpulse = m_kHinge * motor_relvel;329//todo: should clip against accumulated impulse330real_t clippedMotorImpulse = unclippedMotorImpulse > m_maxMotorImpulse ? m_maxMotorImpulse : unclippedMotorImpulse;331clippedMotorImpulse = clippedMotorImpulse < -m_maxMotorImpulse ? -m_maxMotorImpulse : clippedMotorImpulse;332Vector3 motorImp = clippedMotorImpulse * axisA;333334if (dynamic_A) {335A->apply_torque_impulse(motorImp + angularLimit);336}337if (dynamic_B) {338B->apply_torque_impulse(-motorImp - angularLimit);339}340}341}342}343344/*345void HingeJointSW::updateRHS(real_t timeStep)346{347(void)timeStep;348}349350*/351352real_t GodotHingeJoint3D::get_hinge_angle() {353const Vector3 refAxis0 = A->get_transform().basis.xform(m_rbAFrame.basis.get_column(0));354const Vector3 refAxis1 = A->get_transform().basis.xform(m_rbAFrame.basis.get_column(1));355const Vector3 swingAxis = B->get_transform().basis.xform(m_rbBFrame.basis.get_column(1));356357return atan2fast(swingAxis.dot(refAxis0), swingAxis.dot(refAxis1));358}359360void GodotHingeJoint3D::set_param(PhysicsServer3D::HingeJointParam p_param, real_t p_value) {361switch (p_param) {362case PhysicsServer3D::HINGE_JOINT_BIAS:363tau = p_value;364break;365case PhysicsServer3D::HINGE_JOINT_LIMIT_UPPER:366m_upperLimit = p_value;367break;368case PhysicsServer3D::HINGE_JOINT_LIMIT_LOWER:369m_lowerLimit = p_value;370break;371case PhysicsServer3D::HINGE_JOINT_LIMIT_BIAS:372m_biasFactor = p_value;373break;374case PhysicsServer3D::HINGE_JOINT_LIMIT_SOFTNESS:375m_limitSoftness = p_value;376break;377case PhysicsServer3D::HINGE_JOINT_LIMIT_RELAXATION:378m_relaxationFactor = p_value;379break;380case PhysicsServer3D::HINGE_JOINT_MOTOR_TARGET_VELOCITY:381m_motorTargetVelocity = p_value;382break;383case PhysicsServer3D::HINGE_JOINT_MOTOR_MAX_IMPULSE:384m_maxMotorImpulse = p_value;385break;386case PhysicsServer3D::HINGE_JOINT_MAX:387break; // Can't happen, but silences warning388}389}390391real_t GodotHingeJoint3D::get_param(PhysicsServer3D::HingeJointParam p_param) const {392switch (p_param) {393case PhysicsServer3D::HINGE_JOINT_BIAS:394return tau;395case PhysicsServer3D::HINGE_JOINT_LIMIT_UPPER:396return m_upperLimit;397case PhysicsServer3D::HINGE_JOINT_LIMIT_LOWER:398return m_lowerLimit;399case PhysicsServer3D::HINGE_JOINT_LIMIT_BIAS:400return m_biasFactor;401case PhysicsServer3D::HINGE_JOINT_LIMIT_SOFTNESS:402return m_limitSoftness;403case PhysicsServer3D::HINGE_JOINT_LIMIT_RELAXATION:404return m_relaxationFactor;405case PhysicsServer3D::HINGE_JOINT_MOTOR_TARGET_VELOCITY:406return m_motorTargetVelocity;407case PhysicsServer3D::HINGE_JOINT_MOTOR_MAX_IMPULSE:408return m_maxMotorImpulse;409case PhysicsServer3D::HINGE_JOINT_MAX:410break; // Can't happen, but silences warning411}412413return 0;414}415416void GodotHingeJoint3D::set_flag(PhysicsServer3D::HingeJointFlag p_flag, bool p_value) {417switch (p_flag) {418case PhysicsServer3D::HINGE_JOINT_FLAG_USE_LIMIT:419m_useLimit = p_value;420break;421case PhysicsServer3D::HINGE_JOINT_FLAG_ENABLE_MOTOR:422m_enableAngularMotor = p_value;423break;424case PhysicsServer3D::HINGE_JOINT_FLAG_MAX:425break; // Can't happen, but silences warning426}427}428429bool GodotHingeJoint3D::get_flag(PhysicsServer3D::HingeJointFlag p_flag) const {430switch (p_flag) {431case PhysicsServer3D::HINGE_JOINT_FLAG_USE_LIMIT:432return m_useLimit;433case PhysicsServer3D::HINGE_JOINT_FLAG_ENABLE_MOTOR:434return m_enableAngularMotor;435case PhysicsServer3D::HINGE_JOINT_FLAG_MAX:436break; // Can't happen, but silences warning437}438439return false;440}441442443