From ec9fed69f4676e42e677c551423cd5f5c8f13100 Mon Sep 17 00:00:00 2001 From: PouleyKetchoupp Date: Mon, 9 Aug 2021 18:16:45 -0700 Subject: [PATCH] Fix 3D moving platform logic Same thing that was already done in 2D, applies moving platform motion by using a call to move_and_collide that excludes the platform itself, instead of making it part of the body motion. Helps with handling walls and slopes correctly when the character walks on the moving platform. Also made some minor adjustments to the 2D version and documentation. Co-authored-by: fabriceci --- doc/classes/CharacterBody2D.xml | 1 + doc/classes/CharacterBody3D.xml | 1 + doc/classes/PhysicsServer3D.xml | 2 + modules/bullet/bullet_physics_server.cpp | 4 +- modules/bullet/bullet_physics_server.h | 2 +- modules/bullet/godot_result_callbacks.cpp | 4 + modules/bullet/godot_result_callbacks.h | 4 +- modules/bullet/space_bullet.cpp | 16 ++- modules/bullet/space_bullet.h | 4 +- scene/2d/physics_body_2d.cpp | 2 +- scene/2d/physics_body_2d.h | 3 +- scene/3d/physics_body_3d.cpp | 97 ++++++++++++------- scene/3d/physics_body_3d.h | 4 +- servers/physics_3d/physics_server_3d_sw.cpp | 4 +- servers/physics_3d/physics_server_3d_sw.h | 2 +- .../physics_3d/physics_server_3d_wrap_mt.h | 4 +- servers/physics_3d/space_3d_sw.cpp | 11 ++- servers/physics_3d/space_3d_sw.h | 2 +- servers/physics_server_3d.cpp | 10 +- servers/physics_server_3d.h | 4 +- 20 files changed, 119 insertions(+), 62 deletions(-) diff --git a/doc/classes/CharacterBody2D.xml b/doc/classes/CharacterBody2D.xml index 1ace7ee6bf952..b73aba50fd563 100644 --- a/doc/classes/CharacterBody2D.xml +++ b/doc/classes/CharacterBody2D.xml @@ -79,6 +79,7 @@ Moves the body based on [member linear_velocity]. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [CharacterBody2D] or [RigidBody2D], it will also be affected by the motion of the other body. You can use this to make moving and rotating platforms, or to make nodes push other nodes. This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed. Modifies [member linear_velocity] if a slide collision occurred. To get detailed information about collisions that occurred, use [method get_slide_collision]. + When the body touches a moving platform, the platform's velocity is automatically added to the body motion. If a collision occurs due to the platform's motion, it will always be first in the slide collisions. diff --git a/doc/classes/CharacterBody3D.xml b/doc/classes/CharacterBody3D.xml index 577ab5e84cbe1..4cb4f9a850c1e 100644 --- a/doc/classes/CharacterBody3D.xml +++ b/doc/classes/CharacterBody3D.xml @@ -65,6 +65,7 @@ Moves the body based on [member linear_velocity]. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [CharacterBody3D] or [RigidBody3D], it will also be affected by the motion of the other body. You can use this to make moving and rotating platforms, or to make nodes push other nodes. This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed. Modifies [member linear_velocity] if a slide collision occurred. To get detailed information about collisions that occurred, use [method get_slide_collision]. + When the body touches a moving platform, the platform's velocity is automatically added to the body motion. If a collision occurs due to the platform's motion, it will always be first in the slide collisions. diff --git a/doc/classes/PhysicsServer3D.xml b/doc/classes/PhysicsServer3D.xml index 7bca8afddafba..d6b7318a068d1 100644 --- a/doc/classes/PhysicsServer3D.xml +++ b/doc/classes/PhysicsServer3D.xml @@ -572,6 +572,8 @@ + + Returns [code]true[/code] if a collision would result from moving in the given direction from a given point in space. Margin increases the size of the shapes involved in the collision detection. [PhysicsTestMotionResult3D] can be passed to return additional information in. diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp index e07be14c5b385..fc876a81cf44c 100644 --- a/modules/bullet/bullet_physics_server.cpp +++ b/modules/bullet/bullet_physics_server.cpp @@ -842,12 +842,12 @@ PhysicsDirectBodyState3D *BulletPhysicsServer3D::body_get_direct_state(RID p_bod return BulletPhysicsDirectBodyState3D::get_singleton(body); } -bool BulletPhysicsServer3D::body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result, bool p_exclude_raycast_shapes) { +bool BulletPhysicsServer3D::body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result, bool p_exclude_raycast_shapes, const Set &p_exclude) { RigidBodyBullet *body = rigid_body_owner.getornull(p_body); ERR_FAIL_COND_V(!body, false); ERR_FAIL_COND_V(!body->get_space(), false); - return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, r_result, p_exclude_raycast_shapes); + return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, r_result, p_exclude_raycast_shapes, p_exclude); } int BulletPhysicsServer3D::body_test_ray_separation(RID p_body, const Transform3D &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, real_t p_margin) { diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h index d34d619ba272b..7f0934e679a28 100644 --- a/modules/bullet/bullet_physics_server.h +++ b/modules/bullet/bullet_physics_server.h @@ -253,7 +253,7 @@ class BulletPhysicsServer3D : public PhysicsServer3D { // this function only works on physics process, errors and returns null otherwise virtual PhysicsDirectBodyState3D *body_get_direct_state(RID p_body) override; - virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true) override; + virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true, const Set &p_exclude = Set()) override; virtual int body_test_ray_separation(RID p_body, const Transform3D &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, real_t p_margin = 0.001) override; /* SOFT BODY API */ diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp index 399b102284a24..1f962772e7fd9 100644 --- a/modules/bullet/godot_result_callbacks.cpp +++ b/modules/bullet/godot_result_callbacks.cpp @@ -135,6 +135,10 @@ bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *prox if (m_self_object->has_collision_exception(gObj) || gObj->has_collision_exception(m_self_object)) { return false; } + + if (m_exclude->has(gObj->get_self())) { + return false; + } } return true; } else { diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h index 9216322108fb6..96a649d77a1f5 100644 --- a/modules/bullet/godot_result_callbacks.h +++ b/modules/bullet/godot_result_callbacks.h @@ -100,11 +100,13 @@ struct GodotAllConvexResultCallback : public btCollisionWorld::ConvexResultCallb struct GodotKinClosestConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback { public: const RigidBodyBullet *m_self_object; + const Set *m_exclude; const bool m_infinite_inertia; - GodotKinClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const RigidBodyBullet *p_self_object, bool p_infinite_inertia) : + GodotKinClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const RigidBodyBullet *p_self_object, bool p_infinite_inertia, const Set *p_exclude) : btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld), m_self_object(p_self_object), + m_exclude(p_exclude), m_infinite_inertia(p_infinite_inertia) {} virtual bool needsCollision(btBroadphaseProxy *proxy0) const; diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index 583900e6bc6e9..c6d835742dbe4 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -908,7 +908,7 @@ static Ref red_mat; static Ref blue_mat; #endif -bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult *r_result, bool p_exclude_raycast_shapes) { +bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult *r_result, bool p_exclude_raycast_shapes, const Set &p_exclude) { #if debug_test_motion /// Yes I know this is not good, but I've used it as fast debugging hack. /// I'm leaving it here just for speedup the other eventual debugs @@ -948,7 +948,7 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform3D &p btVector3 initial_recover_motion(0, 0, 0); { /// Phase one - multi shapes depenetration using margin for (int t(RECOVERING_MOVEMENT_CYCLES); 0 < t; --t) { - if (!recover_from_penetration(p_body, body_transform, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, initial_recover_motion)) { + if (!recover_from_penetration(p_body, body_transform, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, initial_recover_motion, nullptr, p_exclude)) { break; } } @@ -1000,7 +1000,7 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform3D &p break; } - GodotKinClosestConvexResultCallback btResult(shape_world_from.getOrigin(), shape_world_to.getOrigin(), p_body, p_infinite_inertia); + GodotKinClosestConvexResultCallback btResult(shape_world_from.getOrigin(), shape_world_to.getOrigin(), p_body, p_infinite_inertia, &p_exclude); btResult.m_collisionFilterGroup = p_body->get_collision_layer(); btResult.m_collisionFilterMask = p_body->get_collision_mask(); @@ -1023,7 +1023,7 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform3D &p btVector3 __rec(0, 0, 0); RecoverResult r_recover_result; - has_penetration = recover_from_penetration(p_body, body_transform, 1, p_infinite_inertia, __rec, &r_recover_result); + has_penetration = recover_from_penetration(p_body, body_transform, 1, p_infinite_inertia, __rec, &r_recover_result, p_exclude); // Parse results if (r_result) { @@ -1173,7 +1173,7 @@ struct RecoverPenetrationBroadPhaseCallback : public btBroadphaseAabbCallback { } }; -bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result) { +bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result, const Set &p_exclude) { // Calculate the cumulative AABB of all shapes of the kinematic body btVector3 aabb_min, aabb_max; bool shapes_found = false; @@ -1242,6 +1242,12 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran for (int i = recover_broad_result.results.size() - 1; 0 <= i; --i) { btCollisionObject *otherObject = recover_broad_result.results[i].collision_object; + + CollisionObjectBullet *gObj = static_cast(otherObject->getUserPointer()); + if (p_exclude.has(gObj->get_self())) { + continue; + } + if (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()) { otherObject->activate(); // Force activation of hitten rigid, soft body continue; diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h index 36d0538e6bdb1..2070e0e6332b0 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -188,7 +188,7 @@ class SpaceBullet : public RIDBullet { real_t get_linear_damp() const { return linear_damp; } real_t get_angular_damp() const { return angular_damp; } - bool test_body_motion(RigidBodyBullet *p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult *r_result, bool p_exclude_raycast_shapes); + bool test_body_motion(RigidBodyBullet *p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult *r_result, bool p_exclude_raycast_shapes, const Set &p_exclude = Set()); int test_ray_separation(RigidBodyBullet *p_body, const Transform3D &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, PhysicsServer3D::SeparationResult *r_results, int p_result_max, real_t p_margin); private: @@ -209,7 +209,7 @@ class SpaceBullet : public RIDBullet { RecoverResult() {} }; - bool recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = nullptr); + bool recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = nullptr, const Set &p_exclude = Set()); /// This is an API that recover a kinematic object from penetration /// This allow only Convex Convex test and it always use GJK algorithm, With this API we don't benefit of Bullet special accelerated functions bool RFP_convex_convex_test(const btConvexShape *p_shapeA, const btConvexShape *p_shapeB, btCollisionObject *p_objectB, int p_shapeId_A, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = nullptr); diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 00eeb601922ae..9235a2290eb6d 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -1075,7 +1075,7 @@ void CharacterBody2D::move_and_slide() { floor_normal = Vector2(); floor_velocity = Vector2(); - if (current_floor_velocity != Vector2()) { + if (current_floor_velocity != Vector2() && on_floor_body.is_valid()) { PhysicsServer2D::MotionResult floor_result; Set exclude; exclude.insert(on_floor_body); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 7a319aabc9060..c20372fe75a67 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -292,6 +292,8 @@ class CharacterBody2D : public PhysicsBody2D { Ref _get_slide_collision(int p_bounce); + void _set_collision_direction(const PhysicsServer2D::MotionResult &p_result); + bool separate_raycast_shapes(PhysicsServer2D::MotionResult &r_result); void set_safe_margin(real_t p_margin); @@ -314,7 +316,6 @@ class CharacterBody2D : public PhysicsBody2D { const Vector2 &get_up_direction() const; void set_up_direction(const Vector2 &p_up_direction); - void _set_collision_direction(const PhysicsServer2D::MotionResult &p_result); protected: void _notification(int p_what); diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 38104a83657c2..53ec07f1ce5bb 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -117,9 +117,9 @@ Ref PhysicsBody3D::_move(const Vector3 &p_motion, bool p_i return Ref(); } -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) { +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, const Set &p_exclude) { 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); + bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, p_margin, &r_result, p_exclude_raycast_shapes, p_exclude); // Restore direction of motion to be along original motion, // in order to avoid sliding due to recovery, @@ -1090,6 +1090,9 @@ void CharacterBody3D::move_and_slide() { bool was_on_floor = on_floor; + // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky + float delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + for (int i = 0; i < 3; i++) { if (locked_axis & (1 << i)) { linear_velocity[i] = 0.0; @@ -1097,7 +1100,7 @@ void CharacterBody3D::move_and_slide() { } Vector3 current_floor_velocity = floor_velocity; - if (on_floor && on_floor_body.is_valid()) { + if ((on_floor || on_wall) && on_floor_body.is_valid()) { //this approach makes sure there is less delay between the actual body velocity and the one we saved PhysicsDirectBodyState3D *bs = PhysicsServer3D::get_singleton()->body_get_direct_state(on_floor_body); if (bs) { @@ -1107,20 +1110,30 @@ void CharacterBody3D::move_and_slide() { } } - // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky - Vector3 motion = (floor_velocity + linear_velocity) * (Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time()); - + motion_results.clear(); on_floor = false; - on_floor_body = RID(); on_ceiling = false; on_wall = false; - motion_results.clear(); floor_normal = Vector3(); floor_velocity = Vector3(); + if (current_floor_velocity != Vector3() && on_floor_body.is_valid()) { + PhysicsServer3D::MotionResult floor_result; + Set exclude; + exclude.insert(on_floor_body); + if (move_and_collide(current_floor_velocity * delta, infinite_inertia, floor_result, true, false, false, false, exclude)) { + motion_results.push_back(floor_result); + _set_collision_direction(floor_result); + } + } + + on_floor_body = RID(); + Vector3 motion = linear_velocity * delta; + // No sliding on first attempt to keep floor motion stable when possible, // when stop on slope is enabled. bool sliding_enabled = !stop_on_slope; + for (int iteration = 0; iteration < max_slides; ++iteration) { PhysicsServer3D::MotionResult result; bool found_collision = false; @@ -1144,35 +1157,19 @@ void CharacterBody3D::move_and_slide() { found_collision = true; motion_results.push_back(result); - - if (up_direction == Vector3()) { - //all is a wall - on_wall = true; - } else { - if (Math::acos(result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor - - on_floor = true; - floor_normal = result.collision_normal; - on_floor_body = result.collider; - floor_velocity = result.collider_velocity; - - if (stop_on_slope) { - if ((body_velocity_normal + up_direction).length() < 0.01) { - Transform3D gt = get_global_transform(); - if (result.motion.length() > margin) { - gt.origin -= result.motion.slide(up_direction); - } else { - gt.origin -= result.motion; - } - set_global_transform(gt); - linear_velocity = Vector3(); - return; - } + _set_collision_direction(result); + + if (on_floor && stop_on_slope) { + if ((body_velocity_normal + up_direction).length() < 0.01) { + Transform3D gt = get_global_transform(); + if (result.motion.length() > margin) { + gt.origin -= result.motion.slide(up_direction); + } else { + gt.origin -= result.motion; } - } else if (Math::acos(result.collision_normal.dot(-up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling - on_ceiling = true; - } else { - on_wall = true; + set_global_transform(gt); + linear_velocity = Vector3(); + return; } } @@ -1198,6 +1195,11 @@ void CharacterBody3D::move_and_slide() { } } + if (!on_floor && !on_wall) { + // Add last platform velocity when just left a moving platform. + linear_velocity += current_floor_velocity; + } + if (!was_on_floor || snap == Vector3()) { return; } @@ -1233,6 +1235,29 @@ void CharacterBody3D::move_and_slide() { } } +void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResult &p_result) { + on_floor = false; + on_ceiling = false; + on_wall = false; + if (up_direction == Vector3()) { + //all is a wall + on_wall = true; + } else { + if (Math::acos(p_result.collision_normal.dot(up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor + on_floor = true; + floor_normal = p_result.collision_normal; + on_floor_body = p_result.collider; + floor_velocity = p_result.collider_velocity; + } else if (Math::acos(p_result.collision_normal.dot(-up_direction)) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling + on_ceiling = true; + } else { + on_wall = true; + on_floor_body = p_result.collider; + floor_velocity = p_result.collider_velocity; + } + } +} + bool CharacterBody3D::separate_raycast_shapes(PhysicsServer3D::MotionResult &r_result) { PhysicsServer3D::SeparationResult sep_res[8]; //max 8 rays diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 0ef9c78f3bdee..227e6d43b63c3 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -53,7 +53,7 @@ class PhysicsBody3D : public CollisionObject3D { Ref _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 p_cancel_sliding = true); + 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, const Set &p_exclude = Set()); bool test_move(const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, const Ref &r_collision = Ref(), real_t p_margin = 0.001); void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock); @@ -298,6 +298,8 @@ class CharacterBody3D : public PhysicsBody3D { Ref _get_slide_collision(int p_bounce); + void _set_collision_direction(const PhysicsServer3D::MotionResult &p_result); + bool separate_raycast_shapes(PhysicsServer3D::MotionResult &r_result); void set_safe_margin(real_t p_margin); diff --git a/servers/physics_3d/physics_server_3d_sw.cpp b/servers/physics_3d/physics_server_3d_sw.cpp index c1a9d6259da73..e2ab84428bd5e 100644 --- a/servers/physics_3d/physics_server_3d_sw.cpp +++ b/servers/physics_3d/physics_server_3d_sw.cpp @@ -854,7 +854,7 @@ void PhysicsServer3DSW::body_set_ray_pickable(RID p_body, bool p_enable) { body->set_ray_pickable(p_enable); } -bool PhysicsServer3DSW::body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin, MotionResult *r_result, bool p_exclude_raycast_shapes) { +bool PhysicsServer3DSW::body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin, MotionResult *r_result, bool p_exclude_raycast_shapes, const Set &p_exclude) { Body3DSW *body = body_owner.getornull(p_body); ERR_FAIL_COND_V(!body, false); ERR_FAIL_COND_V(!body->get_space(), false); @@ -862,7 +862,7 @@ bool PhysicsServer3DSW::body_test_motion(RID p_body, const Transform3D &p_from, _update_shapes(); - return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, p_margin, r_result, p_exclude_raycast_shapes); + return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, p_margin, r_result, p_exclude_raycast_shapes, p_exclude); } int PhysicsServer3DSW::body_test_ray_separation(RID p_body, const Transform3D &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, real_t p_margin) { diff --git a/servers/physics_3d/physics_server_3d_sw.h b/servers/physics_3d/physics_server_3d_sw.h index 0ccd15fbb2f79..326b6fbe7a98a 100644 --- a/servers/physics_3d/physics_server_3d_sw.h +++ b/servers/physics_3d/physics_server_3d_sw.h @@ -242,7 +242,7 @@ class PhysicsServer3DSW : public PhysicsServer3D { virtual void body_set_ray_pickable(RID p_body, bool p_enable) override; - virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.001, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true) override; + virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.001, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true, const Set &p_exclude = Set()) override; virtual int body_test_ray_separation(RID p_body, const Transform3D &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, real_t p_margin = 0.001) override; // this function only works on physics process, errors and returns null otherwise diff --git a/servers/physics_3d/physics_server_3d_wrap_mt.h b/servers/physics_3d/physics_server_3d_wrap_mt.h index 9beec22bcd690..c3c2e50e560c8 100644 --- a/servers/physics_3d/physics_server_3d_wrap_mt.h +++ b/servers/physics_3d/physics_server_3d_wrap_mt.h @@ -250,9 +250,9 @@ class PhysicsServer3DWrapMT : public PhysicsServer3D { FUNC2(body_set_ray_pickable, RID, bool); - bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.001, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true) override { + bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.001, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true, const Set &p_exclude = Set()) override { ERR_FAIL_COND_V(main_thread != Thread::get_caller_id(), false); - return physics_3d_server->body_test_motion(p_body, p_from, p_motion, p_infinite_inertia, p_margin, r_result, p_exclude_raycast_shapes); + return physics_3d_server->body_test_motion(p_body, p_from, p_motion, p_infinite_inertia, p_margin, r_result, p_exclude_raycast_shapes, p_exclude); } int body_test_ray_separation(RID p_body, const Transform3D &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, real_t p_margin = 0.001) override { diff --git a/servers/physics_3d/space_3d_sw.cpp b/servers/physics_3d/space_3d_sw.cpp index 1037243d3b055..2a39a9d3637b8 100644 --- a/servers/physics_3d/space_3d_sw.cpp +++ b/servers/physics_3d/space_3d_sw.cpp @@ -720,7 +720,7 @@ int Space3DSW::test_body_ray_separation(Body3DSW *p_body, const Transform3D &p_t return rays_found; } -bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin, PhysicsServer3D::MotionResult *r_result, bool p_exclude_raycast_shapes) { +bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin, PhysicsServer3D::MotionResult *r_result, bool p_exclude_raycast_shapes, const Set &p_exclude) { //give me back regular physics engine logic //this is madness //and most people using this function will think @@ -801,6 +801,9 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co for (int i = 0; i < amount; i++) { const CollisionObject3DSW *col_obj = intersection_query_results[i]; + if (p_exclude.has(col_obj->get_self())) { + continue; + } int shape_idx = intersection_query_subindex_results[i]; if (CollisionObject3DSW::TYPE_BODY == col_obj->get_type()) { @@ -890,6 +893,9 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co for (int i = 0; i < amount; i++) { const CollisionObject3DSW *col_obj = intersection_query_results[i]; + if (p_exclude.has(col_obj->get_self())) { + continue; + } int shape_idx = intersection_query_subindex_results[i]; if (CollisionObject3DSW::TYPE_BODY == col_obj->get_type()) { @@ -1015,6 +1021,9 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co for (int i = 0; i < amount; i++) { const CollisionObject3DSW *col_obj = intersection_query_results[i]; + if (p_exclude.has(col_obj->get_self())) { + continue; + } int shape_idx = intersection_query_subindex_results[i]; if (CollisionObject3DSW::TYPE_BODY == col_obj->get_type()) { diff --git a/servers/physics_3d/space_3d_sw.h b/servers/physics_3d/space_3d_sw.h index 18e93c90cccee..580693deba8ca 100644 --- a/servers/physics_3d/space_3d_sw.h +++ b/servers/physics_3d/space_3d_sw.h @@ -204,7 +204,7 @@ class Space3DSW { uint64_t get_elapsed_time(ElapsedTime p_time) const { return elapsed_time[p_time]; } int test_body_ray_separation(Body3DSW *p_body, const Transform3D &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, PhysicsServer3D::SeparationResult *r_results, int p_result_max, real_t p_margin); - bool test_body_motion(Body3DSW *p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin, PhysicsServer3D::MotionResult *r_result, bool p_exclude_raycast_shapes); + bool test_body_motion(Body3DSW *p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin, PhysicsServer3D::MotionResult *r_result, bool p_exclude_raycast_shapes, const Set &p_exclude = Set()); Space3DSW(); ~Space3DSW(); diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp index 63a51dd9d6b2b..b4ee5fbaf0557 100644 --- a/servers/physics_server_3d.cpp +++ b/servers/physics_server_3d.cpp @@ -447,12 +447,16 @@ void PhysicsTestMotionResult3D::_bind_methods() { /////////////////////////////////////// -bool PhysicsServer3D::_body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin, const Ref &p_result) { +bool PhysicsServer3D::_body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin, const Ref &p_result, bool p_exclude_raycast_shapes, const Vector &p_exclude) { MotionResult *r = nullptr; if (p_result.is_valid()) { r = p_result->get_result_ptr(); } - return body_test_motion(p_body, p_from, p_motion, p_infinite_inertia, p_margin, r); + Set exclude; + for (int i = 0; i < p_exclude.size(); i++) { + exclude.insert(p_exclude[i]); + } + return body_test_motion(p_body, p_from, p_motion, p_infinite_inertia, p_margin, r, p_exclude_raycast_shapes, exclude); } RID PhysicsServer3D::shape_create(ShapeType p_shape) { @@ -608,7 +612,7 @@ void PhysicsServer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("body_set_ray_pickable", "body", "enable"), &PhysicsServer3D::body_set_ray_pickable); - ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "infinite_inertia", "margin", "result"), &PhysicsServer3D::_body_test_motion, DEFVAL(0.001), DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "infinite_inertia", "margin", "result", "exclude_raycast_shapes", "exclude"), &PhysicsServer3D::_body_test_motion, DEFVAL(0.001), DEFVAL(Variant()), DEFVAL(true), DEFVAL(Array())); ClassDB::bind_method(D_METHOD("body_get_direct_state", "body"), &PhysicsServer3D::body_get_direct_state); diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h index 3fb01b301b12f..0d3d5bd4a10bb 100644 --- a/servers/physics_server_3d.h +++ b/servers/physics_server_3d.h @@ -212,7 +212,7 @@ class PhysicsServer3D : public Object { static PhysicsServer3D *singleton; - virtual bool _body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.001, const Ref &p_result = Ref()); + virtual bool _body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.001, const Ref &p_result = Ref(), bool p_exclude_raycast_shapes = true, const Vector &p_exclude = Vector()); protected: static void _bind_methods(); @@ -491,7 +491,7 @@ class PhysicsServer3D : public Object { Variant collider_metadata; }; - virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.001, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true) = 0; + virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin = 0.001, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true, const Set &p_exclude = Set()) = 0; struct SeparationResult { real_t collision_depth;