Merge pull request #65223 from jtnicholl/is_zero_approx

This commit is contained in:
Rémi Verschelde 2022-09-02 08:43:16 +02:00
commit 42398b5f17
26 changed files with 70 additions and 31 deletions

View File

@ -1033,13 +1033,13 @@ void Basis::rotate_sh(real_t *p_values) {
Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up) { Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up) {
#ifdef MATH_CHECKS #ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(p_target.is_equal_approx(Vector3()), Basis(), "The target vector can't be zero."); ERR_FAIL_COND_V_MSG(p_target.is_zero_approx(), Basis(), "The target vector can't be zero.");
ERR_FAIL_COND_V_MSG(p_up.is_equal_approx(Vector3()), Basis(), "The up vector can't be zero."); ERR_FAIL_COND_V_MSG(p_up.is_zero_approx(), Basis(), "The up vector can't be zero.");
#endif #endif
Vector3 v_z = -p_target.normalized(); Vector3 v_z = -p_target.normalized();
Vector3 v_x = p_up.cross(v_z); Vector3 v_x = p_up.cross(v_z);
#ifdef MATH_CHECKS #ifdef MATH_CHECKS
ERR_FAIL_COND_V_MSG(v_x.is_equal_approx(Vector3()), Basis(), "The target vector and up vector can't be parallel to each other."); ERR_FAIL_COND_V_MSG(v_x.is_zero_approx(), Basis(), "The target vector and up vector can't be parallel to each other.");
#endif #endif
v_x.normalize(); v_x.normalize();
Vector3 v_y = v_z.cross(v_x); Vector3 v_y = v_z.cross(v_x);

View File

@ -182,6 +182,10 @@ bool Vector2::is_equal_approx(const Vector2 &p_v) const {
return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y); return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y);
} }
bool Vector2::is_zero_approx() const {
return Math::is_zero_approx(x) && Math::is_zero_approx(y);
}
Vector2::operator String() const { Vector2::operator String() const {
return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")"; return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")";
} }

View File

@ -124,6 +124,7 @@ struct _NO_DISCARD_ Vector2 {
Vector2 reflect(const Vector2 &p_normal) const; Vector2 reflect(const Vector2 &p_normal) const;
bool is_equal_approx(const Vector2 &p_v) const; bool is_equal_approx(const Vector2 &p_v) const;
bool is_zero_approx() const;
Vector2 operator+(const Vector2 &p_v) const; Vector2 operator+(const Vector2 &p_v) const;
void operator+=(const Vector2 &p_v); void operator+=(const Vector2 &p_v);

View File

@ -145,6 +145,10 @@ bool Vector3::is_equal_approx(const Vector3 &p_v) const {
return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y) && Math::is_equal_approx(z, p_v.z); return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y) && Math::is_equal_approx(z, p_v.z);
} }
bool Vector3::is_zero_approx() const {
return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z);
}
Vector3::operator String() const { Vector3::operator String() const {
return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ")"; return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ")";
} }

View File

@ -142,6 +142,7 @@ struct _NO_DISCARD_ Vector3 {
_FORCE_INLINE_ Vector3 reflect(const Vector3 &p_normal) const; _FORCE_INLINE_ Vector3 reflect(const Vector3 &p_normal) const;
bool is_equal_approx(const Vector3 &p_v) const; bool is_equal_approx(const Vector3 &p_v) const;
bool is_zero_approx() const;
/* Operators */ /* Operators */

View File

@ -71,6 +71,10 @@ bool Vector4::is_equal_approx(const Vector4 &p_vec4) const {
return Math::is_equal_approx(x, p_vec4.x) && Math::is_equal_approx(y, p_vec4.y) && Math::is_equal_approx(z, p_vec4.z) && Math::is_equal_approx(w, p_vec4.w); return Math::is_equal_approx(x, p_vec4.x) && Math::is_equal_approx(y, p_vec4.y) && Math::is_equal_approx(z, p_vec4.z) && Math::is_equal_approx(w, p_vec4.w);
} }
bool Vector4::is_zero_approx() const {
return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z) && Math::is_zero_approx(w);
}
real_t Vector4::length() const { real_t Vector4::length() const {
return Math::sqrt(length_squared()); return Math::sqrt(length_squared());
} }

View File

