diff --git a/doc/classes/Generic6DOFJoint3D.xml b/doc/classes/Generic6DOFJoint3D.xml index 8f30c0e772..b1b5a728e4 100644 --- a/doc/classes/Generic6DOFJoint3D.xml +++ b/doc/classes/Generic6DOFJoint3D.xml @@ -116,6 +116,18 @@ + + The maximum torque the joint can apply around the X angular axis. Used by both the spring drive and the angular motor; supersedes [member angular_motor_x/force_limit] when explicitly set. + [b]Note:[/b] Only for Jolt backend. Other 3D physics backends may ignore this value. + + + The maximum torque the joint can apply around the Y angular axis. Used by both the spring drive and the angular motor; supersedes [member angular_motor_y/force_limit] when explicitly set. + [b]Note:[/b] Only for Jolt backend. Other 3D physics backends may ignore this value. + + + The maximum torque the joint can apply around the Z angular axis. Used by both the spring drive and the angular motor; supersedes [member angular_motor_z/force_limit] when explicitly set. + [b]Note:[/b] Only for Jolt backend. Other 3D physics backends may ignore this value. + The amount of rotational damping across the X axis. The lower, the longer an impulse from one side takes to travel to the other side. @@ -192,7 +204,7 @@ If [code]true[/code], a rotating motor at the X axis is enabled. - + Maximum acceleration for the motor at the X axis. @@ -201,7 +213,7 @@ If [code]true[/code], a rotating motor at the Y axis is enabled. - + Maximum acceleration for the motor at the Y axis. @@ -210,7 +222,7 @@ If [code]true[/code], a rotating motor at the Z axis is enabled. - + Maximum acceleration for the motor at the Z axis. @@ -240,6 +252,18 @@ + + The maximum force the joint can apply along the X linear axis. Used by both the spring drive and the linear motor; supersedes [member linear_motor_x/force_limit] when explicitly set. + [b]Note:[/b] Only for Jolt backend. Other 3D physics backends may ignore this value. + + + The maximum force the joint can apply along the Y linear axis. Used by both the spring drive and the linear motor; supersedes [member linear_motor_y/force_limit] when explicitly set. + [b]Note:[/b] Only for Jolt backend. Other 3D physics backends may ignore this value. + + + The maximum force the joint can apply along the Z linear axis. Used by both the spring drive and the linear motor; supersedes [member linear_motor_z/force_limit] when explicitly set. + [b]Note:[/b] Only for Jolt backend. Other 3D physics backends may ignore this value. + The amount of damping that happens at the X motion. @@ -297,7 +321,7 @@ If [code]true[/code], then there is a linear motor on the X axis. It will attempt to reach the target velocity while staying within the force limits. - + The maximum force the linear motor can apply on the X axis while trying to reach the target velocity. @@ -306,7 +330,7 @@ If [code]true[/code], then there is a linear motor on the Y axis. It will attempt to reach the target velocity while staying within the force limits. - + The maximum force the linear motor can apply on the Y axis while trying to reach the target velocity. @@ -315,7 +339,7 @@ If [code]true[/code], then there is a linear motor on the Z axis. It will attempt to reach the target velocity while staying within the force limits. - + The maximum force the linear motor can apply on the Z axis while trying to reach the target velocity. @@ -365,7 +389,7 @@ The velocity the linear motor will try to reach. - + The maximum force the linear motor will apply while trying to reach the velocity target. @@ -398,7 +422,7 @@ Target speed for the motor at the axes. - + Maximum acceleration for the motor at the axes. @@ -407,7 +431,17 @@ - + + The maximum force the joint can apply along this linear axis. + Used by both the spring drive and the linear motor; supersedes [constant PARAM_LINEAR_MOTOR_FORCE_LIMIT] when set. + [b]Note:[/b] Only for Jolt backend. Other 3D physics backends may ignore this value. + + + The maximum torque the joint can apply around this angular axis. + Used by both the spring drive and the angular motor; supersedes [constant PARAM_ANGULAR_MOTOR_FORCE_LIMIT] when set. + [b]Note:[/b] Only for Jolt backend. Other 3D physics backends may ignore this value. + + Represents the size of the [enum Param] enum. diff --git a/doc/classes/PhysicsServer3D.xml b/doc/classes/PhysicsServer3D.xml index 37024d1405..02d50e604f 100644 --- a/doc/classes/PhysicsServer3D.xml +++ b/doc/classes/PhysicsServer3D.xml @@ -1650,7 +1650,7 @@ The velocity that the joint's linear motor will attempt to reach. - + The maximum force that the linear motor can apply while trying to reach the target velocity. @@ -1688,7 +1688,7 @@ Target speed for the motor at the axes. - + Maximum acceleration for the motor at the axes. @@ -1697,7 +1697,17 @@ - + + The maximum force the joint can apply along this linear axis. + Used by both the spring drive and the linear motor; supersedes [constant G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT] when set. + [b]Note:[/b] Only for Jolt backend. Other 3D physics backends may ignore this value. + + + The maximum torque the joint can apply around this angular axis. + Used by both the spring drive and the angular motor; supersedes [constant G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT] when set. + [b]Note:[/b] Only for Jolt backend. Other 3D physics backends may ignore this value. + + Represents the size of the [enum G6DOFJointAxisParam] enum. diff --git a/modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.cpp b/modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.cpp index 5f9cf9de49..1197e7cdfe 100644 --- a/modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.cpp +++ b/modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.cpp @@ -526,6 +526,12 @@ void GodotGeneric6DOFJoint3D::set_param(Vector3::Axis p_axis, PhysicsServer3D::G case PhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT: { // Not implemented in GodotPhysics3D backend } break; + case PhysicsServer3D::G6DOF_JOINT_LINEAR_DRIVE_FORCE_LIMIT: { + m_linearDriveForceLimit[p_axis] = p_value; + } break; + case PhysicsServer3D::G6DOF_JOINT_ANGULAR_DRIVE_TORQUE_LIMIT: { + m_angularDriveTorqueLimit[p_axis] = p_value; + } break; case PhysicsServer3D::G6DOF_JOINT_MAX: break; // Can't happen, but silences warning } @@ -613,6 +619,12 @@ real_t GodotGeneric6DOFJoint3D::get_param(Vector3::Axis p_axis, PhysicsServer3D: case PhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT: { // Not implemented in GodotPhysics3D backend } break; + case PhysicsServer3D::G6DOF_JOINT_LINEAR_DRIVE_FORCE_LIMIT: { + return m_linearDriveForceLimit[p_axis]; + } break; + case PhysicsServer3D::G6DOF_JOINT_ANGULAR_DRIVE_TORQUE_LIMIT: { + return m_angularDriveTorqueLimit[p_axis]; + } break; case PhysicsServer3D::G6DOF_JOINT_MAX: break; // Can't happen, but silences warning } diff --git a/modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.h b/modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.h index a7ced46d0b..ddebfb1019 100644 --- a/modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.h +++ b/modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.h @@ -37,6 +37,8 @@ Adapted to Godot from the Bullet library. #include "../godot_joint_3d.h" #include "godot_jacobian_entry_3d.h" +#include + /* Bullet Continuous Collision Detection and Physics Library Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ @@ -164,11 +166,13 @@ protected: //! Linear_Limit_parameters //!@{ GodotG6DOFTranslationalLimitMotor3D m_linearLimits; + real_t m_linearDriveForceLimit[3] = { FLT_MAX, FLT_MAX, FLT_MAX }; //!@} //! hinge_parameters //!@{ GodotG6DOFRotationalLimitMotor3D m_angularLimits[3]; + real_t m_angularDriveTorqueLimit[3] = { FLT_MAX, FLT_MAX, FLT_MAX }; //!@} protected: diff --git a/modules/jolt_physics/joints/jolt_generic_6dof_joint_3d.cpp b/modules/jolt_physics/joints/jolt_generic_6dof_joint_3d.cpp index 06678cd800..755c65ea73 100644 --- a/modules/jolt_physics/joints/jolt_generic_6dof_joint_3d.cpp +++ b/modules/jolt_physics/joints/jolt_generic_6dof_joint_3d.cpp @@ -151,10 +151,11 @@ void JoltGeneric6DOFJoint3D::_update_motor_limit(int p_axis) { float limit = FLT_MAX; - if (motor_enabled[p_axis]) { + // Drive limit applies in both spring and motor modes when explicitly set + if (drive_limit_set[p_axis]) { + limit = (float)drive_limit[p_axis]; + } else if (motor_enabled[p_axis]) { limit = (float)motor_limit[p_axis]; - } else if (spring_enabled[p_axis]) { - limit = (float)spring_limit[p_axis]; } if (p_axis >= AXIS_LINEAR_X && p_axis <= AXIS_LINEAR_Z) { @@ -263,7 +264,7 @@ void JoltGeneric6DOFJoint3D::_spring_equilibrium_changed(int p_axis) { _wake_up_bodies(); } -void JoltGeneric6DOFJoint3D::_spring_limit_changed(int p_axis) { +void JoltGeneric6DOFJoint3D::_drive_limit_changed(int p_axis) { _update_motor_limit(p_axis); _wake_up_bodies(); } @@ -308,6 +309,9 @@ double JoltGeneric6DOFJoint3D::get_param(Axis p_axis, Param p_param) const { case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_SPRING_EQUILIBRIUM_POINT: { return spring_equilibrium[axis_lin]; } + case PhysicsServer3D::G6DOF_JOINT_LINEAR_DRIVE_FORCE_LIMIT: { + return drive_limit[axis_lin]; + } case PhysicsServer3D::G6DOF_JOINT_ANGULAR_LOWER_LIMIT: { return limit_lower[axis_ang]; } @@ -344,6 +348,9 @@ double JoltGeneric6DOFJoint3D::get_param(Axis p_axis, Param p_param) const { case JoltPhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT: { return spring_equilibrium[axis_ang]; } + case PhysicsServer3D::G6DOF_JOINT_ANGULAR_DRIVE_TORQUE_LIMIT: { + return drive_limit[axis_ang]; + } default: { ERR_FAIL_V_MSG(0.0, vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param)); } @@ -398,6 +405,11 @@ void JoltGeneric6DOFJoint3D::set_param(Axis p_axis, Param p_param, double p_valu spring_equilibrium[axis_lin] = p_value; _spring_equilibrium_changed(axis_lin); } break; + case PhysicsServer3D::G6DOF_JOINT_LINEAR_DRIVE_FORCE_LIMIT: { + drive_limit[axis_lin] = p_value; + drive_limit_set[axis_lin] = true; + _drive_limit_changed(axis_lin); + } break; case PhysicsServer3D::G6DOF_JOINT_ANGULAR_LOWER_LIMIT: { limit_lower[axis_ang] = p_value; _limits_changed(); @@ -452,6 +464,11 @@ void JoltGeneric6DOFJoint3D::set_param(Axis p_axis, Param p_param, double p_valu has_angular_target_rotation = false; _spring_equilibrium_changed(axis_ang); } break; + case PhysicsServer3D::G6DOF_JOINT_ANGULAR_DRIVE_TORQUE_LIMIT: { + drive_limit[axis_ang] = p_value; + drive_limit_set[axis_ang] = true; + _drive_limit_changed(axis_ang); + } break; default: { ERR_FAIL_MSG(vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param)); } break; @@ -560,9 +577,6 @@ double JoltGeneric6DOFJoint3D::get_jolt_param(Axis p_axis, JoltParam p_param) co case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_SPRING_FREQUENCY: { return spring_frequency[axis_lin]; } - case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_SPRING_MAX_FORCE: { - return spring_limit[axis_lin]; - } case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_LIMIT_SPRING_FREQUENCY: { return limit_spring_frequency[axis_lin]; } @@ -572,9 +586,6 @@ double JoltGeneric6DOFJoint3D::get_jolt_param(Axis p_axis, JoltParam p_param) co case JoltPhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_FREQUENCY: { return spring_frequency[axis_ang]; } - case JoltPhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_MAX_TORQUE: { - return spring_limit[axis_ang]; - } default: { ERR_FAIL_V_MSG(0.0, vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param)); } @@ -590,10 +601,6 @@ void JoltGeneric6DOFJoint3D::set_jolt_param(Axis p_axis, JoltParam p_param, doub spring_frequency[axis_lin] = p_value; _spring_parameters_changed(axis_lin); } break; - case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_SPRING_MAX_FORCE: { - spring_limit[axis_lin] = p_value; - _spring_limit_changed(axis_lin); - } break; case JoltPhysicsServer3D::G6DOF_JOINT_LINEAR_LIMIT_SPRING_FREQUENCY: { limit_spring_frequency[axis_lin] = p_value; _limit_spring_parameters_changed(axis_lin); @@ -606,10 +613,6 @@ void JoltGeneric6DOFJoint3D::set_jolt_param(Axis p_axis, JoltParam p_param, doub spring_frequency[axis_ang] = p_value; _spring_parameters_changed(axis_ang); } break; - case JoltPhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_MAX_TORQUE: { - spring_limit[axis_ang] = p_value; - _spring_limit_changed(axis_ang); - } break; default: { ERR_FAIL_MSG(vformat("Unhandled parameter: '%d'. This should not happen. Please report this.", p_param)); } break; diff --git a/modules/jolt_physics/joints/jolt_generic_6dof_joint_3d.h b/modules/jolt_physics/joints/jolt_generic_6dof_joint_3d.h index 04612c4951..7c3194dec4 100644 --- a/modules/jolt_physics/joints/jolt_generic_6dof_joint_3d.h +++ b/modules/jolt_physics/joints/jolt_generic_6dof_joint_3d.h @@ -70,7 +70,8 @@ class JoltGeneric6DOFJoint3D final : public JoltJoint3D { double spring_frequency[AXIS_COUNT] = {}; double spring_damping[AXIS_COUNT] = {}; double spring_equilibrium[AXIS_COUNT] = {}; - double spring_limit[AXIS_COUNT] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; + double drive_limit[AXIS_COUNT] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; + bool drive_limit_set[AXIS_COUNT] = {}; Quaternion angular_target_rotation; bool has_angular_target_rotation = false; @@ -102,7 +103,7 @@ class JoltGeneric6DOFJoint3D final : public JoltJoint3D { void _spring_state_changed(int p_axis); void _spring_parameters_changed(int p_axis); void _spring_equilibrium_changed(int p_axis); - void _spring_limit_changed(int p_axis); + void _drive_limit_changed(int p_axis); public: JoltGeneric6DOFJoint3D(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); diff --git a/modules/jolt_physics/jolt_physics_server_3d.h b/modules/jolt_physics/jolt_physics_server_3d.h index 1ab95b796b..6aca5d68b5 100644 --- a/modules/jolt_physics/jolt_physics_server_3d.h +++ b/modules/jolt_physics/jolt_physics_server_3d.h @@ -108,8 +108,6 @@ public: G6DOF_JOINT_LINEAR_LIMIT_SPRING_FREQUENCY, G6DOF_JOINT_LINEAR_LIMIT_SPRING_DAMPING, G6DOF_JOINT_ANGULAR_SPRING_FREQUENCY, - G6DOF_JOINT_LINEAR_SPRING_MAX_FORCE, - G6DOF_JOINT_ANGULAR_SPRING_MAX_TORQUE, }; enum G6DOFJointAxisFlagJolt { diff --git a/scene/3d/physics/joints/generic_6dof_joint_3d.cpp b/scene/3d/physics/joints/generic_6dof_joint_3d.cpp index 475cfb3fcc..386b70f723 100644 --- a/scene/3d/physics/joints/generic_6dof_joint_3d.cpp +++ b/scene/3d/physics/joints/generic_6dof_joint_3d.cpp @@ -32,6 +32,8 @@ #include "core/object/class_db.h" +#include // FLT_MAX + void Generic6DOFJoint3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_param_x", "param", "value"), &Generic6DOFJoint3D::set_param_x); ClassDB::bind_method(D_METHOD("get_param_x", "param"), &Generic6DOFJoint3D::get_param_x); @@ -110,6 +112,12 @@ void Generic6DOFJoint3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/damping"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_spring_z/equilibrium_point", PROPERTY_HINT_NONE, "suffix:m"), "set_param_z", "get_param_z", PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT); + ADD_GROUP("Linear Drive", "linear_drive_"); + + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_drive_x/force_limit", PROPERTY_HINT_NONE, U"suffix:kg\u22C5m/s\u00B2 (N)"), "set_param_x", "get_param_x", PARAM_LINEAR_DRIVE_FORCE_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_drive_y/force_limit", PROPERTY_HINT_NONE, U"suffix:kg\u22C5m/s\u00B2 (N)"), "set_param_y", "get_param_y", PARAM_LINEAR_DRIVE_FORCE_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_drive_z/force_limit", PROPERTY_HINT_NONE, U"suffix:kg\u22C5m/s\u00B2 (N)"), "set_param_z", "get_param_z", PARAM_LINEAR_DRIVE_FORCE_LIMIT); + ADD_GROUP("Angular Limit", "angular_limit_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_LIMIT); @@ -170,6 +178,12 @@ void Generic6DOFJoint3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_z/damping"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_z/equilibrium_point", PROPERTY_HINT_RANGE, "-180,180,0.01,radians_as_degrees"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT); + ADD_GROUP("Angular Drive", "angular_drive_"); + + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_drive_x/torque_limit", PROPERTY_HINT_NONE, U"suffix:kg\u22C5m\u00B2/s\u00B2 (Nm)"), "set_param_x", "get_param_x", PARAM_ANGULAR_DRIVE_TORQUE_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_drive_y/torque_limit", PROPERTY_HINT_NONE, U"suffix:kg\u22C5m\u00B2/s\u00B2 (Nm)"), "set_param_y", "get_param_y", PARAM_ANGULAR_DRIVE_TORQUE_LIMIT); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_drive_z/torque_limit", PROPERTY_HINT_NONE, U"suffix:kg\u22C5m\u00B2/s\u00B2 (Nm)"), "set_param_z", "get_param_z", PARAM_ANGULAR_DRIVE_TORQUE_LIMIT); + BIND_ENUM_CONSTANT(PARAM_LINEAR_LOWER_LIMIT); BIND_ENUM_CONSTANT(PARAM_LINEAR_UPPER_LIMIT); BIND_ENUM_CONSTANT(PARAM_LINEAR_LIMIT_SOFTNESS); @@ -192,6 +206,8 @@ void Generic6DOFJoint3D::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_ANGULAR_SPRING_STIFFNESS); BIND_ENUM_CONSTANT(PARAM_ANGULAR_SPRING_DAMPING); BIND_ENUM_CONSTANT(PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT); + BIND_ENUM_CONSTANT(PARAM_LINEAR_DRIVE_FORCE_LIMIT); + BIND_ENUM_CONSTANT(PARAM_ANGULAR_DRIVE_TORQUE_LIMIT); BIND_ENUM_CONSTANT(PARAM_MAX); BIND_ENUM_CONSTANT(FLAG_ENABLE_LINEAR_LIMIT); @@ -209,6 +225,10 @@ void Generic6DOFJoint3D::set_param_x(Param p_param, real_t p_value) { if (p_param == PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT) { has_angular_target_rotation = false; } + if (!setting_default_params) { + _set_drive_limit_explicit(Vector3::AXIS_X, p_param); + _warn_if_deprecated_param(p_param); + } if (is_configured()) { PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_rid(), Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value); } @@ -227,6 +247,10 @@ void Generic6DOFJoint3D::set_param_y(Param p_param, real_t p_value) { if (p_param == PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT) { has_angular_target_rotation = false; } + if (!setting_default_params) { + _set_drive_limit_explicit(Vector3::AXIS_Y, p_param); + _warn_if_deprecated_param(p_param); + } if (is_configured()) { PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_rid(), Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value); } @@ -244,12 +268,41 @@ void Generic6DOFJoint3D::set_param_z(Param p_param, real_t p_value) { if (p_param == PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT) { has_angular_target_rotation = false; } + if (!setting_default_params) { + _set_drive_limit_explicit(Vector3::AXIS_Z, p_param); + _warn_if_deprecated_param(p_param); + } if (is_configured()) { PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_rid(), Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value); } update_gizmos(); } +void Generic6DOFJoint3D::_warn_if_deprecated_param(Param p_param) { + if (p_param == PARAM_LINEAR_MOTOR_FORCE_LIMIT) { + WARN_PRINT_ONCE("PARAM_LINEAR_MOTOR_FORCE_LIMIT is deprecated and will be removed in a future release. Use PARAM_LINEAR_DRIVE_FORCE_LIMIT, which applies in both spring and motor modes."); + } else if (p_param == PARAM_ANGULAR_MOTOR_FORCE_LIMIT) { + WARN_PRINT_ONCE("PARAM_ANGULAR_MOTOR_FORCE_LIMIT is deprecated and will be removed in a future release. Use PARAM_ANGULAR_DRIVE_TORQUE_LIMIT, which applies in both spring and motor modes."); + } +} + +void Generic6DOFJoint3D::_set_drive_limit_explicit(Vector3::Axis p_axis, Param p_param) { + if (p_param == PARAM_LINEAR_DRIVE_FORCE_LIMIT) { + linear_drive_force_limit_set[p_axis] = true; + } else if (p_param == PARAM_ANGULAR_DRIVE_TORQUE_LIMIT) { + angular_drive_torque_limit_set[p_axis] = true; + } +} + +bool Generic6DOFJoint3D::_should_replay_param(Vector3::Axis p_axis, Param p_param) const { + if (p_param == PARAM_LINEAR_DRIVE_FORCE_LIMIT) { + return linear_drive_force_limit_set[p_axis]; + } else if (p_param == PARAM_ANGULAR_DRIVE_TORQUE_LIMIT) { + return angular_drive_torque_limit_set[p_axis]; + } + return true; +} + real_t Generic6DOFJoint3D::get_param_z(Param p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); return params_z[p_param]; @@ -369,9 +422,16 @@ void Generic6DOFJoint3D::_configure_joint(RID p_joint, PhysicsBody3D *body_a, Ph PhysicsServer3D::get_singleton()->joint_make_generic_6dof(p_joint, body_a->get_rid(), local_a, body_b ? body_b->get_rid() : RID(), local_b); for (int i = 0; i < PARAM_MAX; i++) { - PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(p_joint, Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisParam(i), params_x[i]); - PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(p_joint, Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisParam(i), params_y[i]); - PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(p_joint, Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisParam(i), params_z[i]); + const Param param = static_cast(i); + if (_should_replay_param(Vector3::AXIS_X, param)) { + PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(p_joint, Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisParam(i), params_x[i]); + } + if (_should_replay_param(Vector3::AXIS_Y, param)) { + PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(p_joint, Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisParam(i), params_y[i]); + } + if (_should_replay_param(Vector3::AXIS_Z, param)) { + PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(p_joint, Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisParam(i), params_z[i]); + } } for (int i = 0; i < FLAG_MAX; i++) { PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(p_joint, Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisFlag(i), flags_x[i]); @@ -395,6 +455,7 @@ Generic6DOFJoint3D::Generic6DOFJoint3D() { set_param_x(PARAM_LINEAR_SPRING_STIFFNESS, 0.01); set_param_x(PARAM_LINEAR_SPRING_DAMPING, 0.01); set_param_x(PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT, 0.0); + set_param_x(PARAM_LINEAR_DRIVE_FORCE_LIMIT, FLT_MAX); set_param_x(PARAM_ANGULAR_LOWER_LIMIT, 0); set_param_x(PARAM_ANGULAR_UPPER_LIMIT, 0); set_param_x(PARAM_ANGULAR_LIMIT_SOFTNESS, 0.5f); @@ -407,6 +468,7 @@ Generic6DOFJoint3D::Generic6DOFJoint3D() { set_param_x(PARAM_ANGULAR_SPRING_STIFFNESS, 0); set_param_x(PARAM_ANGULAR_SPRING_DAMPING, 0); set_param_x(PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT, 0); + set_param_x(PARAM_ANGULAR_DRIVE_TORQUE_LIMIT, FLT_MAX); set_flag_x(FLAG_ENABLE_ANGULAR_LIMIT, true); set_flag_x(FLAG_ENABLE_LINEAR_LIMIT, true); @@ -425,6 +487,7 @@ Generic6DOFJoint3D::Generic6DOFJoint3D() { set_param_y(PARAM_LINEAR_SPRING_STIFFNESS, 0.01); set_param_y(PARAM_LINEAR_SPRING_DAMPING, 0.01); set_param_y(PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT, 0.0); + set_param_y(PARAM_LINEAR_DRIVE_FORCE_LIMIT, FLT_MAX); set_param_y(PARAM_ANGULAR_LOWER_LIMIT, 0); set_param_y(PARAM_ANGULAR_UPPER_LIMIT, 0); set_param_y(PARAM_ANGULAR_LIMIT_SOFTNESS, 0.5f); @@ -437,6 +500,7 @@ Generic6DOFJoint3D::Generic6DOFJoint3D() { set_param_y(PARAM_ANGULAR_SPRING_STIFFNESS, 0); set_param_y(PARAM_ANGULAR_SPRING_DAMPING, 0); set_param_y(PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT, 0); + set_param_y(PARAM_ANGULAR_DRIVE_TORQUE_LIMIT, FLT_MAX); set_flag_y(FLAG_ENABLE_ANGULAR_LIMIT, true); set_flag_y(FLAG_ENABLE_LINEAR_LIMIT, true); @@ -455,6 +519,7 @@ Generic6DOFJoint3D::Generic6DOFJoint3D() { set_param_z(PARAM_LINEAR_SPRING_STIFFNESS, 0.01); set_param_z(PARAM_LINEAR_SPRING_DAMPING, 0.01); set_param_z(PARAM_LINEAR_SPRING_EQUILIBRIUM_POINT, 0.0); + set_param_z(PARAM_LINEAR_DRIVE_FORCE_LIMIT, FLT_MAX); set_param_z(PARAM_ANGULAR_LOWER_LIMIT, 0); set_param_z(PARAM_ANGULAR_UPPER_LIMIT, 0); set_param_z(PARAM_ANGULAR_LIMIT_SOFTNESS, 0.5f); @@ -467,6 +532,7 @@ Generic6DOFJoint3D::Generic6DOFJoint3D() { set_param_z(PARAM_ANGULAR_SPRING_STIFFNESS, 0); set_param_z(PARAM_ANGULAR_SPRING_DAMPING, 0); set_param_z(PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT, 0); + set_param_z(PARAM_ANGULAR_DRIVE_TORQUE_LIMIT, FLT_MAX); set_flag_z(FLAG_ENABLE_ANGULAR_LIMIT, true); set_flag_z(FLAG_ENABLE_LINEAR_LIMIT, true); @@ -474,4 +540,6 @@ Generic6DOFJoint3D::Generic6DOFJoint3D() { set_flag_z(FLAG_ENABLE_LINEAR_SPRING, false); set_flag_z(FLAG_ENABLE_MOTOR, false); set_flag_z(FLAG_ENABLE_LINEAR_MOTOR, false); + + setting_default_params = false; } diff --git a/scene/3d/physics/joints/generic_6dof_joint_3d.h b/scene/3d/physics/joints/generic_6dof_joint_3d.h index 02de0b86e3..04feb06b7f 100644 --- a/scene/3d/physics/joints/generic_6dof_joint_3d.h +++ b/scene/3d/physics/joints/generic_6dof_joint_3d.h @@ -59,6 +59,8 @@ public: PARAM_ANGULAR_SPRING_STIFFNESS = PhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS, PARAM_ANGULAR_SPRING_DAMPING = PhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_DAMPING, PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT = PhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT, + PARAM_LINEAR_DRIVE_FORCE_LIMIT = PhysicsServer3D::G6DOF_JOINT_LINEAR_DRIVE_FORCE_LIMIT, + PARAM_ANGULAR_DRIVE_TORQUE_LIMIT = PhysicsServer3D::G6DOF_JOINT_ANGULAR_DRIVE_TORQUE_LIMIT, PARAM_MAX = PhysicsServer3D::G6DOF_JOINT_MAX, }; @@ -79,12 +81,19 @@ protected: bool flags_y[FLAG_MAX]; real_t params_z[PARAM_MAX]; bool flags_z[FLAG_MAX]; + bool linear_drive_force_limit_set[3] = {}; + bool angular_drive_torque_limit_set[3] = {}; + bool setting_default_params = true; Quaternion angular_target_rotation; bool has_angular_target_rotation = false; virtual void _configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) override; static void _bind_methods(); + static void _warn_if_deprecated_param(Param p_param); + void _set_drive_limit_explicit(Vector3::Axis p_axis, Param p_param); + bool _should_replay_param(Vector3::Axis p_axis, Param p_param) const; + static bool _is_valid_angular_target_rotation(const Quaternion &p_target_rotation); public: diff --git a/servers/physics_3d/physics_server_3d.cpp b/servers/physics_3d/physics_server_3d.cpp index f2165e05a8..13cbf6d207 100644 --- a/servers/physics_3d/physics_server_3d.cpp +++ b/servers/physics_3d/physics_server_3d.cpp @@ -1018,6 +1018,8 @@ void PhysicsServer3D::_bind_methods() { BIND_ENUM_CONSTANT(G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS); BIND_ENUM_CONSTANT(G6DOF_JOINT_ANGULAR_SPRING_DAMPING); BIND_ENUM_CONSTANT(G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT); + BIND_ENUM_CONSTANT(G6DOF_JOINT_LINEAR_DRIVE_FORCE_LIMIT); + BIND_ENUM_CONSTANT(G6DOF_JOINT_ANGULAR_DRIVE_TORQUE_LIMIT); BIND_ENUM_CONSTANT(G6DOF_JOINT_MAX); BIND_ENUM_CONSTANT(G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT); diff --git a/servers/physics_3d/physics_server_3d.h b/servers/physics_3d/physics_server_3d.h index 572ce02ae2..89cafbb8d7 100644 --- a/servers/physics_3d/physics_server_3d.h +++ b/servers/physics_3d/physics_server_3d.h @@ -773,6 +773,8 @@ public: G6DOF_JOINT_ANGULAR_SPRING_STIFFNESS, G6DOF_JOINT_ANGULAR_SPRING_DAMPING, G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT, + G6DOF_JOINT_LINEAR_DRIVE_FORCE_LIMIT, + G6DOF_JOINT_ANGULAR_DRIVE_TORQUE_LIMIT, G6DOF_JOINT_MAX }; diff --git a/tests/scene/test_generic_6dof_joint_3d.cpp b/tests/scene/test_generic_6dof_joint_3d.cpp index 166e267560..da967f66c1 100644 --- a/tests/scene/test_generic_6dof_joint_3d.cpp +++ b/tests/scene/test_generic_6dof_joint_3d.cpp @@ -35,6 +35,9 @@ TEST_FORCE_LINK(test_generic_6dof_joint_3d) #ifndef PHYSICS_3D_DISABLED #include "scene/3d/physics/joints/generic_6dof_joint_3d.h" +#include "servers/physics_3d/physics_server_3d.h" + +#include namespace TestGeneric6DOFJoint3D { @@ -106,6 +109,130 @@ TEST_CASE("[SceneTree][Generic6DOFJoint3D] Explicit quaternion target shadows Eu memdelete(joint); } +static RID make_configured_generic_6dof_joint(PhysicsServer3D *p_server, RID &r_body_a, RID &r_body_b) { + r_body_a = p_server->body_create(); + r_body_b = p_server->body_create(); + + const RID joint = p_server->joint_create(); + p_server->joint_make_generic_6dof(joint, r_body_a, Transform3D(), r_body_b, Transform3D()); + return joint; +} + +TEST_CASE("[SceneTree][Generic6DOFJoint3D] Drive force and torque limits params round-trip") { + Generic6DOFJoint3D *joint = memnew(Generic6DOFJoint3D); + + joint->set_param_x(Generic6DOFJoint3D::PARAM_LINEAR_DRIVE_FORCE_LIMIT, 123.0); + joint->set_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_DRIVE_TORQUE_LIMIT, 45.0); + + CHECK(joint->get_param_x(Generic6DOFJoint3D::PARAM_LINEAR_DRIVE_FORCE_LIMIT) == 123.0); + CHECK(joint->get_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_DRIVE_TORQUE_LIMIT) == 45.0); + + memdelete(joint); +} + +TEST_CASE("[SceneTree][Generic6DOFJoint3D] Drive force and torque limits defaults are unlimited") { + Generic6DOFJoint3D *joint = memnew(Generic6DOFJoint3D); + + CHECK(joint->get_param_x(Generic6DOFJoint3D::PARAM_LINEAR_DRIVE_FORCE_LIMIT) == FLT_MAX); + CHECK(joint->get_param_y(Generic6DOFJoint3D::PARAM_LINEAR_DRIVE_FORCE_LIMIT) == FLT_MAX); + CHECK(joint->get_param_z(Generic6DOFJoint3D::PARAM_LINEAR_DRIVE_FORCE_LIMIT) == FLT_MAX); + + CHECK(joint->get_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_DRIVE_TORQUE_LIMIT) == FLT_MAX); + CHECK(joint->get_param_y(Generic6DOFJoint3D::PARAM_ANGULAR_DRIVE_TORQUE_LIMIT) == FLT_MAX); + CHECK(joint->get_param_z(Generic6DOFJoint3D::PARAM_ANGULAR_DRIVE_TORQUE_LIMIT) == FLT_MAX); + + memdelete(joint); +} + +TEST_CASE("[SceneTree][PhysicsServer3D][Generic6DOFJoint3D] Drive force and torque limits params round-trip per axis") { + PhysicsServer3D *physics_server = PhysicsServer3D::get_singleton(); + REQUIRE(physics_server != nullptr); + + RID body_a; + RID body_b; + RID joint = make_configured_generic_6dof_joint(physics_server, body_a, body_b); + + constexpr Vector3::Axis axes[] = { Vector3::AXIS_X, Vector3::AXIS_Y, Vector3::AXIS_Z }; + constexpr real_t linear_values[] = { 12.0, 34.0, 56.0 }; + constexpr real_t angular_values[] = { 7.0, 8.0, 9.0 }; + + for (int i = 0; i < 3; i++) { + physics_server->generic_6dof_joint_set_param(joint, axes[i], PhysicsServer3D::G6DOF_JOINT_LINEAR_DRIVE_FORCE_LIMIT, linear_values[i]); + physics_server->generic_6dof_joint_set_param(joint, axes[i], PhysicsServer3D::G6DOF_JOINT_ANGULAR_DRIVE_TORQUE_LIMIT, angular_values[i]); + } + + for (int i = 0; i < 3; i++) { + CHECK_EQ(physics_server->generic_6dof_joint_get_param(joint, axes[i], PhysicsServer3D::G6DOF_JOINT_LINEAR_DRIVE_FORCE_LIMIT), doctest::Approx(linear_values[i])); + CHECK_EQ(physics_server->generic_6dof_joint_get_param(joint, axes[i], PhysicsServer3D::G6DOF_JOINT_ANGULAR_DRIVE_TORQUE_LIMIT), doctest::Approx(angular_values[i])); + } + + physics_server->free_rid(joint); + physics_server->free_rid(body_a); + physics_server->free_rid(body_b); +} + +TEST_CASE("[SceneTree][PhysicsServer3D][Generic6DOFJoint3D] Drive force and torque limits defaults are unlimited") { + PhysicsServer3D *physics_server = PhysicsServer3D::get_singleton(); + REQUIRE(physics_server != nullptr); + + RID body_a; + RID body_b; + RID joint = make_configured_generic_6dof_joint(physics_server, body_a, body_b); + + CHECK_EQ(physics_server->generic_6dof_joint_get_param(joint, Vector3::AXIS_X, PhysicsServer3D::G6DOF_JOINT_LINEAR_DRIVE_FORCE_LIMIT), FLT_MAX); + CHECK_EQ(physics_server->generic_6dof_joint_get_param(joint, Vector3::AXIS_Y, PhysicsServer3D::G6DOF_JOINT_LINEAR_DRIVE_FORCE_LIMIT), FLT_MAX); + CHECK_EQ(physics_server->generic_6dof_joint_get_param(joint, Vector3::AXIS_Z, PhysicsServer3D::G6DOF_JOINT_LINEAR_DRIVE_FORCE_LIMIT), FLT_MAX); + + CHECK_EQ(physics_server->generic_6dof_joint_get_param(joint, Vector3::AXIS_X, PhysicsServer3D::G6DOF_JOINT_ANGULAR_DRIVE_TORQUE_LIMIT), FLT_MAX); + CHECK_EQ(physics_server->generic_6dof_joint_get_param(joint, Vector3::AXIS_Y, PhysicsServer3D::G6DOF_JOINT_ANGULAR_DRIVE_TORQUE_LIMIT), FLT_MAX); + CHECK_EQ(physics_server->generic_6dof_joint_get_param(joint, Vector3::AXIS_Z, PhysicsServer3D::G6DOF_JOINT_ANGULAR_DRIVE_TORQUE_LIMIT), FLT_MAX); + + physics_server->free_rid(joint); + physics_server->free_rid(body_a); + physics_server->free_rid(body_b); +} + +TEST_CASE("[SceneTree][Generic6DOFJoint3D] Legacy motor and DRIVE limits are stored independently") { + Generic6DOFJoint3D *joint = memnew(Generic6DOFJoint3D); + + // Set legacy motor limit, then set DRIVE limit to a finite value. + ERR_PRINT_OFF; + joint->set_param_x(Generic6DOFJoint3D::PARAM_LINEAR_MOTOR_FORCE_LIMIT, 50.0); + ERR_PRINT_ON; + joint->set_param_x(Generic6DOFJoint3D::PARAM_LINEAR_DRIVE_FORCE_LIMIT, 80.0); + + CHECK_EQ(joint->get_param_x(Generic6DOFJoint3D::PARAM_LINEAR_MOTOR_FORCE_LIMIT), doctest::Approx(50.0)); + CHECK_EQ(joint->get_param_x(Generic6DOFJoint3D::PARAM_LINEAR_DRIVE_FORCE_LIMIT), doctest::Approx(80.0)); + + memdelete(joint); +} + +TEST_CASE("[SceneTree][PhysicsServer3D][Generic6DOFJoint3D] DRIVE FLT_MAX after legacy motor round-trips through storage") { + PhysicsServer3D *physics_server = PhysicsServer3D::get_singleton(); + REQUIRE(physics_server != nullptr); + + RID body_a; + RID body_b; + RID joint = make_configured_generic_6dof_joint(physics_server, body_a, body_b); + + // Legacy motor limit set first, then explicit DRIVE FLT_MAX + physics_server->generic_6dof_joint_set_param(joint, Vector3::AXIS_X, PhysicsServer3D::G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT, 50.0); + physics_server->generic_6dof_joint_set_param(joint, Vector3::AXIS_X, PhysicsServer3D::G6DOF_JOINT_ANGULAR_DRIVE_TORQUE_LIMIT, FLT_MAX); + + CHECK_EQ(physics_server->generic_6dof_joint_get_param(joint, Vector3::AXIS_X, PhysicsServer3D::G6DOF_JOINT_ANGULAR_DRIVE_TORQUE_LIMIT), FLT_MAX); + + physics_server->free_rid(joint); + physics_server->free_rid(body_a); + physics_server->free_rid(body_b); +} + +TEST_CASE("[SceneTree][Generic6DOFJoint3D] Default joint configuration does not emit MOTOR_FORCE_LIMIT deprecation warning") { + // Instancing a node should be silent + Generic6DOFJoint3D *joint = memnew(Generic6DOFJoint3D); + CHECK(joint != nullptr); + memdelete(joint); +} + } // namespace TestGeneric6DOFJoint3D #endif // PHYSICS_3D_DISABLED