From da159cd258c34389e572dd8d306fa69aaf2e2ca9 Mon Sep 17 00:00:00 2001 From: PouleyKetchoupp Date: Mon, 9 Aug 2021 10:29:39 -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/KinematicBody.xml | 1 + doc/classes/KinematicBody2D.xml | 1 + doc/classes/PhysicsServer.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/3d/physics_body.cpp | 91 +++++++++++++++-------- scene/3d/physics_body.h | 3 +- servers/physics/physics_server_sw.cpp | 4 +- servers/physics/physics_server_sw.h | 2 +- servers/physics/space_sw.cpp | 11 ++- servers/physics/space_sw.h | 2 +- servers/physics_server.cpp | 10 ++- servers/physics_server.h | 4 +- 18 files changed, 111 insertions(+), 56 deletions(-) diff --git a/doc/classes/KinematicBody.xml b/doc/classes/KinematicBody.xml index 0ddd1ba8c0d..abac8d93ace 100644 --- a/doc/classes/KinematicBody.xml +++ b/doc/classes/KinematicBody.xml @@ -95,6 +95,7 @@ [code]floor_max_angle[/code] is the maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall. The default value equals 45 degrees. If [code]infinite_inertia[/code] is [code]true[/code], body will be able to push [RigidBody] nodes, but it won't also detect any collisions with them. If [code]false[/code], it will interact with [RigidBody] nodes like with [StaticBody]. Returns the [code]linear_velocity[/code] vector, rotated and/or scaled 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/KinematicBody2D.xml b/doc/classes/KinematicBody2D.xml index fa893e4531e..1720e166b9a 100644 --- a/doc/classes/KinematicBody2D.xml +++ b/doc/classes/KinematicBody2D.xml @@ -93,6 +93,7 @@ [code]floor_max_angle[/code] is the maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall. The default value equals 45 degrees. If [code]infinite_inertia[/code] is [code]true[/code], body will be able to push [RigidBody2D] nodes, but it won't also detect any collisions with them. If [code]false[/code], it will interact with [RigidBody2D] nodes like with [StaticBody2D]. Returns the [code]linear_velocity[/code] vector, rotated and/or scaled 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/PhysicsServer.xml b/doc/classes/PhysicsServer.xml index c80965d47bf..d017acf2f92 100644 --- a/doc/classes/PhysicsServer.xml +++ b/doc/classes/PhysicsServer.xml @@ -602,6 +602,8 @@ + + Returns [code]true[/code] if a collision would result from moving in the given direction from a given point in space. [PhysicsTestMotionResult] 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 d9d0626ef21..820cc07b494 100644 --- a/modules/bullet/bullet_physics_server.cpp +++ b/modules/bullet/bullet_physics_server.cpp @@ -856,12 +856,12 @@ PhysicsDirectBodyState *BulletPhysicsServer::body_get_direct_state(RID p_body) { return BulletPhysicsDirectBodyState::get_singleton(body); } -bool BulletPhysicsServer::body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result, bool p_exclude_raycast_shapes) { +bool BulletPhysicsServer::body_test_motion(RID p_body, const Transform &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.get(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 BulletPhysicsServer::body_test_ray_separation(RID p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin) { diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h index 32a628c10c0..086097c3852 100644 --- a/modules/bullet/bullet_physics_server.h +++ b/modules/bullet/bullet_physics_server.h @@ -254,7 +254,7 @@ public: // this function only works on physics process, errors and returns null otherwise virtual PhysicsDirectBodyState *body_get_direct_state(RID p_body); - virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true); + virtual bool body_test_motion(RID p_body, const Transform &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()); virtual int body_test_ray_separation(RID p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin = 0.001); /* SOFT BODY API */ diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp index 3dfe6b711b7..fe54ba05f8d 100644 --- a/modules/bullet/godot_result_callbacks.cpp +++ b/modules/bullet/godot_result_callbacks.cpp @@ -142,6 +142,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 5074b4ca614..3da7e63e6e1 100644 --- a/modules/bullet/godot_result_callbacks.h +++ b/modules/bullet/godot_result_callbacks.h @@ -105,11 +105,13 @@ public: 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 1c2ca52b2b2..9a6a11eb4da 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -928,7 +928,7 @@ static Ref red_mat; static Ref blue_mat; #endif -bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer::MotionResult *r_result, bool p_exclude_raycast_shapes) { +bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer::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 @@ -968,7 +968,7 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f 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; } } @@ -1020,7 +1020,7 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f 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(); @@ -1043,7 +1043,7 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f 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) { @@ -1195,7 +1195,7 @@ public: } }; -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; @@ -1264,6 +1264,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 e95972d1637..a18d864780d 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -185,7 +185,7 @@ public: 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 Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer::MotionResult *r_result, bool p_exclude_raycast_shapes); + bool test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer::MotionResult *r_result, bool p_exclude_raycast_shapes, const Set &p_exclude = Set()); int test_ray_separation(RigidBodyBullet *p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, PhysicsServer::SeparationResult *r_results, int p_result_max, float p_margin); private: @@ -213,7 +213,7 @@ private: local_shape_most_recovered(0) {} }; - 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 42770a2e4dc..eaa778c0116 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -1128,7 +1128,7 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const floor_normal = Vector2(); floor_velocity = Vector2(); - if (current_floor_velocity != Vector2()) { + if (current_floor_velocity != Vector2() && on_floor_body.is_valid()) { Collision floor_collision; Set exclude; exclude.insert(on_floor_body); diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index 96ea7cc2e30..8fb5b01aa9c 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -973,14 +973,14 @@ Ref KinematicBody::_move(const Vector3 &p_motion, bool p_inf return Ref(); } -bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes, bool p_test_only, bool p_cancel_sliding) { +bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes, bool p_test_only, bool p_cancel_sliding, const Set &p_exclude) { if (sync_to_physics) { ERR_PRINT("Functions move_and_slide and move_and_collide do not work together with 'sync to physics' option. Please read the documentation."); } Transform gt = get_global_transform(); PhysicsServer::MotionResult result; - bool colliding = PhysicsServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, &result, p_exclude_raycast_shapes); + bool colliding = PhysicsServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, &result, p_exclude_raycast_shapes, p_exclude); // Restore direction of motion to be along original motion, // in order to avoid sliding due to recovery, @@ -1061,8 +1061,11 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve } } + // 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(); + 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. PhysicsDirectBodyState *bs = PhysicsServer::get_singleton()->body_get_direct_state(on_floor_body); if (bs) { @@ -1072,17 +1075,26 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve } } - // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky - Vector3 motion = (current_floor_velocity + body_velocity) * (Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time()); - + colliders.clear(); on_floor = false; - on_floor_body = RID(); on_ceiling = false; on_wall = false; - colliders.clear(); floor_normal = Vector3(); floor_velocity = Vector3(); + if (current_floor_velocity != Vector3() && on_floor_body.is_valid()) { + Collision floor_collision; + Set exclude; + exclude.insert(on_floor_body); + if (move_and_collide(current_floor_velocity * delta, p_infinite_inertia, floor_collision, true, false, false, exclude)) { + colliders.push_back(floor_collision); + _set_collision_direction(floor_collision, up_direction, p_floor_max_angle); + } + } + + on_floor_body = RID(); + Vector3 motion = body_velocity * delta; + // No sliding on first attempt to keep floor motion stable when possible, // when stop on slope is enabled. bool sliding_enabled = !p_stop_on_slope; @@ -1110,33 +1122,18 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve colliders.push_back(collision); - if (up_direction == Vector3()) { - //all is a wall - on_wall = true; - } else { - if (Math::acos(collision.normal.dot(up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor + _set_collision_direction(collision, up_direction, p_floor_max_angle); - on_floor = true; - floor_normal = collision.normal; - on_floor_body = collision.collider_rid; - floor_velocity = collision.collider_vel; - - if (p_stop_on_slope) { - if ((body_velocity_normal + up_direction).length() < 0.01) { - Transform gt = get_global_transform(); - if (collision.travel.length() > margin) { - gt.origin -= collision.travel.slide(up_direction); - } else { - gt.origin -= collision.travel; - } - set_global_transform(gt); - return Vector3(); - } + if (on_floor && p_stop_on_slope) { + if ((body_velocity_normal + up_direction).length() < 0.01) { + Transform gt = get_global_transform(); + if (collision.travel.length() > margin) { + gt.origin -= collision.travel.slide(up_direction); + } else { + gt.origin -= collision.travel; } - } else if (Math::acos(collision.normal.dot(-up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling - on_ceiling = true; - } else { - on_wall = true; + set_global_transform(gt); + return Vector3(); } } @@ -1162,6 +1159,11 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve } } + if (!on_floor && !on_wall) { + // Add last platform velocity when just left a moving platform. + return body_velocity + current_floor_velocity; + } + return body_velocity; } @@ -1207,6 +1209,29 @@ Vector3 KinematicBody::move_and_slide_with_snap(const Vector3 &p_linear_velocity return ret; } +void KinematicBody::_set_collision_direction(const Collision &p_collision, const Vector3 &p_up_direction, float p_floor_max_angle) { + on_floor = false; + on_ceiling = false; + on_wall = false; + if (p_up_direction == Vector3()) { + //all is a wall + on_wall = true; + } else { + if (Math::acos(p_collision.normal.dot(p_up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor + on_floor = true; + floor_normal = p_collision.normal; + on_floor_body = p_collision.collider_rid; + floor_velocity = p_collision.collider_vel; + } else if (Math::acos(p_collision.normal.dot(-p_up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling + on_ceiling = true; + } else { + on_wall = true; + on_floor_body = p_collision.collider_rid; + floor_velocity = p_collision.collider_vel; + } + } +} + bool KinematicBody::is_on_floor() const { return on_floor; } diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index eda958be065..02200f96a0f 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -300,13 +300,14 @@ private: Transform last_valid_transform; void _direct_state_changed(Object *p_state); + void _set_collision_direction(const Collision &p_collision, const Vector3 &p_up_direction, float p_floor_max_angle); protected: void _notification(int p_what); static void _bind_methods(); public: - bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision, 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, Collision &r_collision, 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 Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia); bool separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision); diff --git a/servers/physics/physics_server_sw.cpp b/servers/physics/physics_server_sw.cpp index 690d5bf72e7..825bca2591c 100644 --- a/servers/physics/physics_server_sw.cpp +++ b/servers/physics/physics_server_sw.cpp @@ -860,7 +860,7 @@ bool PhysicsServerSW::body_is_ray_pickable(RID p_body) const { return body->is_ray_pickable(); } -bool PhysicsServerSW::body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result, bool p_exclude_raycast_shapes) { +bool PhysicsServerSW::body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result, bool p_exclude_raycast_shapes, const Set &p_exclude) { BodySW *body = body_owner.get(p_body); ERR_FAIL_COND_V(!body, false); ERR_FAIL_COND_V(!body->get_space(), false); @@ -868,7 +868,7 @@ bool PhysicsServerSW::body_test_motion(RID p_body, const Transform &p_from, cons _update_shapes(); - return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, body->get_kinematic_margin(), r_result, p_exclude_raycast_shapes); + return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, body->get_kinematic_margin(), r_result, p_exclude_raycast_shapes, p_exclude); } int PhysicsServerSW::body_test_ray_separation(RID p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin) { diff --git a/servers/physics/physics_server_sw.h b/servers/physics/physics_server_sw.h index fdead050010..3a8fede2ab8 100644 --- a/servers/physics/physics_server_sw.h +++ b/servers/physics/physics_server_sw.h @@ -233,7 +233,7 @@ public: virtual void body_set_ray_pickable(RID p_body, bool p_enable); virtual bool body_is_ray_pickable(RID p_body) const; - virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true); + virtual bool body_test_motion(RID p_body, const Transform &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()); virtual int body_test_ray_separation(RID p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin = 0.001); // this function only works on physics process, errors and returns null otherwise diff --git a/servers/physics/space_sw.cpp b/servers/physics/space_sw.cpp index bc57f342835..7ecc153a94a 100644 --- a/servers/physics/space_sw.cpp +++ b/servers/physics/space_sw.cpp @@ -712,7 +712,7 @@ int SpaceSW::test_body_ray_separation(BodySW *p_body, const Transform &p_transfo return rays_found; } -bool SpaceSW::test_body_motion(BodySW *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin, PhysicsServer::MotionResult *r_result, bool p_exclude_raycast_shapes) { +bool SpaceSW::test_body_motion(BodySW *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin, PhysicsServer::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 @@ -793,6 +793,9 @@ bool SpaceSW::test_body_motion(BodySW *p_body, const Transform &p_from, const Ve for (int i = 0; i < amount; i++) { const CollisionObjectSW *col_obj = intersection_query_results[i]; + if (p_exclude.has(col_obj->get_self())) { + continue; + } int shape_idx = intersection_query_subindex_results[i]; if (CollisionObjectSW::TYPE_BODY == col_obj->get_type()) { @@ -882,6 +885,9 @@ bool SpaceSW::test_body_motion(BodySW *p_body, const Transform &p_from, const Ve for (int i = 0; i < amount; i++) { const CollisionObjectSW *col_obj = intersection_query_results[i]; + if (p_exclude.has(col_obj->get_self())) { + continue; + } int shape_idx = intersection_query_subindex_results[i]; if (CollisionObjectSW::TYPE_BODY == col_obj->get_type()) { @@ -1008,6 +1014,9 @@ bool SpaceSW::test_body_motion(BodySW *p_body, const Transform &p_from, const Ve for (int i = 0; i < amount; i++) { const CollisionObjectSW *col_obj = intersection_query_results[i]; + if (p_exclude.has(col_obj->get_self())) { + continue; + } int shape_idx = intersection_query_subindex_results[i]; if (CollisionObjectSW::TYPE_BODY == col_obj->get_type()) { diff --git a/servers/physics/space_sw.h b/servers/physics/space_sw.h index 6978bd3e1af..6bbd3b9dd29 100644 --- a/servers/physics/space_sw.h +++ b/servers/physics/space_sw.h @@ -199,7 +199,7 @@ public: uint64_t get_elapsed_time(ElapsedTime p_time) const { return elapsed_time[p_time]; } int test_body_ray_separation(BodySW *p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, PhysicsServer::SeparationResult *r_results, int p_result_max, real_t p_margin); - bool test_body_motion(BodySW *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin, PhysicsServer::MotionResult *r_result, bool p_exclude_raycast_shapes); + bool test_body_motion(BodySW *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin, PhysicsServer::MotionResult *r_result, bool p_exclude_raycast_shapes, const Set &p_exclude = Set()); SpaceSW(); ~SpaceSW(); diff --git a/servers/physics_server.cpp b/servers/physics_server.cpp index 591652e7380..89f9f81d3d9 100644 --- a/servers/physics_server.cpp +++ b/servers/physics_server.cpp @@ -436,12 +436,16 @@ void PhysicsTestMotionResult::_bind_methods() { /////////////////////////////////////// -bool PhysicsServer::_body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, const Ref &p_result) { +bool PhysicsServer::_body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, 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, 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, r, p_exclude_raycast_shapes, exclude); } void PhysicsServer::_bind_methods() { @@ -565,7 +569,7 @@ void PhysicsServer::_bind_methods() { ClassDB::bind_method(D_METHOD("body_set_ray_pickable", "body", "enable"), &PhysicsServer::body_set_ray_pickable); ClassDB::bind_method(D_METHOD("body_is_ray_pickable", "body"), &PhysicsServer::body_is_ray_pickable); - ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "infinite_inertia", "result"), &PhysicsServer::_body_test_motion, DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("body_test_motion", "body", "from", "motion", "infinite_inertia", "result", "exclude_raycast_shapes", "exclude"), &PhysicsServer::_body_test_motion, DEFVAL(Variant()), DEFVAL(true), DEFVAL(Array())); ClassDB::bind_method(D_METHOD("body_get_direct_state", "body"), &PhysicsServer::body_get_direct_state); diff --git a/servers/physics_server.h b/servers/physics_server.h index 2580b313a3f..ea7a342b066 100644 --- a/servers/physics_server.h +++ b/servers/physics_server.h @@ -201,7 +201,7 @@ class PhysicsServer : public Object { static PhysicsServer *singleton; - virtual bool _body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, const Ref &p_result = Ref()); + virtual bool _body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, const Ref &p_result = Ref(), bool p_exclude_raycast_shapes = true, const Vector &p_exclude = Vector()); protected: static void _bind_methods(); @@ -473,7 +473,7 @@ public: Variant collider_metadata; }; - virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true) = 0; + virtual bool body_test_motion(RID p_body, const Transform &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()) = 0; struct SeparationResult { float collision_depth;