2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* godot_joints_2d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2021-10-18 21:24:30 +02:00
# include "godot_joints_2d.h"
2017-08-27 21:07:15 +02:00
2021-10-18 21:24:30 +02:00
# include "godot_space_2d.h"
2014-02-10 02:10:30 +01:00
//based on chipmunk joint constraints
/* Copyright (c) 2007 Scott Lembcke
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to deal
* in the Software without restriction , including without limitation the rights
* to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
*/
2021-10-18 21:24:30 +02:00
void GodotJoint2D : : copy_settings_from ( GodotJoint2D * p_joint ) {
2021-02-09 17:19:03 +01:00
set_self ( p_joint - > get_self ( ) ) ;
set_max_force ( p_joint - > get_max_force ( ) ) ;
set_bias ( p_joint - > get_bias ( ) ) ;
set_max_bias ( p_joint - > get_max_bias ( ) ) ;
disable_collisions_between_bodies ( p_joint - > is_disabled_collisions_between_bodies ( ) ) ;
}
2021-10-18 21:24:30 +02:00
static inline real_t k_scalar ( GodotBody2D * a , GodotBody2D * b , const Vector2 & rA , const Vector2 & rB , const Vector2 & n ) {
2021-09-14 11:01:49 +02:00
real_t value = 0.0 ;
2014-02-10 02:10:30 +01:00
{
value + = a - > get_inv_mass ( ) ;
2021-06-11 02:37:19 +02:00
real_t rcn = ( rA - a - > get_center_of_mass ( ) ) . cross ( n ) ;
2014-02-10 02:10:30 +01:00
value + = a - > get_inv_inertia ( ) * rcn * rcn ;
}
if ( b ) {
value + = b - > get_inv_mass ( ) ;
2021-06-11 02:37:19 +02:00
real_t rcn = ( rB - b - > get_center_of_mass ( ) ) . cross ( n ) ;
2014-02-10 02:10:30 +01:00
value + = b - > get_inv_inertia ( ) * rcn * rcn ;
}
return value ;
}
static inline Vector2
2021-10-18 21:24:30 +02:00
relative_velocity ( GodotBody2D * a , GodotBody2D * b , Vector2 rA , Vector2 rB ) {
2021-06-11 02:37:19 +02:00
Vector2 sum = a - > get_linear_velocity ( ) - ( rA - a - > get_center_of_mass ( ) ) . orthogonal ( ) * a - > get_angular_velocity ( ) ;
2020-05-14 16:41:43 +02:00
if ( b ) {
2021-06-11 02:37:19 +02:00
return ( b - > get_linear_velocity ( ) - ( rB - b - > get_center_of_mass ( ) ) . orthogonal ( ) * b - > get_angular_velocity ( ) ) - sum ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
return - sum ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
static inline real_t
2021-10-18 21:24:30 +02:00
normal_relative_velocity ( GodotBody2D * a , GodotBody2D * b , Vector2 rA , Vector2 rB , Vector2 n ) {
2014-02-10 02:10:30 +01:00
return relative_velocity ( a , b , rA , rB ) . dot ( n ) ;
}
2021-10-18 21:24:30 +02:00
bool GodotPinJoint2D : : setup ( real_t p_step ) {
2021-04-20 03:38:11 +02:00
dynamic_A = ( A - > get_mode ( ) > PhysicsServer2D : : BODY_MODE_KINEMATIC ) ;
dynamic_B = ( B - > get_mode ( ) > PhysicsServer2D : : BODY_MODE_KINEMATIC ) ;
if ( ! dynamic_A & & ! dynamic_B ) {
2021-04-16 02:37:45 +02:00
return false ;
}
2021-10-18 21:24:30 +02:00
GodotSpace2D * space = A - > get_space ( ) ;
2023-09-09 17:04:18 +02:00
ERR_FAIL_NULL_V ( space , false ) ;
2021-04-16 02:37:45 +02:00
2014-02-10 02:10:30 +01:00
rA = A - > get_transform ( ) . basis_xform ( anchor_A ) ;
rB = B ? B - > get_transform ( ) . basis_xform ( anchor_B ) : anchor_B ;
real_t B_inv_mass = B ? B - > get_inv_mass ( ) : 0.0 ;
2017-01-11 04:52:51 +01:00
Transform2D K1 ;
2014-02-10 02:10:30 +01:00
K1 [ 0 ] . x = A - > get_inv_mass ( ) + B_inv_mass ;
K1 [ 1 ] . x = 0.0f ;
K1 [ 0 ] . y = 0.0f ;
K1 [ 1 ] . y = A - > get_inv_mass ( ) + B_inv_mass ;
2022-03-16 19:55:20 +01:00
Vector2 r1 = rA - A - > get_center_of_mass ( ) ;
2017-01-11 04:52:51 +01:00
Transform2D K2 ;
2022-03-16 19:55:20 +01:00
K2 [ 0 ] . x = A - > get_inv_inertia ( ) * r1 . y * r1 . y ;
K2 [ 1 ] . x = - A - > get_inv_inertia ( ) * r1 . x * r1 . y ;
K2 [ 0 ] . y = - A - > get_inv_inertia ( ) * r1 . x * r1 . y ;
K2 [ 1 ] . y = A - > get_inv_inertia ( ) * r1 . x * r1 . x ;
2014-02-10 02:10:30 +01:00
2017-01-11 04:52:51 +01:00
Transform2D K ;
2014-02-10 02:10:30 +01:00
K [ 0 ] = K1 [ 0 ] + K2 [ 0 ] ;
K [ 1 ] = K1 [ 1 ] + K2 [ 1 ] ;
if ( B ) {
2022-03-16 19:55:20 +01:00
Vector2 r2 = rB - B - > get_center_of_mass ( ) ;
2017-01-11 04:52:51 +01:00
Transform2D K3 ;
2022-03-16 19:55:20 +01:00
K3 [ 0 ] . x = B - > get_inv_inertia ( ) * r2 . y * r2 . y ;
K3 [ 1 ] . x = - B - > get_inv_inertia ( ) * r2 . x * r2 . y ;
K3 [ 0 ] . y = - B - > get_inv_inertia ( ) * r2 . x * r2 . y ;
K3 [ 1 ] . y = B - > get_inv_inertia ( ) * r2 . x * r2 . x ;
2014-02-10 02:10:30 +01:00
K [ 0 ] + = K3 [ 0 ] ;
K [ 1 ] + = K3 [ 1 ] ;
}
K [ 0 ] . x + = softness ;
K [ 1 ] . y + = softness ;
M = K . affine_inverse ( ) ;
Vector2 gA = rA + A - > get_transform ( ) . get_origin ( ) ;
Vector2 gB = B ? rB + B - > get_transform ( ) . get_origin ( ) : rB ;
Vector2 delta = gB - gA ;
bias = delta * - ( get_bias ( ) = = 0 ? space - > get_constraint_bias ( ) : get_bias ( ) ) * ( 1.0 / p_step ) ;
2023-09-13 13:45:24 +02:00
// Compute max impulse.
jn_max = get_max_force ( ) * p_step ;
2014-02-10 02:10:30 +01:00
return true ;
}
2018-02-08 08:48:14 +01:00
inline Vector2 custom_cross ( const Vector2 & p_vec , real_t p_other ) {
return Vector2 ( p_other * p_vec . y , - p_other * p_vec . x ) ;
}
2021-10-18 21:24:30 +02:00
bool GodotPinJoint2D : : pre_solve ( real_t p_step ) {
2021-04-20 03:38:11 +02:00
// Apply accumulated impulse.
if ( dynamic_A ) {
A - > apply_impulse ( - P , rA ) ;
}
if ( B & & dynamic_B ) {
B - > apply_impulse ( P , rB ) ;
}
2023-09-13 13:45:24 +02:00
// Angle limits joint pre_solve step taken from https://github.com/slembcke/Chipmunk2D/blob/d0239ef4599b3688a5a336373f7d0a68426414ba/src/cpRotaryLimitJoint.c
real_t i_sum_local = A - > get_inv_inertia ( ) ;
if ( B ) {
i_sum_local + = B - > get_inv_inertia ( ) ;
}
i_sum = 1.0 / ( i_sum_local ) ;
if ( angular_limit_enabled & & B ) {
Vector2 diff_vector = B - > get_transform ( ) . get_origin ( ) - A - > get_transform ( ) . get_origin ( ) ;
diff_vector = diff_vector . rotated ( - initial_angle ) ;
real_t dist = diff_vector . angle ( ) ;
real_t pdist = 0.0 ;
if ( dist > angular_limit_upper ) {
pdist = dist - angular_limit_upper ;
} else if ( dist < angular_limit_lower ) {
pdist = dist - angular_limit_lower ;
}
real_t error_bias = Math : : pow ( 1.0 - 0.15 , 60.0 ) ;
// Calculate bias velocity.
bias_velocity = - CLAMP ( ( - 1.0 - Math : : pow ( error_bias , p_step ) ) * pdist / p_step , - get_max_bias ( ) , get_max_bias ( ) ) ;
// If the bias velocity is 0, the joint is not at a limit.
if ( bias_velocity > = - CMP_EPSILON & & bias_velocity < = CMP_EPSILON ) {
j_acc = 0 ;
is_joint_at_limit = false ;
} else {
is_joint_at_limit = true ;
}
} else {
bias_velocity = 0.0 ;
}
2021-04-20 03:38:11 +02:00
return true ;
}
2021-10-18 21:24:30 +02:00
void GodotPinJoint2D : : solve ( real_t p_step ) {
2023-09-13 13:45:24 +02:00
// Compute relative velocity.
2021-06-11 02:37:19 +02:00
Vector2 vA = A - > get_linear_velocity ( ) - custom_cross ( rA - A - > get_center_of_mass ( ) , A - > get_angular_velocity ( ) ) ;
2014-02-10 02:10:30 +01:00
Vector2 rel_vel ;
2020-05-14 16:41:43 +02:00
if ( B ) {
2021-06-11 02:37:19 +02:00
rel_vel = B - > get_linear_velocity ( ) - custom_cross ( rB - B - > get_center_of_mass ( ) , B - > get_angular_velocity ( ) ) - vA ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
rel_vel = - vA ;
2020-05-14 16:41:43 +02:00
}
2023-09-13 13:45:24 +02:00
// Angle limits joint solve step taken from https://github.com/slembcke/Chipmunk2D/blob/d0239ef4599b3688a5a336373f7d0a68426414ba/src/cpRotaryLimitJoint.c
if ( ( angular_limit_enabled | | motor_enabled ) & & B ) {
// Compute relative rotational velocity.
real_t wr = B - > get_angular_velocity ( ) - A - > get_angular_velocity ( ) ;
// Motor solve part taken from https://github.com/slembcke/Chipmunk2D/blob/d0239ef4599b3688a5a336373f7d0a68426414ba/src/cpSimpleMotor.c
if ( motor_enabled ) {
wr - = motor_target_velocity ;
}
real_t j_max = jn_max ;
// Compute normal impulse.
real_t j = - ( bias_velocity + wr ) * i_sum ;
real_t j_old = j_acc ;
// Only enable the limits if we have to.
if ( angular_limit_enabled & & is_joint_at_limit ) {
if ( bias_velocity < 0.0 ) {
j_acc = CLAMP ( j_old + j , 0.0 , j_max ) ;
} else {
j_acc = CLAMP ( j_old + j , - j_max , 0.0 ) ;
}
} else {
j_acc = CLAMP ( j_old + j , - j_max , j_max ) ;
}
j = j_acc - j_old ;
A - > apply_torque_impulse ( - j * A - > get_inv_inertia ( ) ) ;
B - > apply_torque_impulse ( j * B - > get_inv_inertia ( ) ) ;
}
2014-02-10 02:10:30 +01:00
Vector2 impulse = M . basis_xform ( bias - rel_vel - Vector2 ( softness , softness ) * P ) ;
2021-04-20 03:38:11 +02:00
if ( dynamic_A ) {
A - > apply_impulse ( - impulse , rA ) ;
}
if ( B & & dynamic_B ) {
2020-03-26 05:23:34 +01:00
B - > apply_impulse ( impulse , rB ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
P + = impulse ;
}
2021-10-18 21:24:30 +02:00
void GodotPinJoint2D : : set_param ( PhysicsServer2D : : PinJointParam p_param , real_t p_value ) {
2023-09-13 13:45:24 +02:00
switch ( p_param ) {
case PhysicsServer2D : : PIN_JOINT_SOFTNESS : {
softness = p_value ;
} break ;
case PhysicsServer2D : : PIN_JOINT_LIMIT_UPPER : {
angular_limit_upper = p_value ;
} break ;
case PhysicsServer2D : : PIN_JOINT_LIMIT_LOWER : {
angular_limit_lower = p_value ;
} break ;
case PhysicsServer2D : : PIN_JOINT_MOTOR_TARGET_VELOCITY : {
motor_target_velocity = p_value ;
} break ;
2020-05-14 16:41:43 +02:00
}
2015-10-10 22:28:05 +02:00
}
2021-10-18 21:24:30 +02:00
real_t GodotPinJoint2D : : get_param ( PhysicsServer2D : : PinJointParam p_param ) const {
2023-09-13 13:45:24 +02:00
switch ( p_param ) {
case PhysicsServer2D : : PIN_JOINT_SOFTNESS : {
return softness ;
}
case PhysicsServer2D : : PIN_JOINT_LIMIT_UPPER : {
return angular_limit_upper ;
}
case PhysicsServer2D : : PIN_JOINT_LIMIT_LOWER : {
return angular_limit_lower ;
}
case PhysicsServer2D : : PIN_JOINT_MOTOR_TARGET_VELOCITY : {
return motor_target_velocity ;
}
}
ERR_FAIL_V ( 0 ) ;
}
void GodotPinJoint2D : : set_flag ( PhysicsServer2D : : PinJointFlag p_flag , bool p_enabled ) {
switch ( p_flag ) {
case PhysicsServer2D : : PIN_JOINT_FLAG_ANGULAR_LIMIT_ENABLED : {
angular_limit_enabled = p_enabled ;
} break ;
case PhysicsServer2D : : PIN_JOINT_FLAG_MOTOR_ENABLED : {
motor_enabled = p_enabled ;
} break ;
}
}
bool GodotPinJoint2D : : get_flag ( PhysicsServer2D : : PinJointFlag p_flag ) const {
switch ( p_flag ) {
case PhysicsServer2D : : PIN_JOINT_FLAG_ANGULAR_LIMIT_ENABLED : {
return angular_limit_enabled ;
}
case PhysicsServer2D : : PIN_JOINT_FLAG_MOTOR_ENABLED : {
return motor_enabled ;
}
2020-05-14 16:41:43 +02:00
}
2015-10-10 22:28:05 +02:00
ERR_FAIL_V ( 0 ) ;
}
2014-02-10 02:10:30 +01:00
2021-10-18 21:24:30 +02:00
GodotPinJoint2D : : GodotPinJoint2D ( const Vector2 & p_pos , GodotBody2D * p_body_a , GodotBody2D * p_body_b ) :
GodotJoint2D ( _arr , p_body_b ? 2 : 1 ) {
2014-02-10 02:10:30 +01:00
A = p_body_a ;
B = p_body_b ;
anchor_A = p_body_a - > get_inv_transform ( ) . xform ( p_pos ) ;
anchor_B = p_body_b ? p_body_b - > get_inv_transform ( ) . xform ( p_pos ) : p_pos ;
p_body_a - > add_constraint ( this , 0 ) ;
2020-05-14 16:41:43 +02:00
if ( p_body_b ) {
2014-02-10 02:10:30 +01:00
p_body_b - > add_constraint ( this , 1 ) ;
2023-09-13 13:45:24 +02:00
initial_angle = A - > get_transform ( ) . get_origin ( ) . angle_to_point ( B - > get_transform ( ) . get_origin ( ) ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
//////////////////////////////////////////////
//////////////////////////////////////////////
//////////////////////////////////////////////
static inline void
2021-10-18 21:24:30 +02:00
k_tensor ( GodotBody2D * a , GodotBody2D * b , Vector2 r1 , Vector2 r2 , Vector2 * k1 , Vector2 * k2 ) {
2014-02-10 02:10:30 +01:00
// calculate mass matrix
// If I wasn't lazy and wrote a proper matrix class, this wouldn't be so gross...
real_t k11 , k12 , k21 , k22 ;
real_t m_sum = a - > get_inv_mass ( ) + b - > get_inv_mass ( ) ;
// start with I*m_sum
k11 = m_sum ;
k12 = 0.0f ;
k21 = 0.0f ;
k22 = m_sum ;
2021-06-11 02:37:19 +02:00
r1 - = a - > get_center_of_mass ( ) ;
r2 - = b - > get_center_of_mass ( ) ;
2014-02-10 02:10:30 +01:00
// add the influence from r1
real_t a_i_inv = a - > get_inv_inertia ( ) ;
real_t r1xsq = r1 . x * r1 . x * a_i_inv ;
real_t r1ysq = r1 . y * r1 . y * a_i_inv ;
real_t r1nxy = - r1 . x * r1 . y * a_i_inv ;
k11 + = r1ysq ;
k12 + = r1nxy ;
k21 + = r1nxy ;
k22 + = r1xsq ;
// add the influnce from r2
real_t b_i_inv = b - > get_inv_inertia ( ) ;
real_t r2xsq = r2 . x * r2 . x * b_i_inv ;
real_t r2ysq = r2 . y * r2 . y * b_i_inv ;
real_t r2nxy = - r2 . x * r2 . y * b_i_inv ;
k11 + = r2ysq ;
k12 + = r2nxy ;
k21 + = r2nxy ;
k22 + = r2xsq ;
// invert
real_t determinant = k11 * k22 - k12 * k21 ;
ERR_FAIL_COND ( determinant = = 0.0 ) ;
real_t det_inv = 1.0f / determinant ;
* k1 = Vector2 ( k22 * det_inv , - k12 * det_inv ) ;
* k2 = Vector2 ( - k21 * det_inv , k11 * det_inv ) ;
}
static _FORCE_INLINE_ Vector2
mult_k ( const Vector2 & vr , const Vector2 & k1 , const Vector2 & k2 ) {
return Vector2 ( vr . dot ( k1 ) , vr . dot ( k2 ) ) ;
}
2021-10-18 21:24:30 +02:00
bool GodotGrooveJoint2D : : setup ( real_t p_step ) {
2021-04-20 03:38:11 +02:00
dynamic_A = ( A - > get_mode ( ) > PhysicsServer2D : : BODY_MODE_KINEMATIC ) ;
dynamic_B = ( B - > get_mode ( ) > PhysicsServer2D : : BODY_MODE_KINEMATIC ) ;
if ( ! dynamic_A & & ! dynamic_B ) {
2021-04-16 02:37:45 +02:00
return false ;
}
2021-10-18 21:24:30 +02:00
GodotSpace2D * space = A - > get_space ( ) ;
2023-09-09 17:04:18 +02:00
ERR_FAIL_NULL_V ( space , false ) ;
2021-04-20 03:38:11 +02:00
2014-02-10 02:10:30 +01:00
// calculate endpoints in worldspace
Vector2 ta = A - > get_transform ( ) . xform ( A_groove_1 ) ;
Vector2 tb = A - > get_transform ( ) . xform ( A_groove_2 ) ;
// calculate axis
2020-12-06 19:16:06 +01:00
Vector2 n = - ( tb - ta ) . orthogonal ( ) . normalized ( ) ;
2014-02-10 02:10:30 +01:00
real_t d = ta . dot ( n ) ;
xf_normal = n ;
rB = B - > get_transform ( ) . basis_xform ( B_anchor ) ;
// calculate tangential distance along the axis of rB
real_t td = ( B - > get_transform ( ) . get_origin ( ) + rB ) . cross ( n ) ;
// calculate clamping factor and rB
if ( td < = ta . cross ( n ) ) {
clamp = 1.0f ;
rA = ta - A - > get_transform ( ) . get_origin ( ) ;
} else if ( td > = tb . cross ( n ) ) {
clamp = - 1.0f ;
rA = tb - A - > get_transform ( ) . get_origin ( ) ;
} else {
clamp = 0.0f ;
//joint->r1 = cpvsub(cpvadd(cpvmult(cpvperp(n), -td), cpvmult(n, d)), a->p);
2020-12-06 19:16:06 +01:00
rA = ( ( - n . orthogonal ( ) * - td ) + n * d ) - A - > get_transform ( ) . get_origin ( ) ;
2014-02-10 02:10:30 +01:00
}
// Calculate mass tensor
k_tensor ( A , B , rA , rB , & k1 , & k2 ) ;
// compute max impulse
jn_max = get_max_force ( ) * p_step ;
// calculate bias velocity
2017-01-14 12:26:56 +01:00
//cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1));
//joint->bias = cpvclamp(cpvmult(delta, -joint->constraint.biasCoef*dt_inv), joint->constraint.maxBias);
2014-02-10 02:10:30 +01:00
Vector2 delta = ( B - > get_transform ( ) . get_origin ( ) + rB ) - ( A - > get_transform ( ) . get_origin ( ) + rA ) ;
2017-08-21 21:15:36 +02:00
2019-03-27 19:21:07 +01:00
real_t _b = get_bias ( ) ;
2021-02-01 06:42:00 +01:00
gbias = ( delta * - ( _b = = 0 ? space - > get_constraint_bias ( ) : _b ) * ( 1.0 / p_step ) ) . limit_length ( get_max_bias ( ) ) ;
2014-02-10 02:10:30 +01:00
correct = true ;
return true ;
}
2021-10-18 21:24:30 +02:00
bool GodotGrooveJoint2D : : pre_solve ( real_t p_step ) {
2021-04-20 03:38:11 +02:00
// Apply accumulated impulse.
if ( dynamic_A ) {
A - > apply_impulse ( - jn_acc , rA ) ;
}
if ( dynamic_B ) {
B - > apply_impulse ( jn_acc , rB ) ;
}
return true ;
}
2021-10-18 21:24:30 +02:00
void GodotGrooveJoint2D : : solve ( real_t p_step ) {
2014-02-10 02:10:30 +01:00
// compute impulse
Vector2 vr = relative_velocity ( A , B , rA , rB ) ;
Vector2 j = mult_k ( gbias - vr , k1 , k2 ) ;
Vector2 jOld = jn_acc ;
j + = jOld ;
2021-02-01 06:42:00 +01:00
jn_acc = ( ( ( clamp * j . cross ( xf_normal ) ) > 0 ) ? j : j . project ( xf_normal ) ) . limit_length ( jn_max ) ;
2014-02-10 02:10:30 +01:00
j = jn_acc - jOld ;
2021-04-20 03:38:11 +02:00
if ( dynamic_A ) {
A - > apply_impulse ( - j , rA ) ;
}
if ( dynamic_B ) {
B - > apply_impulse ( j , rB ) ;
}
2014-02-10 02:10:30 +01:00
}
2021-10-18 21:24:30 +02:00
GodotGrooveJoint2D : : GodotGrooveJoint2D ( const Vector2 & p_a_groove1 , const Vector2 & p_a_groove2 , const Vector2 & p_b_anchor , GodotBody2D * p_body_a , GodotBody2D * p_body_b ) :
GodotJoint2D ( _arr , 2 ) {
2014-02-10 02:10:30 +01:00
A = p_body_a ;
B = p_body_b ;
A_groove_1 = A - > get_inv_transform ( ) . xform ( p_a_groove1 ) ;
A_groove_2 = A - > get_inv_transform ( ) . xform ( p_a_groove2 ) ;
B_anchor = B - > get_inv_transform ( ) . xform ( p_b_anchor ) ;
2020-12-06 19:16:06 +01:00
A_groove_normal = - ( A_groove_2 - A_groove_1 ) . normalized ( ) . orthogonal ( ) ;
2014-02-10 02:10:30 +01:00
A - > add_constraint ( this , 0 ) ;
B - > add_constraint ( this , 1 ) ;
}
//////////////////////////////////////////////
//////////////////////////////////////////////
//////////////////////////////////////////////
2021-10-18 21:24:30 +02:00
bool GodotDampedSpringJoint2D : : setup ( real_t p_step ) {
2021-04-20 03:38:11 +02:00
dynamic_A = ( A - > get_mode ( ) > PhysicsServer2D : : BODY_MODE_KINEMATIC ) ;
dynamic_B = ( B - > get_mode ( ) > PhysicsServer2D : : BODY_MODE_KINEMATIC ) ;
if ( ! dynamic_A & & ! dynamic_B ) {
2021-04-16 02:37:45 +02:00
return false ;
}
2014-02-10 02:10:30 +01:00
rA = A - > get_transform ( ) . basis_xform ( anchor_A ) ;
rB = B - > get_transform ( ) . basis_xform ( anchor_B ) ;
Vector2 delta = ( B - > get_transform ( ) . get_origin ( ) + rB ) - ( A - > get_transform ( ) . get_origin ( ) + rA ) ;
real_t dist = delta . length ( ) ;
2020-05-14 16:41:43 +02:00
if ( dist ) {
2014-02-10 02:10:30 +01:00
n = delta / dist ;
2020-05-14 16:41:43 +02:00
} else {
2014-02-10 02:10:30 +01:00
n = Vector2 ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
real_t k = k_scalar ( A , B , rA , rB , n ) ;
n_mass = 1.0f / k ;
target_vrn = 0.0f ;
v_coef = 1.0f - Math : : exp ( - damping * ( p_step ) * k ) ;
2021-04-20 03:38:11 +02:00
// Calculate spring force.
2014-02-10 02:10:30 +01:00
real_t f_spring = ( rest_length - dist ) * stiffness ;
2021-04-20 03:38:11 +02:00
j = n * f_spring * ( p_step ) ;
2014-02-10 02:10:30 +01:00
2021-04-20 03:38:11 +02:00
return true ;
}
2021-10-18 21:24:30 +02:00
bool GodotDampedSpringJoint2D : : pre_solve ( real_t p_step ) {
2021-04-20 03:38:11 +02:00
// Apply spring force.
if ( dynamic_A ) {
A - > apply_impulse ( - j , rA ) ;
}
if ( dynamic_B ) {
B - > apply_impulse ( j , rB ) ;
}
2014-02-10 02:10:30 +01:00
return true ;
}
2021-10-18 21:24:30 +02:00
void GodotDampedSpringJoint2D : : solve ( real_t p_step ) {
2014-02-10 02:10:30 +01:00
// compute relative velocity
real_t vrn = normal_relative_velocity ( A , B , rA , rB , n ) - target_vrn ;
// compute velocity loss from drag
// not 100% certain this is derived correctly, though it makes sense
real_t v_damp = - vrn * v_coef ;
target_vrn = vrn + v_damp ;
2022-09-29 11:53:28 +02:00
Vector2 j_new = n * v_damp * n_mass ;
2014-02-10 02:10:30 +01:00
2021-04-20 03:38:11 +02:00
if ( dynamic_A ) {
2022-09-29 11:53:28 +02:00
A - > apply_impulse ( - j_new , rA ) ;
2021-04-20 03:38:11 +02:00
}
if ( dynamic_B ) {
2022-09-29 11:53:28 +02:00
B - > apply_impulse ( j_new , rB ) ;
2021-04-20 03:38:11 +02:00
}
2014-02-10 02:10:30 +01:00
}
2021-10-18 21:24:30 +02:00
void GodotDampedSpringJoint2D : : set_param ( PhysicsServer2D : : DampedSpringParam p_param , real_t p_value ) {
2014-02-10 02:10:30 +01:00
switch ( p_param ) {
2020-06-04 18:53:29 +02:00
case PhysicsServer2D : : DAMPED_SPRING_REST_LENGTH : {
2014-02-10 02:10:30 +01:00
rest_length = p_value ;
} break ;
2020-06-04 18:53:29 +02:00
case PhysicsServer2D : : DAMPED_SPRING_DAMPING : {
2014-02-10 02:10:30 +01:00
damping = p_value ;
} break ;
2020-06-04 18:53:29 +02:00
case PhysicsServer2D : : DAMPED_SPRING_STIFFNESS : {
2014-02-10 02:10:30 +01:00
stiffness = p_value ;
} break ;
}
}
2021-10-18 21:24:30 +02:00
real_t GodotDampedSpringJoint2D : : get_param ( PhysicsServer2D : : DampedSpringParam p_param ) const {
2014-02-10 02:10:30 +01:00
switch ( p_param ) {
2020-06-04 18:53:29 +02:00
case PhysicsServer2D : : DAMPED_SPRING_REST_LENGTH : {
2014-02-10 02:10:30 +01:00
return rest_length ;
} break ;
2020-06-04 18:53:29 +02:00
case PhysicsServer2D : : DAMPED_SPRING_DAMPING : {
2014-02-10 02:10:30 +01:00
return damping ;
} break ;
2020-06-04 18:53:29 +02:00
case PhysicsServer2D : : DAMPED_SPRING_STIFFNESS : {
2014-02-10 02:10:30 +01:00
return stiffness ;
} break ;
}
ERR_FAIL_V ( 0 ) ;
}
2021-10-18 21:24:30 +02:00
GodotDampedSpringJoint2D : : GodotDampedSpringJoint2D ( const Vector2 & p_anchor_a , const Vector2 & p_anchor_b , GodotBody2D * p_body_a , GodotBody2D * p_body_b ) :
GodotJoint2D ( _arr , 2 ) {
2014-02-10 02:10:30 +01:00
A = p_body_a ;
B = p_body_b ;
anchor_A = A - > get_inv_transform ( ) . xform ( p_anchor_a ) ;
anchor_B = B - > get_inv_transform ( ) . xform ( p_anchor_b ) ;
rest_length = p_anchor_a . distance_to ( p_anchor_b ) ;
A - > add_constraint ( this , 0 ) ;
B - > add_constraint ( this , 1 ) ;
}