@ -73,6 +73,7 @@ struct _NO_DISCARD_ Vector4 {
_FORCE_INLINE_ real_t length_squared() const; _FORCE_INLINE_ real_t length_squared() const;
bool is_equal_approx(const Vector4 &p_vec4) const; bool is_equal_approx(const Vector4 &p_vec4) const;
bool is_zero_approx() const;
real_t length() const; real_t length() const;
void normalize(); void normalize();
Vector4 normalized() const; Vector4 normalized() const;

View File

@ -1605,6 +1605,7 @@ static void _register_variant_builtin_methods() {
bind_method(Vector2, normalized, sarray(), varray()); bind_method(Vector2, normalized, sarray(), varray());
bind_method(Vector2, is_normalized, sarray(), varray()); bind_method(Vector2, is_normalized, sarray(), varray());
bind_method(Vector2, is_equal_approx, sarray("to"), varray()); bind_method(Vector2, is_equal_approx, sarray("to"), varray());
bind_method(Vector2, is_zero_approx, sarray(), varray());
bind_method(Vector2, posmod, sarray("mod"), varray()); bind_method(Vector2, posmod, sarray("mod"), varray());
bind_method(Vector2, posmodv, sarray("modv"), varray()); bind_method(Vector2, posmodv, sarray("modv"), varray());
bind_method(Vector2, project, sarray("b"), varray()); bind_method(Vector2, project, sarray("b"), varray());
@ -1693,6 +1694,7 @@ static void _register_variant_builtin_methods() {
bind_method(Vector3, normalized, sarray(), varray()); bind_method(Vector3, normalized, sarray(), varray());
bind_method(Vector3, is_normalized, sarray(), varray()); bind_method(Vector3, is_normalized, sarray(), varray());
bind_method(Vector3, is_equal_approx, sarray("to"), varray()); bind_method(Vector3, is_equal_approx, sarray("to"), varray());
bind_method(Vector3, is_zero_approx, sarray(), varray());
bind_method(Vector3, inverse, sarray(), varray()); bind_method(Vector3, inverse, sarray(), varray());
bind_method(Vector3, clamp, sarray("min", "max"), varray()); bind_method(Vector3, clamp, sarray("min", "max"), varray());
bind_method(Vector3, snapped, sarray("step"), varray()); bind_method(Vector3, snapped, sarray("step"), varray());
@ -1756,6 +1758,7 @@ static void _register_variant_builtin_methods() {
bind_method(Vector4, dot, sarray("with"), varray()); bind_method(Vector4, dot, sarray("with"), varray());
bind_method(Vector4, inverse, sarray(), varray()); bind_method(Vector4, inverse, sarray(), varray());
bind_method(Vector4, is_equal_approx, sarray("with"), varray()); bind_method(Vector4, is_equal_approx, sarray("with"), varray());
bind_method(Vector4, is_zero_approx, sarray(), varray());
/* Vector4i */ /* Vector4i */

View File

@ -212,6 +212,13 @@
Returns [code]true[/code] if the vector is normalized, [code]false[/code] otherwise. Returns [code]true[/code] if the vector is normalized, [code]false[/code] otherwise.
</description> </description>
</method> </method>
<method name="is_zero_approx" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this vector's values are approximately zero, by running [method @GlobalScope.is_zero_approx] on each component.
This method is faster than using [method is_equal_approx] with one value as a zero vector.
</description>
</method>
<method name="length" qualifiers="const"> <method name="length" qualifiers="const">
<return type="float" /> <return type="float" />
<description> <description>

View File

@ -180,6 +180,13 @@
Returns [code]true[/code] if the vector is normalized, [code]false[/code] otherwise. Returns [code]true[/code] if the vector is normalized, [code]false[/code] otherwise.
</description> </description>
</method> </method>
<method name="is_zero_approx" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this vector's values are approximately zero, by running [method @GlobalScope.is_zero_approx] on each component.
This method is faster than using [method is_equal_approx] with one value as a zero vector.
</description>
</method>
<method name="length" qualifiers="const"> <method name="length" qualifiers="const">
<return type="float" /> <return type="float" />
<description> <description>

View File

@ -141,6 +141,13 @@
Returns [code]true[/code] if the vector is normalized, i.e. its length is equal to 1. Returns [code]true[/code] if the vector is normalized, i.e. its length is equal to 1.
</description> </description>
</method> </method>
<method name="is_zero_approx" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if this vector's values are approximately zero, by running [method @GlobalScope.is_zero_approx] on each component.
This method is faster than using [method is_equal_approx] with one value as a zero vector.
</description>
</method>
<method name="length" qualifiers="const"> <method name="length" qualifiers="const">
<return type="float" /> <return type="float" />
<description> <description>

View File

@ -681,7 +681,7 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
} }
if (!found) { if (!found) {
for (int i = 0; i < search_path.size(); i++) { for (int i = 0; i < search_path.size(); i++) {
if (Vector3(0, 0, 0).is_equal_approx(skeleton->get_bone_global_rest(search_path[i]).origin)) { if (skeleton->get_bone_global_rest(search_path[i]).origin.is_zero_approx()) {
bone_idx = search_path[i]; // The bone existing at the origin is appropriate as a root. bone_idx = search_path[i]; // The bone existing at the origin is appropriate as a root.
found = true; found = true;
break; break;

View File

@ -433,7 +433,7 @@ PhysicalBone3D *Skeleton3DEditor::create_physical_bone(int bone_id, int bone_chi
/// Get an up vector not collinear with child rest origin /// Get an up vector not collinear with child rest origin
Vector3 up = Vector3(0, 1, 0); Vector3 up = Vector3(0, 1, 0);
if (up.cross(child_rest.origin).is_equal_approx(Vector3())) { if (up.cross(child_rest.origin).is_zero_approx()) {
up = Vector3(0, 0, 1); up = Vector3(0, 0, 1);
} }

View File

@ -139,7 +139,7 @@ void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos) {
// Center of panel. // Center of panel.
panning = panning * zoom / previous_zoom; panning = panning * zoom / previous_zoom;
} }
button_center_view->set_disabled(panning.is_equal_approx(Vector2())); button_center_view->set_disabled(panning.is_zero_approx());
previous_zoom = zoom; previous_zoom = zoom;

View File

@ -448,7 +448,7 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event)
} else if (drag_type == DRAG_TYPE_PAN) { } else if (drag_type == DRAG_TYPE_PAN) {
panning += mm->get_position() - drag_last_pos; panning += mm->get_position() - drag_last_pos;
drag_last_pos = mm->get_position(); drag_last_pos = mm->get_position();
button_center_view->set_disabled(panning.is_equal_approx(Vector2())); button_center_view->set_disabled(panning.is_zero_approx());
} else { } else {
// Update hovered point. // Update hovered point.
_grab_polygon_point(mm->get_position(), xform, hovered_polygon_index, hovered_point_index); _grab_polygon_point(mm->get_position(), xform, hovered_polygon_index, hovered_point_index);

View File

@ -435,7 +435,7 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> state) {
node["scale"] = _vec3_to_arr(n->scale); node["scale"] = _vec3_to_arr(n->scale);
} }
if (!n->position.is_equal_approx(Vector3())) { if (!n->position.is_zero_approx()) {
node["translation"] = _vec3_to_arr(n->position); node["translation"] = _vec3_to_arr(n->position);
} }
if (n->children.size()) { if (n->children.size()) {

View File

@ -1142,7 +1142,7 @@ bool CharacterBody2D::move_and_slide() {
on_ceiling = false; on_ceiling = false;
on_wall = false; on_wall = false;
if (!current_platform_velocity.is_equal_approx(Vector2())) { if (!current_platform_velocity.is_zero_approx()) {
PhysicsServer2D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin); PhysicsServer2D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin);
parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.recovery_as_collision = true; // Also report collisions generated only from recovery.
parameters.exclude_bodies.insert(platform_rid); parameters.exclude_bodies.insert(platform_rid);
@ -1241,7 +1241,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
break; break;
} }
if (result.remainder.is_equal_approx(Vector2())) { if (result.remainder.is_zero_approx()) {
motion = Vector2(); motion = Vector2();
break; break;
} }
@ -1325,7 +1325,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
sliding_enabled = true; sliding_enabled = true;
first_slide = false; first_slide = false;
if (!collided || motion.is_equal_approx(Vector2())) { if (!collided || motion.is_zero_approx()) {
break; break;
} }
} }
@ -1371,7 +1371,7 @@ void CharacterBody2D::_move_and_slide_floating(double p_delta) {
motion_results.push_back(result); motion_results.push_back(result);
_set_collision_direction(result); _set_collision_direction(result);
if (result.remainder.is_equal_approx(Vector2())) { if (result.remainder.is_zero_approx()) {
motion = Vector2(); motion = Vector2();
break; break;
} }
@ -1390,7 +1390,7 @@ void CharacterBody2D::_move_and_slide_floating(double p_delta) {
} }
} }
if (!collided || motion.is_equal_approx(Vector2())) { if (!collided || motion.is_zero_approx()) {
break; break;
} }

View File

@ -792,8 +792,8 @@ void Node3D::look_at(const Vector3 &p_target, const Vector3 &p_up) {
void Node3D::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up) { void Node3D::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up) {
ERR_FAIL_COND_MSG(p_pos.is_equal_approx(p_target), "Node origin and target are in the same position, look_at() failed."); ERR_FAIL_COND_MSG(p_pos.is_equal_approx(p_target), "Node origin and target are in the same position, look_at() failed.");
ERR_FAIL_COND_MSG(p_up.is_equal_approx(Vector3()), "The up vector can't be zero, look_at() failed."); ERR_FAIL_COND_MSG(p_up.is_zero_approx(), "The up vector can't be zero, look_at() failed.");
ERR_FAIL_COND_MSG(p_up.cross(p_target - p_pos).is_equal_approx(Vector3()), "Up vector and direction between node origin and target are aligned, look_at() failed."); ERR_FAIL_COND_MSG(p_up.cross(p_target - p_pos).is_zero_approx(), "Up vector and direction between node origin and target are aligned, look_at() failed.");
Transform3D lookat = Transform3D(Basis::looking_at(p_target - p_pos, p_up), p_pos); Transform3D lookat = Transform3D(Basis::looking_at(p_target - p_pos, p_up), p_pos);
Vector3 original_scale = get_scale(); Vector3 original_scale = get_scale();

View File

@ -1208,7 +1208,7 @@ bool CharacterBody3D::move_and_slide() {
last_motion = Vector3(); last_motion = Vector3();
if (!current_platform_velocity.is_equal_approx(Vector3())) { if (!current_platform_velocity.is_zero_approx()) {
PhysicsServer3D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin); PhysicsServer3D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin);
parameters.recovery_as_collision = true; // Also report collisions generated only from recovery. parameters.recovery_as_collision = true; // Also report collisions generated only from recovery.
@ -1315,7 +1315,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
break; break;
} }
if (result.remainder.is_equal_approx(Vector3())) { if (result.remainder.is_zero_approx()) {
motion = Vector3(); motion = Vector3();
break; break;
} }
@ -1428,7 +1428,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
const PhysicsServer3D::MotionCollision &collision = result.collisions[0]; const PhysicsServer3D::MotionCollision &collision = result.collisions[0];
Vector3 slide_motion = result.remainder.slide(collision.normal); Vector3 slide_motion = result.remainder.slide(collision.normal);
if (collision_state.floor && !collision_state.wall && !motion_slide_up.is_equal_approx(Vector3())) { if (collision_state.floor && !collision_state.wall && !motion_slide_up.is_zero_approx()) {
// Slide using the intersection between the motion plane and the floor plane, // Slide using the intersection between the motion plane and the floor plane,
// in order to keep the direction intact. // in order to keep the direction intact.
real_t motion_length = slide_motion.length(); real_t motion_length = slide_motion.length();
@ -1469,7 +1469,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
total_travel += result.travel; total_travel += result.travel;
// Apply Constant Speed. // Apply Constant Speed.
if (p_was_on_floor && floor_constant_speed && can_apply_constant_speed && collision_state.floor && !motion.is_equal_approx(Vector3())) { if (p_was_on_floor && floor_constant_speed && can_apply_constant_speed && collision_state.floor && !motion.is_zero_approx()) {
Vector3 travel_slide_up = total_travel.slide(up_direction); Vector3 travel_slide_up = total_travel.slide(up_direction);
motion = motion.normalized() * MAX(0, (motion_slide_up.length() - travel_slide_up.length())); motion = motion.normalized() * MAX(0, (motion_slide_up.length() - travel_slide_up.length()));
} }
@ -1492,7 +1492,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
collided = true; collided = true;
} }
if (!collided || motion.is_equal_approx(Vector3())) { if (!collided || motion.is_zero_approx()) {
break; break;
} }
@ -1533,7 +1533,7 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) {
CollisionState result_state; CollisionState result_state;
_set_collision_direction(result, result_state); _set_collision_direction(result, result_state);
if (result.remainder.is_equal_approx(Vector3())) { if (result.remainder.is_zero_approx()) {
motion = Vector3(); motion = Vector3();
break; break;
} }
@ -1557,7 +1557,7 @@ void CharacterBody3D::_move_and_slide_floating(double p_delta) {
} }
} }
if (!collided || motion.is_equal_approx(Vector3())) { if (!collided || motion.is_zero_approx()) {
break; break;
} }

