summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/physics_body_2d.cpp57
-rw-r--r--scene/2d/physics_body_2d.h2
-rw-r--r--scene/3d/physics_body_3d.cpp63
-rw-r--r--scene/3d/physics_body_3d.h2
-rw-r--r--scene/main/viewport.cpp13
-rw-r--r--scene/main/viewport.h5
6 files changed, 117 insertions, 25 deletions
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 241a997ada..7c41cd98fb 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -76,13 +76,46 @@ Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_i
return Ref<KinematicCollision2D>();
}
-bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes, bool p_test_only) {
+bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes, bool p_test_only, bool p_cancel_sliding) {
if (is_only_update_transform_changes_enabled()) {
ERR_PRINT("Move functions do not work together with 'sync to physics' option. Please read the documentation.");
}
Transform2D gt = get_global_transform();
bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, p_margin, &r_result, p_exclude_raycast_shapes);
+ // Restore direction of motion to be along original motion,
+ // in order to avoid sliding due to recovery,
+ // but only if collision depth is low enough to avoid tunneling.
+ real_t motion_length = p_motion.length();
+ if (motion_length > CMP_EPSILON) {
+ real_t precision = 0.001;
+
+ if (colliding && p_cancel_sliding) {
+ // Can't just use margin as a threshold because collision depth is calculated on unsafe motion,
+ // so even in normal resting cases the depth can be a bit more than the margin.
+ precision += motion_length * (r_result.collision_unsafe_fraction - r_result.collision_safe_fraction);
+
+ if (r_result.collision_depth > (real_t)p_margin + precision) {
+ p_cancel_sliding = false;
+ }
+ }
+
+ if (p_cancel_sliding) {
+ // Check depth of recovery.
+ Vector2 motion_normal = p_motion / motion_length;
+ real_t dot = r_result.motion.dot(motion_normal);
+ Vector2 recovery = r_result.motion - motion_normal * dot;
+ real_t recovery_length = recovery.length();
+ // Fixes cases where canceling slide causes the motion to go too deep into the ground,
+ // Becauses we're only taking rest information into account and not general recovery.
+ if (recovery_length < (real_t)p_margin + precision) {
+ // Apply adjustment to motion.
+ r_result.motion = motion_normal * dot;
+ r_result.remainder = p_motion - r_result.motion;
+ }
+ }
+ }
+
if (!p_test_only) {
gt.elements[2] += r_result.motion;
set_global_transform(gt);
@@ -942,15 +975,16 @@ void CharacterBody2D::move_and_slide() {
floor_normal = Vector2();
floor_velocity = Vector2();
- int slide_count = max_slides;
- while (slide_count) {
+ // No sliding on first attempt to keep floor motion stable when possible.
+ bool sliding_enabled = false;
+ for (int iteration = 0; iteration < max_slides; ++iteration) {
PhysicsServer2D::MotionResult result;
bool found_collision = false;
for (int i = 0; i < 2; ++i) {
bool collided;
if (i == 0) { //collide
- collided = move_and_collide(motion, infinite_inertia, result, margin);
+ collided = move_and_collide(motion, infinite_inertia, result, margin, true, false, !sliding_enabled);
if (!collided) {
motion = Vector2(); //clear because no collision happened and motion completed
}
@@ -966,7 +1000,6 @@ void CharacterBody2D::move_and_slide() {
found_collision = true;
motion_results.push_back(result);
- motion = result.remainder;
if (up_direction == Vector2()) {
//all is a wall
@@ -980,7 +1013,7 @@ void CharacterBody2D::move_and_slide() {
floor_velocity = result.collider_velocity;
if (stop_on_slope) {
- if ((body_velocity_normal + up_direction).length() < 0.01 && result.motion.length() < 1) {
+ if ((body_velocity_normal + up_direction).length() < 0.01) {
Transform2D gt = get_global_transform();
gt.elements[2] -= result.motion.slide(up_direction);
set_global_transform(gt);
@@ -995,16 +1028,20 @@ void CharacterBody2D::move_and_slide() {
}
}
- motion = motion.slide(result.collision_normal);
- linear_velocity = linear_velocity.slide(result.collision_normal);
+ if (sliding_enabled || !on_floor) {
+ motion = result.remainder.slide(result.collision_normal);
+ linear_velocity = linear_velocity.slide(result.collision_normal);
+ } else {
+ motion = result.remainder;
+ }
}
+
+ sliding_enabled = true;
}
if (!found_collision || motion == Vector2()) {
break;
}
-
- --slide_count;
}
if (!was_on_floor || snap == Vector2()) {
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index 31e7fccde0..5a5417eaf3 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -50,7 +50,7 @@ protected:
Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, bool p_test_only = false, real_t p_margin = 0.08);
public:
- bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes = true, bool p_test_only = false);
+ bool move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes = true, bool p_test_only = false, bool p_cancel_sliding = true);
bool test_move(const Transform2D &p_from, const Vector2 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08);
TypedArray<PhysicsBody2D> get_collision_exceptions();
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index 00c372cd26..cb2a873f62 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -118,10 +118,43 @@ Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_i
return Ref<KinematicCollision3D>();
}
-bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes, bool p_test_only) {
+bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes, bool p_test_only, bool p_cancel_sliding) {
Transform3D gt = get_global_transform();
bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, p_margin, &r_result, p_exclude_raycast_shapes);
+ // Restore direction of motion to be along original motion,
+ // in order to avoid sliding due to recovery,
+ // but only if collision depth is low enough to avoid tunneling.
+ real_t motion_length = p_motion.length();
+ if (motion_length > CMP_EPSILON) {
+ real_t precision = CMP_EPSILON;
+
+ if (colliding && p_cancel_sliding) {
+ // Can't just use margin as a threshold because collision depth is calculated on unsafe motion,
+ // so even in normal resting cases the depth can be a bit more than the margin.
+ precision += motion_length * (r_result.collision_unsafe_fraction - r_result.collision_safe_fraction);
+
+ if (r_result.collision_depth > (real_t)p_margin + precision) {
+ p_cancel_sliding = false;
+ }
+ }
+
+ if (p_cancel_sliding) {
+ // Check depth of recovery.
+ Vector3 motion_normal = p_motion / motion_length;
+ real_t dot = r_result.motion.dot(motion_normal);
+ Vector3 recovery = r_result.motion - motion_normal * dot;
+ real_t recovery_length = recovery.length();
+ // Fixes cases where canceling slide causes the motion to go too deep into the ground,
+ // Becauses we're only taking rest information into account and not general recovery.
+ if (recovery_length < (real_t)p_margin + precision) {
+ // Apply adjustment to motion.
+ r_result.motion = motion_normal * dot;
+ r_result.remainder = p_motion - r_result.motion;
+ }
+ }
+ }
+
for (int i = 0; i < 3; i++) {
if (locked_axis & (1 << i)) {
r_result.motion[i] = 0;
@@ -978,15 +1011,16 @@ void CharacterBody3D::move_and_slide() {
floor_normal = Vector3();
floor_velocity = Vector3();
- int slide_count = max_slides;
- while (slide_count) {
+ // No sliding on first attempt to keep motion stable when possible.
+ bool sliding_enabled = false;
+ for (int iteration = 0; iteration < max_slides; ++iteration) {
PhysicsServer3D::MotionResult result;
bool found_collision = false;
for (int i = 0; i < 2; ++i) {
bool collided;
if (i == 0) { //collide
- collided = move_and_collide(motion, infinite_inertia, result, margin);
+ collided = move_and_collide(motion, infinite_inertia, result, margin, true, false, !sliding_enabled);
if (!collided) {
motion = Vector3(); //clear because no collision happened and motion completed
}
@@ -1002,7 +1036,6 @@ void CharacterBody3D::move_and_slide() {
found_collision = true;
motion_results.push_back(result);
- motion = result.remainder;
if (up_direction == Vector3()) {
//all is a wall
@@ -1016,7 +1049,7 @@ void CharacterBody3D::move_and_slide() {
floor_velocity = result.collider_velocity;
if (stop_on_slope) {
- if ((body_velocity_normal + up_direction).length() < 0.01 && result.motion.length() < 1) {
+ if ((body_velocity_normal + up_direction).length() < 0.01) {
Transform3D gt = get_global_transform();
gt.origin -= result.motion.slide(up_direction);
set_global_transform(gt);
@@ -1031,22 +1064,26 @@ void CharacterBody3D::move_and_slide() {
}
}
- motion = motion.slide(result.collision_normal);
- linear_velocity = linear_velocity.slide(result.collision_normal);
+ if (sliding_enabled || !on_floor) {
+ motion = result.remainder.slide(result.collision_normal);
+ linear_velocity = linear_velocity.slide(result.collision_normal);
- for (int j = 0; j < 3; j++) {
- if (locked_axis & (1 << j)) {
- linear_velocity[j] = 0.0;
+ for (int j = 0; j < 3; j++) {
+ if (locked_axis & (1 << j)) {
+ linear_velocity[j] = 0.0;
+ }
}
+ } else {
+ motion = result.remainder;
}
}
+
+ sliding_enabled = true;
}
if (!found_collision || motion == Vector3()) {
break;
}
-
- --slide_count;
}
if (!was_on_floor || snap == Vector3()) {
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index c704a578d7..9d45ce3799 100644
--- a/scene/3d/physics_body_3d.h
+++ b/scene/3d/physics_body_3d.h
@@ -53,7 +53,7 @@ protected:
Ref<KinematicCollision3D> _move(const Vector3 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, bool p_test_only = false, real_t p_margin = 0.001);
public:
- bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes = true, bool p_test_only = false);
+ bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_exclude_raycast_shapes = true, bool p_test_only = false, bool p_cancel_sliding = true);
bool test_move(const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001);
void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock);
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index cc56d6a49e..319e589036 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -929,6 +929,15 @@ bool Viewport::is_audio_listener_2d() const {
return audio_listener_2d;
}
+void Viewport::set_disable_3d(bool p_disable) {
+ disable_3d = p_disable;
+ RenderingServer::get_singleton()->viewport_set_disable_3d(viewport, disable_3d);
+}
+
+bool Viewport::is_3d_disabled() const {
+ return disable_3d;
+}
+
void Viewport::enable_canvas_transform_override(bool p_enable) {
if (override_canvas_transform == p_enable) {
return;
@@ -3516,6 +3525,9 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_as_audio_listener_2d", "enable"), &Viewport::set_as_audio_listener_2d);
ClassDB::bind_method(D_METHOD("is_audio_listener_2d"), &Viewport::is_audio_listener_2d);
+ ClassDB::bind_method(D_METHOD("set_disable_3d", "disable"), &Viewport::set_disable_3d);
+ ClassDB::bind_method(D_METHOD("is_3d_disabled"), &Viewport::is_3d_disabled);
+
ClassDB::bind_method(D_METHOD("get_mouse_position"), &Viewport::get_mouse_position);
ClassDB::bind_method(D_METHOD("warp_mouse", "to_position"), &Viewport::warp_mouse);
@@ -3582,6 +3594,7 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_transforms_to_pixel"), "set_snap_2d_transforms_to_pixel", "is_snap_2d_transforms_to_pixel_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_vertices_to_pixel"), "set_snap_2d_vertices_to_pixel", "is_snap_2d_vertices_to_pixel_enabled");
ADD_GROUP("Rendering", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, "Disabled (Fastest),2x (Fast),4x (Average),8x (Slow),16x (Slower)"), "set_msaa", "get_msaa");
ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)"), "set_screen_space_aa", "get_screen_space_aa");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "is_using_debanding");
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index d905e44a82..29491a1178 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -288,6 +288,8 @@ private:
void _update_listener();
void _update_listener_2d();
+ bool disable_3d = false;
+
void _propagate_enter_world(Node *p_node);
void _propagate_exit_world(Node *p_node);
void _propagate_viewport_notification(Node *p_node, int p_what);
@@ -503,6 +505,9 @@ public:
void set_as_audio_listener_2d(bool p_enable);
bool is_audio_listener_2d() const;
+ void set_disable_3d(bool p_disable);
+ bool is_3d_disabled() const;
+
void update_canvas_items();
Rect2 get_visible_rect() const;