From 28c4afeb5733f9ca9725ab2a5f4066af8e02b2a6 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Sun, 19 Apr 2015 20:50:55 -0300 Subject: [PATCH] -Rewritten KinematicBody2D::move to MUCH more efficient code. -KinematicBody2D::move now properly recognizes collision exceptions and masks, fixes #1649 -Removed object type masking for KinematicBody2D -Added a test_motion() function to RigidBody2D, allowing simlar behavior to KinematicBody2D::move there. --- demos/2d/kinematic_char/colworld.gd | 3 +- demos/2d/kinematic_char/colworld.scn | Bin 6367 -> 6596 bytes scene/2d/physics_body_2d.cpp | 152 ++---- scene/2d/physics_body_2d.h | 22 +- servers/physics_2d/physics_2d_server_sw.cpp | 12 + servers/physics_2d/physics_2d_server_sw.h | 3 + servers/physics_2d/space_2d_sw.cpp | 504 ++++++++++++++++++++ servers/physics_2d/space_2d_sw.h | 2 + servers/physics_2d_server.cpp | 75 +++ servers/physics_2d_server.h | 50 ++ servers/register_server_types.cpp | 1 + 11 files changed, 692 insertions(+), 132 deletions(-) diff --git a/demos/2d/kinematic_char/colworld.gd b/demos/2d/kinematic_char/colworld.gd index d13ff9236bd..fe2dc30bb6d 100644 --- a/demos/2d/kinematic_char/colworld.gd +++ b/demos/2d/kinematic_char/colworld.gd @@ -14,4 +14,5 @@ func _ready(): func _on_princess_body_enter( body ): #the name of this editor-generated callback is unfortunate - get_node("youwin").show() + if (body.get_name()=="player"): + get_node("youwin").show() diff --git a/demos/2d/kinematic_char/colworld.scn b/demos/2d/kinematic_char/colworld.scn index 7b79a1d887e07370da20522a537ba5b6be161e11..6c73e8b126807ba64f335e95095e3922e0e00719 100644 GIT binary patch delta 4330 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 Kb_EOn;P`JHh}fY3 delta 4139 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 p_result) { + + Physics2DServer::MotionResult *r=NULL; + if (p_result.is_valid()) + r=p_result->get_result_ptr(); + return Physics2DServer::get_singleton()->body_test_motion(get_rid(),p_motion,p_margin,r); + +} + void RigidBody2D::_direct_state_changed(Object *p_state) { //eh.. fuck @@ -791,6 +801,8 @@ void RigidBody2D::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_can_sleep","able_to_sleep"),&RigidBody2D::set_can_sleep); ObjectTypeDB::bind_method(_MD("is_able_to_sleep"),&RigidBody2D::is_able_to_sleep); + ObjectTypeDB::bind_method(_MD("test_motion","motion","margin","result:Physics2DTestMotionResult"),&RigidBody2D::_test_motion,DEFVAL(0.08),DEFVAL(Variant())); + ObjectTypeDB::bind_method(_MD("_direct_state_changed"),&RigidBody2D::_direct_state_changed); ObjectTypeDB::bind_method(_MD("_body_enter_tree"),&RigidBody2D::_body_enter_tree); ObjectTypeDB::bind_method(_MD("_body_exit_tree"),&RigidBody2D::_body_exit_tree); @@ -888,20 +900,25 @@ Variant KinematicBody2D::_get_collider() const { } -bool KinematicBody2D::_ignores_mode(Physics2DServer::BodyMode p_mode) const { - - switch(p_mode) { - case Physics2DServer::BODY_MODE_STATIC: return !collide_static; - case Physics2DServer::BODY_MODE_KINEMATIC: return !collide_kinematic; - case Physics2DServer::BODY_MODE_RIGID: return !collide_rigid; - case Physics2DServer::BODY_MODE_CHARACTER: return !collide_character; - } - - return true; -} - Vector2 KinematicBody2D::move(const Vector2& p_motion) { +#if 1 + Physics2DServer::MotionResult result; + colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(),p_motion,margin,&result); + + collider_metadata=result.collider_metadata; + collider_shape=result.collider_shape; + collider_vel=result.collider_velocity; + collision=result.collision_point; + normal=result.collision_normal; + collider=result.collider_id; + + Matrix32 gt = get_global_transform(); + gt.elements[2]+=result.motion; + set_global_transform(gt); + return result.remainder; + +#else //give me back regular physics engine logic //this is madness //and most people using this function will think @@ -1051,7 +1068,7 @@ Vector2 KinematicBody2D::move(const Vector2& p_motion) { set_global_transform(gt); return p_motion-motion; - +#endif } Vector2 KinematicBody2D::move_to(const Vector2& p_position) { @@ -1059,58 +1076,22 @@ Vector2 KinematicBody2D::move_to(const Vector2& p_position) { return move(p_position-get_global_pos()); } -bool KinematicBody2D::can_move_to(const Vector2& p_position, bool p_discrete) { - - ERR_FAIL_COND_V(!is_inside_tree(),false); - Physics2DDirectSpaceState *dss = Physics2DServer::get_singleton()->space_get_direct_state(get_world_2d()->get_space()); - ERR_FAIL_COND_V(!dss,false); - - uint32_t mask=0; - if (collide_static) - mask|=Physics2DDirectSpaceState::TYPE_MASK_STATIC_BODY; - if (collide_kinematic) - mask|=Physics2DDirectSpaceState::TYPE_MASK_KINEMATIC_BODY; - if (collide_rigid) - mask|=Physics2DDirectSpaceState::TYPE_MASK_RIGID_BODY; - if (collide_character) - mask|=Physics2DDirectSpaceState::TYPE_MASK_CHARACTER_BODY; - - Vector2 motion = p_position-get_global_pos(); - Matrix32 xform=get_global_transform(); - - if (p_discrete) { - - xform.elements[2]+=motion; - motion=Vector2(); - } - - Set exclude; - exclude.insert(get_rid()); - - //fill exclude list.. - for(int i=0;iintersect_shape(get_shape(i)->get_rid(), xform * get_shape_transform(i),motion,0,NULL,0,exclude,get_layer_mask(),mask); - if (col) - return false; - } - - return true; -} - -bool KinematicBody2D::is_colliding() const { +bool KinematicBody2D::test_move(const Vector2& p_motion) { ERR_FAIL_COND_V(!is_inside_tree(),false); - return colliding; + return Physics2DServer::get_singleton()->body_test_motion(get_rid(),p_motion,margin); + + } + Vector2 KinematicBody2D::get_collision_pos() const { ERR_FAIL_COND_V(!colliding,Vector2()); return collision; } + Vector2 KinematicBody2D::get_collision_normal() const { ERR_FAIL_COND_V(!colliding,Vector2()); @@ -1143,43 +1124,10 @@ Variant KinematicBody2D::get_collider_metadata() const { } -void KinematicBody2D::set_collide_with_static_bodies(bool p_enable) { - collide_static=p_enable; -} -bool KinematicBody2D::can_collide_with_static_bodies() const { +bool KinematicBody2D::is_colliding() const{ - return collide_static; -} - -void KinematicBody2D::set_collide_with_rigid_bodies(bool p_enable) { - - collide_rigid=p_enable; - -} -bool KinematicBody2D::can_collide_with_rigid_bodies() const { - - - return collide_rigid; -} - -void KinematicBody2D::set_collide_with_kinematic_bodies(bool p_enable) { - - collide_kinematic=p_enable; - -} -bool KinematicBody2D::can_collide_with_kinematic_bodies() const { - - return collide_kinematic; -} - -void KinematicBody2D::set_collide_with_character_bodies(bool p_enable) { - - collide_character=p_enable; -} -bool KinematicBody2D::can_collide_with_character_bodies() const { - - return collide_character; + return colliding; } void KinematicBody2D::set_collision_margin(float p_margin) { @@ -1198,7 +1146,7 @@ void KinematicBody2D::_bind_methods() { ObjectTypeDB::bind_method(_MD("move","rel_vec"),&KinematicBody2D::move); ObjectTypeDB::bind_method(_MD("move_to","position"),&KinematicBody2D::move_to); - ObjectTypeDB::bind_method(_MD("can_move_to","position","discrete"),&KinematicBody2D::can_move_to,DEFVAL(false)); + ObjectTypeDB::bind_method(_MD("test_move","rel_vec"),&KinematicBody2D::test_move); ObjectTypeDB::bind_method(_MD("is_colliding"),&KinematicBody2D::is_colliding); @@ -1209,26 +1157,9 @@ void KinematicBody2D::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_collider_shape"),&KinematicBody2D::get_collider_shape); ObjectTypeDB::bind_method(_MD("get_collider_metadata"),&KinematicBody2D::get_collider_metadata); - - ObjectTypeDB::bind_method(_MD("set_collide_with_static_bodies","enable"),&KinematicBody2D::set_collide_with_static_bodies); - ObjectTypeDB::bind_method(_MD("can_collide_with_static_bodies"),&KinematicBody2D::can_collide_with_static_bodies); - - ObjectTypeDB::bind_method(_MD("set_collide_with_kinematic_bodies","enable"),&KinematicBody2D::set_collide_with_kinematic_bodies); - ObjectTypeDB::bind_method(_MD("can_collide_with_kinematic_bodies"),&KinematicBody2D::can_collide_with_kinematic_bodies); - - ObjectTypeDB::bind_method(_MD("set_collide_with_rigid_bodies","enable"),&KinematicBody2D::set_collide_with_rigid_bodies); - ObjectTypeDB::bind_method(_MD("can_collide_with_rigid_bodies"),&KinematicBody2D::can_collide_with_rigid_bodies); - - ObjectTypeDB::bind_method(_MD("set_collide_with_character_bodies","enable"),&KinematicBody2D::set_collide_with_character_bodies); - ObjectTypeDB::bind_method(_MD("can_collide_with_character_bodies"),&KinematicBody2D::can_collide_with_character_bodies); - ObjectTypeDB::bind_method(_MD("set_collision_margin","pixels"),&KinematicBody2D::set_collision_margin); ObjectTypeDB::bind_method(_MD("get_collision_margin","pixels"),&KinematicBody2D::get_collision_margin); - ADD_PROPERTY( PropertyInfo(Variant::BOOL,"collide_with/static"),_SCS("set_collide_with_static_bodies"),_SCS("can_collide_with_static_bodies")); - ADD_PROPERTY( PropertyInfo(Variant::BOOL,"collide_with/kinematic"),_SCS("set_collide_with_kinematic_bodies"),_SCS("can_collide_with_kinematic_bodies")); - ADD_PROPERTY( PropertyInfo(Variant::BOOL,"collide_with/rigid"),_SCS("set_collide_with_rigid_bodies"),_SCS("can_collide_with_rigid_bodies")); - ADD_PROPERTY( PropertyInfo(Variant::BOOL,"collide_with/character"),_SCS("set_collide_with_character_bodies"),_SCS("can_collide_with_character_bodies")); ADD_PROPERTY( PropertyInfo(Variant::REAL,"collision/margin",PROPERTY_HINT_RANGE,"0.001,256,0.001"),_SCS("set_collision_margin"),_SCS("get_collision_margin")); @@ -1236,11 +1167,6 @@ void KinematicBody2D::_bind_methods() { KinematicBody2D::KinematicBody2D() : PhysicsBody2D(Physics2DServer::BODY_MODE_KINEMATIC){ - collide_static=true; - collide_rigid=true; - collide_kinematic=true; - collide_character=true; - colliding=false; collider=0; diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index c05a4ff0583..b8cba6e5ba8 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -188,6 +188,7 @@ private: void _body_inout(int p_status, ObjectID p_instance, int p_body_shape,int p_local_shape); void _direct_state_changed(Object *p_state); + bool _test_motion(const Vector2& p_motion,float p_margin=0.08,const Ref& p_result=Ref()); protected: @@ -249,6 +250,8 @@ public: void set_applied_force(const Vector2& p_force); Vector2 get_applied_force() const; + + Array get_colliding_bodies() const; //function for script RigidBody2D(); @@ -266,11 +269,6 @@ class KinematicBody2D : public PhysicsBody2D { OBJ_TYPE(KinematicBody2D,PhysicsBody2D); float margin; - bool collide_static; - bool collide_rigid; - bool collide_kinematic; - bool collide_character; - bool colliding; Vector2 collision; Vector2 normal; @@ -290,7 +288,7 @@ public: Vector2 move(const Vector2& p_motion); Vector2 move_to(const Vector2& p_position); - bool can_move_to(const Vector2& p_position,bool p_discrete=false); + bool test_move(const Vector2& p_motion); bool is_colliding() const; Vector2 get_collision_pos() const; Vector2 get_collision_normal() const; @@ -299,18 +297,6 @@ public: int get_collider_shape() const; Variant get_collider_metadata() const; - void set_collide_with_static_bodies(bool p_enable); - bool can_collide_with_static_bodies() const; - - void set_collide_with_rigid_bodies(bool p_enable); - bool can_collide_with_rigid_bodies() const; - - void set_collide_with_kinematic_bodies(bool p_enable); - bool can_collide_with_kinematic_bodies() const; - - void set_collide_with_character_bodies(bool p_enable); - bool can_collide_with_character_bodies() const; - void set_collision_margin(float p_margin); float get_collision_margin() const; diff --git a/servers/physics_2d/physics_2d_server_sw.cpp b/servers/physics_2d/physics_2d_server_sw.cpp index d8ad59d997e..d0a0ff67d77 100644 --- a/servers/physics_2d/physics_2d_server_sw.cpp +++ b/servers/physics_2d/physics_2d_server_sw.cpp @@ -959,6 +959,18 @@ void Physics2DServerSW::body_set_pickable(RID p_body,bool p_pickable) { } +bool Physics2DServerSW::body_test_motion(RID p_body,const Vector2& p_motion,float p_margin,MotionResult *r_result) { + + Body2DSW *body = body_owner.get(p_body); + ERR_FAIL_COND_V(!body,false); + ERR_FAIL_COND_V(!body->get_space(),false); + ERR_FAIL_COND_V(body->get_space()->is_locked(),false); + + return body->get_space()->test_body_motion(body,p_motion,p_margin,r_result); + +} + + /* JOINT API */ void Physics2DServerSW::joint_set_param(RID p_joint, JointParam p_param, real_t p_value) { diff --git a/servers/physics_2d/physics_2d_server_sw.h b/servers/physics_2d/physics_2d_server_sw.h index 10143bdadb9..50675cbd09f 100644 --- a/servers/physics_2d/physics_2d_server_sw.h +++ b/servers/physics_2d/physics_2d_server_sw.h @@ -223,6 +223,9 @@ public: virtual void body_set_pickable(RID p_body,bool p_pickable); + virtual bool body_test_motion(RID p_body,const Vector2& p_motion,float p_margin=0.001,MotionResult *r_result=NULL); + + /* JOINT API */ virtual void joint_set_param(RID p_joint, JointParam p_param, real_t p_value); diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp index 06441477626..9a1b977bdad 100644 --- a/servers/physics_2d/space_2d_sw.cpp +++ b/servers/physics_2d/space_2d_sw.cpp @@ -556,7 +556,511 @@ Physics2DDirectSpaceStateSW::Physics2DDirectSpaceStateSW() { +bool Space2DSW::test_body_motion(Body2DSW *p_body,const Vector2&p_motion,float p_margin,Physics2DServer::MotionResult *r_result) { + //give me back regular physics engine logic + //this is madness + //and most people using this function will think + //what it does is simpler than using physics + //this took about a week to get right.. + //but is it right? who knows at this point.. + + Rect2 body_aabb; + + for(int i=0;iget_shape_count();i++) { + + if (i==0) + body_aabb=p_body->get_shape_aabb(i); + else + body_aabb=body_aabb.merge(p_body->get_shape_aabb(i)); + } + + body_aabb=body_aabb.grow(p_margin); + + { + //add motion + + Rect2 motion_aabb=body_aabb; + motion_aabb.pos+=p_motion; + body_aabb=body_aabb.merge(motion_aabb); + } + + + int amount = broadphase->cull_aabb(body_aabb,intersection_query_results,INTERSECTION_QUERY_MAX,intersection_query_subindex_results); + + for(int i=0;iget_type()==CollisionObject2DSW::TYPE_AREA) + keep=false; + else if ((static_cast(intersection_query_results[i])->get_layer_mask()&p_body->get_layer_mask())==0) + keep=false; + else if (static_cast(intersection_query_results[i])->has_exception(p_body->get_self()) || p_body->has_exception(intersection_query_results[i]->get_self())) + keep=false; + else if (static_cast(intersection_query_results[i])->is_shape_set_as_trigger(intersection_query_subindex_results[i])) + keep=false; + + if (!keep) { + + if (iget_transform(); + + { + //STEP 1, FREE BODY IF STUCK + + const int max_results = 32; + int recover_attempts=4; + Vector2 sr[max_results*2]; + + do { + + Physics2DServerSW::CollCbkData cbk; + cbk.max=max_results; + cbk.amount=0; + cbk.ptr=sr; + + + CollisionSolver2DSW::CallbackResult cbkres=NULL; + + Physics2DServerSW::CollCbkData *cbkptr=NULL; + cbkptr=&cbk; + cbkres=Physics2DServerSW::_shape_col_cbk; + + bool collided=false; + + + for(int j=0;jget_shape_count();j++) { + if (p_body->is_shape_set_as_trigger(j)) + continue; + + Matrix32 body_shape_xform = body_transform * p_body->get_shape_transform(j); + Shape2DSW *body_shape = p_body->get_shape(j); + for(int i=0;iget_type()==CollisionObject2DSW::TYPE_BODY) { + + const Body2DSW *body=static_cast(col_obj); + cbk.valid_dir=body->get_one_way_collision_direction(); + cbk.valid_depth=body->get_one_way_collision_max_depth(); + } else { + cbk.valid_dir=Vector2(); + cbk.valid_depth=0; + } + + if (CollisionSolver2DSW::solve(body_shape,body_shape_xform,Vector2(),col_obj->get_shape(shape_idx),col_obj->get_transform() * col_obj->get_shape_transform(shape_idx),Vector2(),cbkres,cbkptr,NULL,p_margin)) { + collided=cbk.amount>0; + } + } + } + + + if (!collided) + break; + + Vector2 recover_motion; + + for(int i=0;iget_shape_count();j++) { + + if (p_body->is_shape_set_as_trigger(j)) + continue; + + Matrix32 body_shape_xform = body_transform * p_body->get_shape_transform(j); + Shape2DSW *body_shape = p_body->get_shape(j); + + bool stuck=false; + + float best_safe=1; + float best_unsafe=1; + + for(int i=0;iget_transform() * col_obj->get_shape_transform(shape_idx); + //test initial overlap, does it collide if going all the way? + if (!CollisionSolver2DSW::solve(body_shape,body_shape_xform,p_motion,col_obj->get_shape(shape_idx),col_obj_xform,Vector2() ,NULL,NULL,NULL,0)) { + continue; + } + + + //test initial overlap + if (CollisionSolver2DSW::solve(body_shape,body_shape_xform,Vector2(),col_obj->get_shape(shape_idx),col_obj_xform,Vector2() ,NULL,NULL,NULL,0)) { + + if (col_obj->get_type()==CollisionObject2DSW::TYPE_BODY) { + //if one way collision direction ignore initial overlap + const Body2DSW *body=static_cast(col_obj); + if (body->get_one_way_collision_direction()!=Vector2()) { + continue; + } + } + + stuck=true; + break; + } + + + //just do kinematic solving + float low=0; + float hi=1; + Vector2 mnormal=p_motion.normalized(); + + for(int i=0;i<8;i++) { //steps should be customizable.. + + //Matrix32 xfa = p_xform; + float ofs = (low+hi)*0.5; + + Vector2 sep=mnormal; //important optimization for this to work fast enough + bool collided = CollisionSolver2DSW::solve(body_shape,body_shape_xform,p_motion*ofs,col_obj->get_shape(shape_idx),col_obj_xform,Vector2(),NULL,NULL,&sep,0); + + if (collided) { + + hi=ofs; + } else { + + low=ofs; + } + } + + if (col_obj->get_type()==CollisionObject2DSW::TYPE_BODY) { + + const Body2DSW *body=static_cast(col_obj); + if (body->get_one_way_collision_direction()!=Vector2()) { + + Vector2 cd[2]; + Physics2DServerSW::CollCbkData cbk; + cbk.max=1; + cbk.amount=0; + cbk.ptr=cd; + cbk.valid_dir=body->get_one_way_collision_direction(); + cbk.valid_depth=body->get_one_way_collision_max_depth(); + + Vector2 sep=mnormal; //important optimization for this to work fast enough + bool collided = CollisionSolver2DSW::solve(body_shape,body_shape_xform,p_motion*(hi+contact_max_allowed_penetration),col_obj->get_shape(shape_idx),col_obj_xform,Vector2(),Physics2DServerSW::_shape_col_cbk,&cbk,&sep,0); + if (!collided || cbk.amount==0) { + continue; + } + + } + } + + + if (low=1) { + //not collided + collided=false; + if (r_result) { + + r_result->motion=p_motion+(body_transform.elements[2]-p_body->get_transform().elements[2]); + r_result->remainder=Vector2(); + } + + } else { + + //it collided, let's get the rest info in unsafe advance + Matrix32 ugt = body_transform; + ugt.elements[2]+=p_motion*unsafe; + + _RestCallbackData2D rcd; + rcd.best_len=0; + rcd.best_object=NULL; + rcd.best_shape=0; + + Matrix32 body_shape_xform = ugt * p_body->get_shape_transform(best_shape); + Shape2DSW *body_shape = p_body->get_shape(best_shape); + + + for(int i=0;iget_type()==CollisionObject2DSW::TYPE_BODY) { + + const Body2DSW *body=static_cast(col_obj); + rcd.valid_dir=body->get_one_way_collision_direction(); + rcd.valid_depth=body->get_one_way_collision_max_depth(); + } else { + rcd.valid_dir=Vector2(); + rcd.valid_depth=0; + } + + + rcd.object=col_obj; + rcd.shape=shape_idx; + bool sc = CollisionSolver2DSW::solve(body_shape,body_shape_xform,Vector2(),col_obj->get_shape(shape_idx),col_obj->get_transform() * col_obj->get_shape_transform(shape_idx),Vector2() ,_rest_cbk_result,&rcd,NULL,p_margin); + if (!sc) + continue; + + } + + if (rcd.best_len!=0) { + + if (r_result) { + r_result->collider=rcd.best_object->get_self(); + r_result->collider_id=rcd.best_object->get_instance_id(); + r_result->collider_shape=rcd.best_shape; + r_result->collision_normal=rcd.best_normal; + r_result->collision_point=rcd.best_contact; + r_result->collider_metadata=rcd.best_object->get_shape_metadata(rcd.best_shape); + + const Body2DSW *body = static_cast(rcd.best_object); + Vector2 rel_vec = r_result->collision_point-body->get_transform().get_origin(); + r_result->collider_velocity = Vector2(-body->get_angular_velocity() * rel_vec.y, body->get_angular_velocity() * rel_vec.x) + body->get_linear_velocity(); + + r_result->motion=safe*p_motion+(body_transform.elements[2]-p_body->get_transform().elements[2]); + r_result->remainder=p_motion - safe * p_motion; + } + + collided=true; + } else { + if (r_result) { + + r_result->motion=p_motion+(body_transform.elements[2]-p_body->get_transform().elements[2]); + r_result->remainder=Vector2(); + } + + collided=false; + + } + } + + return collided; + + +#if 0 + //give me back regular physics engine logic + //this is madness + //and most people using this function will think + //what it does is simpler than using physics + //this took about a week to get right.. + //but is it right? who knows at this point.. + + + colliding=false; + ERR_FAIL_COND_V(!is_inside_tree(),Vector2()); + Physics2DDirectSpaceState *dss = Physics2DServer::get_singleton()->space_get_direct_state(get_world_2d()->get_space()); + ERR_FAIL_COND_V(!dss,Vector2()); + const int max_shapes=32; + Vector2 sr[max_shapes*2]; + int res_shapes; + + Set exclude; + exclude.insert(get_rid()); + + + //recover first + int recover_attempts=4; + + bool collided=false; + uint32_t mask=0; + if (collide_static) + mask|=Physics2DDirectSpaceState::TYPE_MASK_STATIC_BODY; + if (collide_kinematic) + mask|=Physics2DDirectSpaceState::TYPE_MASK_KINEMATIC_BODY; + if (collide_rigid) + mask|=Physics2DDirectSpaceState::TYPE_MASK_RIGID_BODY; + if (collide_character) + mask|=Physics2DDirectSpaceState::TYPE_MASK_CHARACTER_BODY; + +// print_line("motion: "+p_motion+" margin: "+rtos(margin)); + + //print_line("margin: "+rtos(margin)); + do { + + //motion recover + for(int i=0;icollide_shape(get_shape(i)->get_rid(), get_global_transform() * get_shape_transform(i),Vector2(),margin,sr,max_shapes,res_shapes,exclude,get_layer_mask(),mask)) + collided=true; + + } + + if (!collided) + break; + + Vector2 recover_motion; + + for(int i=0;icast_motion(get_shape(i)->get_rid(), get_global_transform() * get_shape_transform(i), p_motion, 0,lsafe,lunsafe,exclude,get_layer_mask(),mask); + //print_line("shape: "+itos(i)+" travel:"+rtos(ltravel)); + if (!valid) { + + safe=0; + unsafe=0; + best_shape=i; //sadly it's the best + break; + } + if (lsafe==1.0) { + continue; + } + if (lsafe < safe) { + + safe=lsafe; + unsafe=lunsafe; + best_shape=i; + } + } + + + //print_line("best shape: "+itos(best_shape)+" motion "+p_motion); + + if (safe>=1) { + //not collided + colliding=false; + } else { + + //it collided, let's get the rest info in unsafe advance + Matrix32 ugt = get_global_transform(); + ugt.elements[2]+=p_motion*unsafe; + Physics2DDirectSpaceState::ShapeRestInfo rest_info; + bool c2 = dss->rest_info(get_shape(best_shape)->get_rid(), ugt*get_shape_transform(best_shape), Vector2(), margin,&rest_info,exclude,get_layer_mask(),mask); + if (!c2) { + //should not happen, but floating point precision is so weird.. + + colliding=false; + } else { + + + //print_line("Travel: "+rtos(travel)); + colliding=true; + collision=rest_info.point; + normal=rest_info.normal; + collider=rest_info.collider_id; + collider_vel=rest_info.linear_velocity; + collider_shape=rest_info.shape; + collider_metadata=rest_info.metadata; + } + + } + + Vector2 motion=p_motion*safe; + Matrix32 gt = get_global_transform(); + gt.elements[2]+=motion; + set_global_transform(gt); + + return p_motion-motion; + +#endif + return false; +} diff --git a/servers/physics_2d/space_2d_sw.h b/servers/physics_2d/space_2d_sw.h index d100ada9db9..95a576609eb 100644 --- a/servers/physics_2d/space_2d_sw.h +++ b/servers/physics_2d/space_2d_sw.h @@ -165,6 +165,8 @@ public: int get_collision_pairs() const { return collision_pairs; } + bool test_body_motion(Body2DSW *p_body, const Vector2&p_motion, float p_margin, Physics2DServer::MotionResult *r_result); + Physics2DDirectSpaceStateSW *get_direct_state(); Space2DSW(); diff --git a/servers/physics_2d_server.cpp b/servers/physics_2d_server.cpp index ffd240365b8..16a15617ff4 100644 --- a/servers/physics_2d_server.cpp +++ b/servers/physics_2d_server.cpp @@ -421,13 +421,86 @@ void Physics2DShapeQueryResult::_bind_methods() { } +/////////////////////////////// +/*bool Physics2DTestMotionResult::is_colliding() const { + return colliding; +}*/ +Vector2 Physics2DTestMotionResult::get_motion() const{ + return result.motion; +} +Vector2 Physics2DTestMotionResult::get_motion_remainder() const{ + + return result.remainder; +} + +Vector2 Physics2DTestMotionResult::get_collision_point() const{ + + return result.collision_point; +} +Vector2 Physics2DTestMotionResult::get_collision_normal() const{ + + return result.collision_normal; +} +Vector2 Physics2DTestMotionResult::get_collider_velocity() const{ + + return result.collider_velocity; +} +ObjectID Physics2DTestMotionResult::get_collider_id() const{ + + return result.collider_id; +} +RID Physics2DTestMotionResult::get_collider_rid() const{ + + return result.collider; +} + +Object* Physics2DTestMotionResult::get_collider() const { + return ObjectDB::get_instance(result.collider_id); +} + +int Physics2DTestMotionResult::get_collider_shape() const{ + + return result.collider_shape; +} + +void Physics2DTestMotionResult::_bind_methods() { + + //ObjectTypeDB::bind_method(_MD("is_colliding"),&Physics2DTestMotionResult::is_colliding); + ObjectTypeDB::bind_method(_MD("get_motion"),&Physics2DTestMotionResult::get_motion); + ObjectTypeDB::bind_method(_MD("get_motion_remainder"),&Physics2DTestMotionResult::get_motion_remainder); + ObjectTypeDB::bind_method(_MD("get_collision_point"),&Physics2DTestMotionResult::get_collision_point); + ObjectTypeDB::bind_method(_MD("get_collision_normal"),&Physics2DTestMotionResult::get_collision_normal); + ObjectTypeDB::bind_method(_MD("get_collider_velocity"),&Physics2DTestMotionResult::get_collider_velocity); + ObjectTypeDB::bind_method(_MD("get_collider_id"),&Physics2DTestMotionResult::get_collider_id); + ObjectTypeDB::bind_method(_MD("get_collider_rid"),&Physics2DTestMotionResult::get_collider_rid); + ObjectTypeDB::bind_method(_MD("get_collider"),&Physics2DTestMotionResult::get_collider); + ObjectTypeDB::bind_method(_MD("get_collider_shape"),&Physics2DTestMotionResult::get_collider_shape); + +} + +Physics2DTestMotionResult::Physics2DTestMotionResult(){ + + colliding=false; + result.collider_id=0; + result.collider_shape=0; +} /////////////////////////////////////// + + +bool Physics2DServer::_body_test_motion(RID p_body,const Vector2& p_motion,float p_margin,const Ref& p_result) { + + MotionResult *r=NULL; + if (p_result.is_valid()) + r=p_result->get_result_ptr(); + return body_test_motion(p_body,p_motion,p_margin,r); +} + void Physics2DServer::_bind_methods() { @@ -543,6 +616,8 @@ void Physics2DServer::_bind_methods() { ObjectTypeDB::bind_method(_MD("body_set_force_integration_callback","body","receiver","method"),&Physics2DServer::body_set_force_integration_callback); + ObjectTypeDB::bind_method(_MD("body_test_motion","body","motion","margin","result:Physics2DTestMotionResult"),&Physics2DServer::_body_test_motion,DEFVAL(0.08),DEFVAL(Variant())); + /* JOINT API */ ObjectTypeDB::bind_method(_MD("joint_set_param","joint","param","value"),&Physics2DServer::joint_set_param); diff --git a/servers/physics_2d_server.h b/servers/physics_2d_server.h index a3e65ec28cf..306144c2bab 100644 --- a/servers/physics_2d_server.h +++ b/servers/physics_2d_server.h @@ -230,6 +230,7 @@ public: Physics2DShapeQueryResult(); }; +class Physics2DTestMotionResult; class Physics2DServer : public Object { @@ -237,6 +238,8 @@ class Physics2DServer : public Object { static Physics2DServer * singleton; + virtual bool _body_test_motion(RID p_body,const Vector2& p_motion,float p_margin=0.08,const Ref& p_result=Ref()); + protected: static void _bind_methods(); @@ -468,6 +471,22 @@ public: virtual void body_set_pickable(RID p_body,bool p_pickable)=0; + struct MotionResult { + + Vector2 motion; + Vector2 remainder; + + Vector2 collision_point; + Vector2 collision_normal; + Vector2 collider_velocity; + ObjectID collider_id; + RID collider; + int collider_shape; + Variant collider_metadata; + }; + + virtual bool body_test_motion(RID p_body,const Vector2& p_motion,float p_margin=0.001,MotionResult *r_result=NULL)=0; + /* JOINT API */ enum JointType { @@ -532,6 +551,37 @@ public: ~Physics2DServer(); }; + +class Physics2DTestMotionResult : public Reference { + + OBJ_TYPE( Physics2DTestMotionResult, Reference ); + + Physics2DServer::MotionResult result; + bool colliding; +friend class Physics2DServer; + +protected: + static void _bind_methods(); +public: + + Physics2DServer::MotionResult* get_result_ptr() const { return const_cast(&result); } + + //bool is_colliding() const; + Vector2 get_motion() const; + Vector2 get_motion_remainder() const; + + Vector2 get_collision_point() const; + Vector2 get_collision_normal() const; + Vector2 get_collider_velocity() const; + ObjectID get_collider_id() const; + RID get_collider_rid() const; + Object* get_collider() const; + int get_collider_shape() const; + + Physics2DTestMotionResult(); +}; + + VARIANT_ENUM_CAST( Physics2DServer::ShapeType ); VARIANT_ENUM_CAST( Physics2DServer::SpaceParameter ); VARIANT_ENUM_CAST( Physics2DServer::AreaParameter ); diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index 6ee01f9d434..d35b6e1e5fb 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -55,6 +55,7 @@ void register_server_types() { ObjectTypeDB::register_virtual_type(); ObjectTypeDB::register_virtual_type(); ObjectTypeDB::register_virtual_type(); + ObjectTypeDB::register_virtual_type(); ObjectTypeDB::register_type(); ObjectTypeDB::register_type();