View File

@ -431,7 +431,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> m = p_event; Ref<InputEventMouseMotion> m = p_event;
if (m.is_valid()) { if (m.is_valid()) {
if (m->get_velocity().is_equal_approx(Vector2())) { if (m->get_velocity().is_zero_approx()) {
return; return;
} }
activated_by_keyboard = false; activated_by_keyboard = false;

View File

@ -687,7 +687,7 @@ void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const {
const bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0); const bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0);
// Only enable antialiasing if it is actually needed. This improve performances // Only enable antialiasing if it is actually needed. This improve performances
// and maximizes sharpness for non-skewed StyleBoxes with sharp corners. // and maximizes sharpness for non-skewed StyleBoxes with sharp corners.
const bool aa_on = (rounded_corners || !skew.is_equal_approx(Vector2())) && anti_aliased; const bool aa_on = (rounded_corners || !skew.is_zero_approx()) && anti_aliased;
const bool blend_on = blend_border && draw_border; const bool blend_on = blend_border && draw_border;

View File

@ -138,7 +138,7 @@ void GodotPhysicsDirectBodyState2D::add_constant_torque(real_t p_torque) {
} }
void GodotPhysicsDirectBodyState2D::set_constant_force(const Vector2 &p_force) { void GodotPhysicsDirectBodyState2D::set_constant_force(const Vector2 &p_force) {
if (!p_force.is_equal_approx(Vector2())) { if (!p_force.is_zero_approx()) {
body->wakeup(); body->wakeup();
} }
body->set_constant_force(p_force); body->set_constant_force(p_force);

View File

@ -848,7 +848,7 @@ void GodotPhysicsServer2D::body_set_constant_force(RID p_body, const Vector2 &p_
ERR_FAIL_COND(!body); ERR_FAIL_COND(!body);
body->set_constant_force(p_force); body->set_constant_force(p_force);
if (!p_force.is_equal_approx(Vector2())) { if (!p_force.is_zero_approx()) {
body->wakeup(); body->wakeup();
} }
} }

View File

@ -145,7 +145,7 @@ void GodotPhysicsDirectBodyState3D::add_constant_torque(const Vector3 &p_torque)
} }
void GodotPhysicsDirectBodyState3D::set_constant_force(const Vector3 &p_force) { void GodotPhysicsDirectBodyState3D::set_constant_force(const Vector3 &p_force) {
if (!p_force.is_equal_approx(Vector3())) { if (!p_force.is_zero_approx()) {
body->wakeup(); body->wakeup();
} }
body->set_constant_force(p_force); body->set_constant_force(p_force);
@ -156,7 +156,7 @@ Vector3 GodotPhysicsDirectBodyState3D::get_constant_force() const {
} }
void GodotPhysicsDirectBodyState3D::set_constant_torque(const Vector3 &p_torque) { void GodotPhysicsDirectBodyState3D::set_constant_torque(const Vector3 &p_torque) {
if (!p_torque.is_equal_approx(Vector3())) { if (!p_torque.is_zero_approx()) {
body->wakeup(); body->wakeup();
} }
body->set_constant_torque(p_torque); body->set_constant_torque(p_torque);

View File

@ -629,7 +629,7 @@ public:
_FORCE_INLINE_ bool test_axis(const Vector3 &p_axis) { _FORCE_INLINE_ bool test_axis(const Vector3 &p_axis) {
Vector3 axis = p_axis; Vector3 axis = p_axis;
if (axis.is_equal_approx(Vector3())) { if (axis.is_zero_approx()) {
// strange case, try an upwards separator // strange case, try an upwards separator
axis = Vector3(0.0, 1.0, 0.0); axis = Vector3(0.0, 1.0, 0.0);
} }

View File

@ -760,7 +760,7 @@ void GodotPhysicsServer3D::body_set_constant_force(RID p_body, const Vector3 &p_
ERR_FAIL_COND(!body); ERR_FAIL_COND(!body);
body->set_constant_force(p_force); body->set_constant_force(p_force);
if (!p_force.is_equal_approx(Vector3())) { if (!p_force.is_zero_approx()) {
body->wakeup(); body->wakeup();
} }
} }
@ -776,7 +776,7 @@ void GodotPhysicsServer3D::body_set_constant_torque(RID p_body, const Vector3 &p
ERR_FAIL_COND(!body); ERR_FAIL_COND(!body);
body->set_constant_torque(p_torque); body->set_constant_torque(p_torque);
if (!p_torque.is_equal_approx(Vector3())) { if (!p_torque.is_zero_approx()) {
body->wakeup(); body->wakeup();
} }
} }