Add quaternion rotation targets to Generic6DOFJoint3D:

- SixDOF target orientations supported for Jolt, trivial fallback to identity quaternion is used when explicit angular rotation is unconfigured or unsupported
- Conversion between BS and CS is handled inside, surface exposes BS

Implements godotengine/godot-proposals#3891
This commit is contained in:
nikolay-egorov
2026-04-27 10:34:26 +03:00
parent 6d6e822c68
commit e777acec15
19 changed files with 368 additions and 2 deletions
+26
View File
@@ -10,6 +10,18 @@
<tutorials>
</tutorials>
<methods>
<method name="clear_angular_target_rotation">
<return type="void" />
<description>
Clears the quaternion angular target. After clearing, the joint is driven by [constant PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT] on each axis as configured.
</description>
</method>
<method name="get_angular_target_rotation" qualifiers="const">
<return type="Quaternion" />
<description>
Returns the joint's current angular target as a body-space quaternion. If no explicit target was set via [method set_angular_target_rotation], the value is derived from [constant PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT] on backends that support it; other backends return the identity quaternion.
</description>
</method>
<method name="get_flag_x" qualifiers="const">
<return type="bool" />
<param index="0" name="flag" type="int" enum="Generic6DOFJoint3D.Flag" />
@@ -46,6 +58,20 @@
<description>
</description>
</method>
<method name="has_target_rotation" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if a quaternion angular target was explicitly set via [method set_angular_target_rotation] and has not been cleared. Returns [code]false[/code] when the joint is driven by [constant PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT] only.
</description>
</method>
<method name="set_angular_target_rotation">
<return type="void" />
<param index="0" name="target_rotation" type="Quaternion" />
<description>
Sets the target angular orientation as a body-space quaternion describing the desired orientation of body B relative to body A. Replaces any previously set target and supersedes [constant PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT] until [method clear_angular_target_rotation] is called.
[b]Note:[/b] Only for Jolt backend. Other 3D physics backends may ignore this value.
</description>
</method>
<method name="set_flag_x">
<return type="void" />
<param index="0" name="flag" type="int" enum="Generic6DOFJoint3D.Flag" />
+17
View File
@@ -759,6 +759,14 @@
Destroys any of the objects created by PhysicsServer3D. If the [RID] passed is not one of the objects that can be created by PhysicsServer3D, an error will be sent to the console.
</description>
</method>
<method name="generic_6dof_joint_get_angular_target_rotation" qualifiers="const">
<return type="Quaternion" />
<param index="0" name="joint" type="RID" />
<description>
Returns the target angular orientation of a generic 6DOF joint as a body-space quaternion describing the desired orientation of body B relative to body A.
[b]Note:[/b] Only for Jolt backend. Other 3D physics backends return the identity quaternion.
</description>
</method>
<method name="generic_6dof_joint_get_flag" qualifiers="const">
<return type="bool" />
<param index="0" name="joint" type="RID" />
@@ -777,6 +785,15 @@
Returns the value of a generic 6DOF joint parameter.
</description>
</method>
<method name="generic_6dof_joint_set_angular_target_rotation">
<return type="void" />
<param index="0" name="joint" type="RID" />
<param index="1" name="target_rotation" type="Quaternion" />
<description>
Sets the target angular orientation of a generic 6DOF joint as a body-space quaternion describing the desired orientation of body B relative to body A.
[b]Note:[/b] Only for Jolt backend. Other 3D physics backends may ignore this value.
</description>
</method>
<method name="generic_6dof_joint_set_flag">
<return type="void" />
<param index="0" name="joint" type="RID" />
+13
View File
@@ -680,6 +680,12 @@
<description>
</description>
</method>
<method name="_generic_6dof_joint_get_angular_target_rotation" qualifiers="virtual required const">
<return type="Quaternion" />
<param index="0" name="joint" type="RID" />
<description>
</description>
</method>
<method name="_generic_6dof_joint_get_flag" qualifiers="virtual required const">
<return type="bool" />
<param index="0" name="joint" type="RID" />
@@ -696,6 +702,13 @@
<description>
</description>
</method>
<method name="_generic_6dof_joint_set_angular_target_rotation" qualifiers="virtual required">
<return type="void" />
<param index="0" name="joint" type="RID" />
<param index="1" name="target_rotation" type="Quaternion" />
<description>
</description>
</method>
<method name="_generic_6dof_joint_set_flag" qualifiers="virtual required">
<return type="void" />
<param index="0" name="joint" type="RID" />
@@ -1596,6 +1596,25 @@ bool GodotPhysicsServer3D::generic_6dof_joint_get_flag(RID p_joint, Vector3::Axi
return generic_6dof_joint->get_flag(p_axis, p_flag);
}
void GodotPhysicsServer3D::generic_6dof_joint_set_angular_target_rotation(RID p_joint, const Quaternion &p_target_rotation) {
ERR_FAIL_COND_MSG(!p_target_rotation.is_finite() || p_target_rotation.length_squared() <= CMP_EPSILON, "Angular target rotation must be a finite, non-zero quaternion.");
GodotJoint3D *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_NULL(joint);
ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_6DOF);
WARN_PRINT_ONCE("Quaternion angular target rotations for Generic6DOFJoint3D are only supported by Jolt Physics. This value will be ignored by GodotPhysics3D.");
}
Quaternion GodotPhysicsServer3D::generic_6dof_joint_get_angular_target_rotation(RID p_joint) const {
const GodotJoint3D *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_NULL_V(joint, Quaternion());
ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_6DOF, Quaternion());
// GodotPhysics3D does not apply angular spring equilibrium points, so there is no body-space target to return.
WARN_PRINT_ONCE("Quaternion angular target rotations for Generic6DOFJoint3D are only supported by Jolt Physics. GodotPhysics3D returns the identity quaternion.");
return Quaternion();
}
void GodotPhysicsServer3D::free_rid(RID p_rid) {
_update_shapes(); //just in case
@@ -361,6 +361,9 @@ public:
virtual void generic_6dof_joint_set_flag(RID p_joint, Vector3::Axis, G6DOFJointAxisFlag p_flag, bool p_enable) override;
virtual bool generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis, G6DOFJointAxisFlag p_flag) const override;
virtual void generic_6dof_joint_set_angular_target_rotation(RID p_joint, const Quaternion &p_target_rotation) override;
virtual Quaternion generic_6dof_joint_get_angular_target_rotation(RID p_joint) const override;
virtual JointType joint_get_type(RID p_joint) const override;
virtual void joint_set_solver_priority(RID p_joint, int p_priority) override;
@@ -208,6 +208,20 @@ void JoltGeneric6DOFJoint3D::_update_spring_equilibrium(int p_axis) {
}
}
void JoltGeneric6DOFJoint3D::_update_angular_target_rotation() {
if (!has_angular_target_rotation) {
return;
}
JPH::SixDOFConstraint *constraint = static_cast<JPH::SixDOFConstraint *>(jolt_ref.GetPtr());
if (unlikely(constraint == nullptr)) {
return;
}
const JPH::Quat target_orientation = to_jolt(angular_target_rotation);
constraint->SetTargetOrientationBS(target_orientation);
}
void JoltGeneric6DOFJoint3D::_limits_changed() {
rebuild();
_wake_up_bodies();
@@ -435,6 +449,7 @@ void JoltGeneric6DOFJoint3D::set_param(Axis p_axis, Param p_param, double p_valu
} break;
case JoltPhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT: {
spring_equilibrium[axis_ang] = p_value;
has_angular_target_rotation = false;
_spring_equilibrium_changed(axis_ang);
} break;
default: {
@@ -507,6 +522,36 @@ void JoltGeneric6DOFJoint3D::set_flag(Axis p_axis, Flag p_flag, bool p_enabled)
}
}
void JoltGeneric6DOFJoint3D::set_angular_target_rotation(const Quaternion &p_target_rotation) {
ERR_FAIL_COND_MSG(!p_target_rotation.is_finite() || p_target_rotation.length_squared() <= CMP_EPSILON, "Angular target rotation must be a finite, non-zero quaternion.");
angular_target_rotation = p_target_rotation.normalized();
has_angular_target_rotation = true;
_update_angular_target_rotation();
_wake_up_bodies();
}
Quaternion JoltGeneric6DOFJoint3D::get_angular_target_rotation() const {
if (has_angular_target_rotation) {
return angular_target_rotation;
}
const JPH::SixDOFConstraint *constraint = static_cast<const JPH::SixDOFConstraint *>(jolt_ref.GetPtr());
if (unlikely(constraint == nullptr)) {
return Quaternion();
}
const Basis target_orientation_cs = Basis::from_euler(
Vector3(static_cast<real_t>(-spring_equilibrium[AXIS_ANGULAR_X]),
static_cast<real_t>(-spring_equilibrium[AXIS_ANGULAR_Y]),
static_cast<real_t>(-spring_equilibrium[AXIS_ANGULAR_Z])),
EulerOrder::ZYX);
const Basis constraint_to_body_1 = to_godot(constraint->GetConstraintToBody1Matrix()).basis;
const Basis constraint_to_body_2 = to_godot(constraint->GetConstraintToBody2Matrix()).basis;
return (constraint_to_body_1 * target_orientation_cs * constraint_to_body_2.inverse()).get_quaternion();
}
double JoltGeneric6DOFJoint3D::get_jolt_param(Axis p_axis, JoltParam p_param) const {
const int axis_lin = AXES_LINEAR + (int)p_axis;
const int axis_ang = AXES_ANGULAR + (int)p_axis;
@@ -683,4 +728,6 @@ void JoltGeneric6DOFJoint3D::rebuild() {
_update_spring_parameters(axis);
_update_spring_equilibrium(axis);
}
_update_angular_target_rotation();
}
@@ -72,6 +72,9 @@ class JoltGeneric6DOFJoint3D final : public JoltJoint3D {
double spring_equilibrium[AXIS_COUNT] = {};
double spring_limit[AXIS_COUNT] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
Quaternion angular_target_rotation;
bool has_angular_target_rotation = false;
bool limit_enabled[AXIS_COUNT] = {};
bool limit_spring_enabled[AXIS_COUNT] = {};
@@ -89,6 +92,7 @@ class JoltGeneric6DOFJoint3D final : public JoltJoint3D {
void _update_motor_limit(int p_axis);
void _update_spring_parameters(int p_axis);
void _update_spring_equilibrium(int p_axis);
void _update_angular_target_rotation();
void _limits_changed();
void _limit_spring_parameters_changed(int p_axis);
@@ -111,6 +115,9 @@ public:
bool get_flag(Axis p_axis, Flag p_flag) const;
void set_flag(Axis p_axis, Flag p_flag, bool p_enabled);
void set_angular_target_rotation(const Quaternion &p_target_rotation);
Quaternion get_angular_target_rotation() const;
double get_jolt_param(Axis p_axis, JoltParam p_param) const;
void set_jolt_param(Axis p_axis, JoltParam p_param, double p_value);
@@ -1546,6 +1546,26 @@ bool JoltPhysicsServer3D::generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis
return g6dof_joint->get_flag(p_axis, p_flag);
}
void JoltPhysicsServer3D::generic_6dof_joint_set_angular_target_rotation(RID p_joint, const Quaternion &p_target_rotation) {
JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_NULL(joint);
ERR_FAIL_COND(joint->get_type() != JOINT_TYPE_6DOF);
JoltGeneric6DOFJoint3D *g6dof_joint = static_cast<JoltGeneric6DOFJoint3D *>(joint);
g6dof_joint->set_angular_target_rotation(p_target_rotation);
}
Quaternion JoltPhysicsServer3D::generic_6dof_joint_get_angular_target_rotation(RID p_joint) const {
const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_NULL_V(joint, Quaternion());
ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_6DOF, Quaternion());
const JoltGeneric6DOFJoint3D *g6dof_joint = static_cast<const JoltGeneric6DOFJoint3D *>(joint);
return g6dof_joint->get_angular_target_rotation();
}
PhysicsServer3D::JointType JoltPhysicsServer3D::joint_get_type(RID p_joint) const {
const JoltJoint3D *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_NULL_V(joint, JOINT_TYPE_PIN);
@@ -405,6 +405,9 @@ public:
virtual void generic_6dof_joint_set_flag(RID p_joint, Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisFlag p_flag, bool p_enable) override;
virtual bool generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisFlag p_flag) const override;
virtual void generic_6dof_joint_set_angular_target_rotation(RID p_joint, const Quaternion &p_target_rotation) override;
virtual Quaternion generic_6dof_joint_get_angular_target_rotation(RID p_joint) const override;
virtual PhysicsServer3D::JointType joint_get_type(RID p_joint) const override;
virtual void joint_set_solver_priority(RID p_joint, int p_priority) override;
@@ -95,9 +95,12 @@ _FORCE_INLINE_ JPH::Vec3 to_jolt(const Vector3 &p_vec) {
return JPH::Vec3((float)p_vec.x, (float)p_vec.y, (float)p_vec.z);
}
_FORCE_INLINE_ JPH::Quat to_jolt(const Quaternion &p_quat) {
return JPH::Quat((float)p_quat.x, (float)p_quat.y, (float)p_quat.z, (float)p_quat.w);
}
_FORCE_INLINE_ JPH::Quat to_jolt(const Basis &p_basis) {
const Quaternion quat = p_basis.get_quaternion().normalized();
return JPH::Quat((float)quat.x, (float)quat.y, (float)quat.z, (float)quat.w);
return to_jolt(p_basis.get_quaternion().normalized());
}
_FORCE_INLINE_ JPH::Mat44 to_jolt(const Transform3D &p_transform) {
@@ -51,6 +51,11 @@ void Generic6DOFJoint3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_flag_z", "flag", "value"), &Generic6DOFJoint3D::set_flag_z);
ClassDB::bind_method(D_METHOD("get_flag_z", "flag"), &Generic6DOFJoint3D::get_flag_z);
ClassDB::bind_method(D_METHOD("set_angular_target_rotation", "target_rotation"), &Generic6DOFJoint3D::set_angular_target_rotation);
ClassDB::bind_method(D_METHOD("get_angular_target_rotation"), &Generic6DOFJoint3D::get_angular_target_rotation);
ClassDB::bind_method(D_METHOD("has_target_rotation"), &Generic6DOFJoint3D::has_target_rotation);
ClassDB::bind_method(D_METHOD("clear_angular_target_rotation"), &Generic6DOFJoint3D::clear_angular_target_rotation);
ADD_GROUP("Linear Limit", "linear_limit_");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_LIMIT);
@@ -201,6 +206,9 @@ void Generic6DOFJoint3D::_bind_methods() {
void Generic6DOFJoint3D::set_param_x(Param p_param, real_t p_value) {
ERR_FAIL_INDEX(p_param, PARAM_MAX);
params_x[p_param] = p_value;
if (p_param == PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT) {
has_angular_target_rotation = false;
}
if (is_configured()) {
PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_rid(), Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value);
}
@@ -216,6 +224,9 @@ real_t Generic6DOFJoint3D::get_param_x(Param p_param) const {
void Generic6DOFJoint3D::set_param_y(Param p_param, real_t p_value) {
ERR_FAIL_INDEX(p_param, PARAM_MAX);
params_y[p_param] = p_value;
if (p_param == PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT) {
has_angular_target_rotation = false;
}
if (is_configured()) {
PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_rid(), Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value);
}
@@ -230,6 +241,9 @@ real_t Generic6DOFJoint3D::get_param_y(Param p_param) const {
void Generic6DOFJoint3D::set_param_z(Param p_param, real_t p_value) {
ERR_FAIL_INDEX(p_param, PARAM_MAX);
params_z[p_param] = p_value;
if (p_param == PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT) {
has_angular_target_rotation = false;
}
if (is_configured()) {
PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_rid(), Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value);
}
@@ -283,6 +297,58 @@ bool Generic6DOFJoint3D::get_flag_z(Flag p_flag) const {
return flags_z[p_flag];
}
bool Generic6DOFJoint3D::_is_valid_angular_target_rotation(const Quaternion &p_target_rotation) {
return p_target_rotation.is_finite() && p_target_rotation.length_squared() > CMP_EPSILON;
}
void Generic6DOFJoint3D::set_angular_target_rotation(const Quaternion &p_target_rotation) {
ERR_FAIL_COND_MSG(!_is_valid_angular_target_rotation(p_target_rotation), "Angular target rotation must be a finite, non-zero quaternion.");
angular_target_rotation = p_target_rotation.normalized();
has_angular_target_rotation = true;
if (!is_configured()) {
return;
}
PhysicsServer3D::get_singleton()->generic_6dof_joint_set_angular_target_rotation(get_rid(), angular_target_rotation);
}
Quaternion Generic6DOFJoint3D::get_angular_target_rotation() const {
if (has_angular_target_rotation) {
return angular_target_rotation;
}
if (is_configured()) {
return PhysicsServer3D::get_singleton()->generic_6dof_joint_get_angular_target_rotation(get_rid());
}
// Equilibrium point is in constraint space; body-space conversion requires the constraint frame.
ERR_PRINT("Cannot derive a body-space angular target rotation from Generic6DOFJoint3D equilibrium points before the joint is configured. Returning the identity quaternion.");
return Quaternion();
}
bool Generic6DOFJoint3D::has_target_rotation() const {
return has_angular_target_rotation;
}
void Generic6DOFJoint3D::clear_angular_target_rotation() {
if (!has_angular_target_rotation) {
return;
}
has_angular_target_rotation = false;
angular_target_rotation = Quaternion();
if (!is_configured()) {
return;
}
PhysicsServer3D *server = PhysicsServer3D::get_singleton();
server->generic_6dof_joint_set_param(get_rid(), Vector3::AXIS_X, PhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT, params_x[PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT]);
server->generic_6dof_joint_set_param(get_rid(), Vector3::AXIS_Y, PhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT, params_y[PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT]);
server->generic_6dof_joint_set_param(get_rid(), Vector3::AXIS_Z, PhysicsServer3D::G6DOF_JOINT_ANGULAR_SPRING_EQUILIBRIUM_POINT, params_z[PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT]);
}
void Generic6DOFJoint3D::_configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) {
Transform3D gt = get_global_transform();
//Vector3 cone_twistpos = gt.origin;
@@ -312,6 +378,10 @@ void Generic6DOFJoint3D::_configure_joint(RID p_joint, PhysicsBody3D *body_a, Ph
PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(p_joint, Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisFlag(i), flags_y[i]);
PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(p_joint, Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisFlag(i), flags_z[i]);
}
if (has_angular_target_rotation) {
PhysicsServer3D::get_singleton()->generic_6dof_joint_set_angular_target_rotation(p_joint, angular_target_rotation);
}
}
Generic6DOFJoint3D::Generic6DOFJoint3D() {
@@ -79,10 +79,14 @@ protected:
bool flags_y[FLAG_MAX];
real_t params_z[PARAM_MAX];
bool flags_z[FLAG_MAX];
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 bool _is_valid_angular_target_rotation(const Quaternion &p_target_rotation);
public:
void set_param_x(Param p_param, real_t p_value);
real_t get_param_x(Param p_param) const;
@@ -102,6 +106,11 @@ public:
void set_flag_z(Flag p_flag, bool p_enabled);
bool get_flag_z(Flag p_flag) const;
void set_angular_target_rotation(const Quaternion &p_target_rotation);
Quaternion get_angular_target_rotation() const;
bool has_target_rotation() const;
void clear_angular_target_rotation();
Generic6DOFJoint3D();
};
+3
View File
@@ -1044,6 +1044,9 @@ void PhysicsServer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("generic_6dof_joint_set_flag", "joint", "axis", "flag", "enable"), &PhysicsServer3D::generic_6dof_joint_set_flag);
ClassDB::bind_method(D_METHOD("generic_6dof_joint_get_flag", "joint", "axis", "flag"), &PhysicsServer3D::generic_6dof_joint_get_flag);
ClassDB::bind_method(D_METHOD("generic_6dof_joint_set_angular_target_rotation", "joint", "target_rotation"), &PhysicsServer3D::generic_6dof_joint_set_angular_target_rotation);
ClassDB::bind_method(D_METHOD("generic_6dof_joint_get_angular_target_rotation", "joint"), &PhysicsServer3D::generic_6dof_joint_get_angular_target_rotation);
ClassDB::bind_method(D_METHOD("free_rid", "rid"), &PhysicsServer3D::free_rid);
ClassDB::bind_method(D_METHOD("set_active", "active"), &PhysicsServer3D::set_active);
+3
View File
@@ -794,6 +794,9 @@ public:
virtual void generic_6dof_joint_set_flag(RID p_joint, Vector3::Axis, G6DOFJointAxisFlag p_flag, bool p_enable) = 0;
virtual bool generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis, G6DOFJointAxisFlag p_flag) const = 0;
virtual void generic_6dof_joint_set_angular_target_rotation(RID p_joint, const Quaternion &p_target_rotation) = 0;
virtual Quaternion generic_6dof_joint_get_angular_target_rotation(RID p_joint) const = 0;
/* QUERY API */
enum AreaBodyStatus {
@@ -423,6 +423,9 @@ public:
virtual void generic_6dof_joint_set_flag(RID p_joint, Vector3::Axis, G6DOFJointAxisFlag p_flag, bool p_enable) override {}
virtual bool generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis, G6DOFJointAxisFlag p_flag) const override { return false; }
virtual void generic_6dof_joint_set_angular_target_rotation(RID p_joint, const Quaternion &p_target_rotation) override {}
virtual Quaternion generic_6dof_joint_get_angular_target_rotation(RID p_joint) const override { return Quaternion(); }
/* MISC */
virtual void free_rid(RID p_rid) override {}
@@ -417,6 +417,9 @@ void PhysicsServer3DExtension::_bind_methods() {
GDVIRTUAL_BIND(_generic_6dof_joint_set_flag, "joint", "axis", "flag", "enable");
GDVIRTUAL_BIND(_generic_6dof_joint_get_flag, "joint", "axis", "flag");
GDVIRTUAL_BIND(_generic_6dof_joint_set_angular_target_rotation, "joint", "target_rotation");
GDVIRTUAL_BIND(_generic_6dof_joint_get_angular_target_rotation, "joint");
GDVIRTUAL_BIND(_joint_get_type, "joint");
GDVIRTUAL_BIND(_joint_set_solver_priority, "joint", "priority");
@@ -522,6 +522,9 @@ public:
EXBIND4(generic_6dof_joint_set_flag, RID, Vector3::Axis, G6DOFJointAxisFlag, bool)
EXBIND3RC(bool, generic_6dof_joint_get_flag, RID, Vector3::Axis, G6DOFJointAxisFlag)
EXBIND2(generic_6dof_joint_set_angular_target_rotation, RID, const Quaternion &)
EXBIND1RC(Quaternion, generic_6dof_joint_get_angular_target_rotation, RID)
EXBIND1RC(JointType, joint_get_type, RID)
EXBIND2(joint_set_solver_priority, RID, int)
@@ -383,6 +383,9 @@ public:
FUNC4(generic_6dof_joint_set_flag, RID, Vector3::Axis, G6DOFJointAxisFlag, bool)
FUNC3RC(bool, generic_6dof_joint_get_flag, RID, Vector3::Axis, G6DOFJointAxisFlag)
FUNC2(generic_6dof_joint_set_angular_target_rotation, RID, const Quaternion &)
FUNC1RC(Quaternion, generic_6dof_joint_get_angular_target_rotation, RID)
FUNC1RC(JointType, joint_get_type, RID);
FUNC2(joint_set_solver_priority, RID, int);
+111
View File
@@ -0,0 +1,111 @@
/**************************************************************************/
/* test_generic_6dof_joint_3d.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. */
/**************************************************************************/
#include "tests/test_macros.h"
TEST_FORCE_LINK(test_generic_6dof_joint_3d)
#ifndef PHYSICS_3D_DISABLED
#include "scene/3d/physics/joints/generic_6dof_joint_3d.h"
namespace TestGeneric6DOFJoint3D {
TEST_CASE("[SceneTree][Generic6DOFJoint3D] Quaternion angular target is normalized and stored") {
Generic6DOFJoint3D *joint = memnew(Generic6DOFJoint3D);
const Quaternion target_rotation(Vector3(0, 1, 0), 0.5);
joint->set_angular_target_rotation(target_rotation * 2.0);
CHECK(joint->get_angular_target_rotation().is_normalized());
CHECK(joint->get_angular_target_rotation().is_equal_approx(target_rotation));
CHECK(joint->has_target_rotation());
memdelete(joint);
}
TEST_CASE("[SceneTree][Generic6DOFJoint3D] Euler equilibrium setter clears stored quaternion target") {
Generic6DOFJoint3D *joint = memnew(Generic6DOFJoint3D);
joint->set_angular_target_rotation(Quaternion(Vector3(0, 0, 1), 0.25));
CHECK(joint->has_target_rotation());
joint->set_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT, 0.1);
CHECK_FALSE(joint->has_target_rotation());
memdelete(joint);
}
TEST_CASE("[SceneTree][Generic6DOFJoint3D] clear_angular_target_rotation resets state") {
Generic6DOFJoint3D *joint = memnew(Generic6DOFJoint3D);
joint->set_angular_target_rotation(Quaternion(Vector3(1, 0, 0), 0.5));
CHECK(joint->has_target_rotation());
joint->clear_angular_target_rotation();
CHECK_FALSE(joint->has_target_rotation());
ERR_PRINT_OFF;
CHECK(joint->get_angular_target_rotation().is_equal_approx(Quaternion()));
ERR_PRINT_ON;
memdelete(joint);
}
TEST_CASE("[SceneTree][Generic6DOFJoint3D] Getter returns identity before configuration when no explicit target is set") {
Generic6DOFJoint3D *joint = memnew(Generic6DOFJoint3D);
joint->set_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT, 0.3);
joint->set_param_y(Generic6DOFJoint3D::PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT, -0.2);
joint->set_param_z(Generic6DOFJoint3D::PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT, 0.4);
CHECK_FALSE(joint->has_target_rotation());
ERR_PRINT_OFF;
CHECK(joint->get_angular_target_rotation().is_equal_approx(Quaternion()));
ERR_PRINT_ON;
memdelete(joint);
}
TEST_CASE("[SceneTree][Generic6DOFJoint3D] Explicit quaternion target shadows Euler equilibrium derivation") {
Generic6DOFJoint3D *joint = memnew(Generic6DOFJoint3D);
joint->set_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT, 0.3);
const Quaternion explicit_target(Vector3(0, 1, 0), 0.7);
joint->set_angular_target_rotation(explicit_target);
CHECK(joint->has_target_rotation());
CHECK(joint->get_angular_target_rotation().is_equal_approx(explicit_target));
memdelete(joint);
}
} // namespace TestGeneric6DOFJoint3D
#endif // PHYSICS_3D_DISABLED