Path: blob/master/modules/godot_physics_2d/godot_space_2d.cpp
10277 views
/**************************************************************************/1/* godot_space_2d.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "godot_space_2d.h"3132#include "godot_collision_solver_2d.h"33#include "godot_physics_server_2d.h"3435#include "core/config/project_settings.h"36#include "godot_area_pair_2d.h"37#include "godot_body_pair_2d.h"3839#define TEST_MOTION_MARGIN_MIN_VALUE 0.000140#define TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR 0.054142_FORCE_INLINE_ static bool _can_collide_with(GodotCollisionObject2D *p_object, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {43if (!(p_object->get_collision_layer() & p_collision_mask)) {44return false;45}4647if (p_object->get_type() == GodotCollisionObject2D::TYPE_AREA && !p_collide_with_areas) {48return false;49}5051if (p_object->get_type() == GodotCollisionObject2D::TYPE_BODY && !p_collide_with_bodies) {52return false;53}5455return true;56}5758int GodotPhysicsDirectSpaceState2D::intersect_point(const PointParameters &p_parameters, ShapeResult *r_results, int p_result_max) {59if (p_result_max <= 0) {60return 0;61}6263Rect2 aabb;64aabb.position = p_parameters.position - Vector2(0.00001, 0.00001);65aabb.size = Vector2(0.00002, 0.00002);6667int amount = space->broadphase->cull_aabb(aabb, space->intersection_query_results, GodotSpace2D::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results);6869int cc = 0;7071for (int i = 0; i < amount; i++) {72if (!_can_collide_with(space->intersection_query_results[i], p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas)) {73continue;74}7576if (p_parameters.exclude.has(space->intersection_query_results[i]->get_self())) {77continue;78}7980const GodotCollisionObject2D *col_obj = space->intersection_query_results[i];8182if (p_parameters.pick_point && !col_obj->is_pickable()) {83continue;84}8586if (col_obj->get_canvas_instance_id() != p_parameters.canvas_instance_id) {87continue;88}8990int shape_idx = space->intersection_query_subindex_results[i];9192GodotShape2D *shape = col_obj->get_shape(shape_idx);9394Vector2 local_point = (col_obj->get_transform() * col_obj->get_shape_transform(shape_idx)).affine_inverse().xform(p_parameters.position);9596if (!shape->contains_point(local_point)) {97continue;98}99100if (cc >= p_result_max) {101continue;102}103104r_results[cc].collider_id = col_obj->get_instance_id();105if (r_results[cc].collider_id.is_valid()) {106r_results[cc].collider = ObjectDB::get_instance(r_results[cc].collider_id);107}108r_results[cc].rid = col_obj->get_self();109r_results[cc].shape = shape_idx;110111cc++;112}113114return cc;115}116117bool GodotPhysicsDirectSpaceState2D::intersect_ray(const RayParameters &p_parameters, RayResult &r_result) {118ERR_FAIL_COND_V(space->locked, false);119120Vector2 begin, end;121Vector2 normal;122begin = p_parameters.from;123end = p_parameters.to;124normal = (end - begin).normalized();125126int amount = space->broadphase->cull_segment(begin, end, space->intersection_query_results, GodotSpace2D::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results);127128//todo, create another array that references results, compute AABBs and check closest point to ray origin, sort, and stop evaluating results when beyond first collision129130bool collided = false;131Vector2 res_point, res_normal;132int res_shape = -1;133const GodotCollisionObject2D *res_obj = nullptr;134real_t min_d = 1e10;135136for (int i = 0; i < amount; i++) {137if (!_can_collide_with(space->intersection_query_results[i], p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas)) {138continue;139}140141if (p_parameters.exclude.has(space->intersection_query_results[i]->get_self())) {142continue;143}144145const GodotCollisionObject2D *col_obj = space->intersection_query_results[i];146147int shape_idx = space->intersection_query_subindex_results[i];148Transform2D inv_xform = col_obj->get_shape_inv_transform(shape_idx) * col_obj->get_inv_transform();149150Vector2 local_from = inv_xform.xform(begin);151Vector2 local_to = inv_xform.xform(end);152153const GodotShape2D *shape = col_obj->get_shape(shape_idx);154155Vector2 shape_point, shape_normal;156157if (shape->contains_point(local_from)) {158if (p_parameters.hit_from_inside) {159// Hit shape at starting point.160min_d = 0;161res_point = begin;162res_normal = Vector2();163res_shape = shape_idx;164res_obj = col_obj;165collided = true;166break;167} else {168// Ignore shape when starting inside.169continue;170}171}172173if (shape->intersect_segment(local_from, local_to, shape_point, shape_normal)) {174Transform2D xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);175shape_point = xform.xform(shape_point);176177real_t ld = normal.dot(shape_point);178179if (ld < min_d) {180min_d = ld;181res_point = shape_point;182res_normal = inv_xform.basis_xform_inv(shape_normal).normalized();183res_shape = shape_idx;184res_obj = col_obj;185collided = true;186}187}188}189190if (!collided) {191return false;192}193ERR_FAIL_NULL_V(res_obj, false); // Shouldn't happen but silences warning.194195r_result.collider_id = res_obj->get_instance_id();196if (r_result.collider_id.is_valid()) {197r_result.collider = ObjectDB::get_instance(r_result.collider_id);198}199r_result.normal = res_normal;200r_result.position = res_point;201r_result.rid = res_obj->get_self();202r_result.shape = res_shape;203204return true;205}206207int GodotPhysicsDirectSpaceState2D::intersect_shape(const ShapeParameters &p_parameters, ShapeResult *r_results, int p_result_max) {208if (p_result_max <= 0) {209return 0;210}211212GodotShape2D *shape = GodotPhysicsServer2D::godot_singleton->shape_owner.get_or_null(p_parameters.shape_rid);213ERR_FAIL_NULL_V(shape, 0);214215Rect2 aabb = p_parameters.transform.xform(shape->get_aabb());216aabb = aabb.merge(Rect2(aabb.position + p_parameters.motion, aabb.size)); //motion217aabb = aabb.grow(p_parameters.margin);218219int amount = space->broadphase->cull_aabb(aabb, space->intersection_query_results, GodotSpace2D::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results);220221int cc = 0;222223for (int i = 0; i < amount; i++) {224if (cc >= p_result_max) {225break;226}227228if (!_can_collide_with(space->intersection_query_results[i], p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas)) {229continue;230}231232if (p_parameters.exclude.has(space->intersection_query_results[i]->get_self())) {233continue;234}235236const GodotCollisionObject2D *col_obj = space->intersection_query_results[i];237int shape_idx = space->intersection_query_subindex_results[i];238239if (!GodotCollisionSolver2D::solve(shape, p_parameters.transform, p_parameters.motion, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), Vector2(), nullptr, nullptr, nullptr, p_parameters.margin)) {240continue;241}242243r_results[cc].collider_id = col_obj->get_instance_id();244if (r_results[cc].collider_id.is_valid()) {245r_results[cc].collider = ObjectDB::get_instance(r_results[cc].collider_id);246}247r_results[cc].rid = col_obj->get_self();248r_results[cc].shape = shape_idx;249250cc++;251}252253return cc;254}255256bool GodotPhysicsDirectSpaceState2D::cast_motion(const ShapeParameters &p_parameters, real_t &p_closest_safe, real_t &p_closest_unsafe) {257GodotShape2D *shape = GodotPhysicsServer2D::godot_singleton->shape_owner.get_or_null(p_parameters.shape_rid);258ERR_FAIL_NULL_V(shape, false);259260Rect2 aabb = p_parameters.transform.xform(shape->get_aabb());261aabb = aabb.merge(Rect2(aabb.position + p_parameters.motion, aabb.size)); //motion262aabb = aabb.grow(p_parameters.margin);263264int amount = space->broadphase->cull_aabb(aabb, space->intersection_query_results, GodotSpace2D::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results);265266real_t best_safe = 1;267real_t best_unsafe = 1;268269for (int i = 0; i < amount; i++) {270if (!_can_collide_with(space->intersection_query_results[i], p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas)) {271continue;272}273274if (p_parameters.exclude.has(space->intersection_query_results[i]->get_self())) {275continue; //ignore excluded276}277278const GodotCollisionObject2D *col_obj = space->intersection_query_results[i];279int shape_idx = space->intersection_query_subindex_results[i];280281Transform2D col_obj_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);282//test initial overlap, does it collide if going all the way?283if (!GodotCollisionSolver2D::solve(shape, p_parameters.transform, p_parameters.motion, col_obj->get_shape(shape_idx), col_obj_xform, Vector2(), nullptr, nullptr, nullptr, p_parameters.margin)) {284continue;285}286287//test initial overlap, ignore objects it's inside of.288if (GodotCollisionSolver2D::solve(shape, p_parameters.transform, Vector2(), col_obj->get_shape(shape_idx), col_obj_xform, Vector2(), nullptr, nullptr, nullptr, p_parameters.margin)) {289continue;290}291292Vector2 mnormal = p_parameters.motion.normalized();293294//just do kinematic solving295real_t low = 0.0;296real_t hi = 1.0;297real_t fraction_coeff = 0.5;298for (int j = 0; j < 8; j++) { //steps should be customizable..299real_t fraction = low + (hi - low) * fraction_coeff;300301Vector2 sep = mnormal; //important optimization for this to work fast enough302bool collided = GodotCollisionSolver2D::solve(shape, p_parameters.transform, p_parameters.motion * fraction, col_obj->get_shape(shape_idx), col_obj_xform, Vector2(), nullptr, nullptr, &sep, p_parameters.margin);303304if (collided) {305hi = fraction;306if ((j == 0) || (low > 0.0)) { // Did it not collide before?307// When alternating or first iteration, use dichotomy.308fraction_coeff = 0.5;309} else {310// When colliding again, converge faster towards low fraction311// for more accurate results with long motions that collide near the start.312fraction_coeff = 0.25;313}314} else {315low = fraction;316if ((j == 0) || (hi < 1.0)) { // Did it collide before?317// When alternating or first iteration, use dichotomy.318fraction_coeff = 0.5;319} else {320// When not colliding again, converge faster towards high fraction321// for more accurate results with long motions that collide near the end.322fraction_coeff = 0.75;323}324}325}326327if (low < best_safe) {328best_safe = low;329best_unsafe = hi;330}331}332333p_closest_safe = best_safe;334p_closest_unsafe = best_unsafe;335336return true;337}338339bool GodotPhysicsDirectSpaceState2D::collide_shape(const ShapeParameters &p_parameters, Vector2 *r_results, int p_result_max, int &r_result_count) {340if (p_result_max <= 0) {341return false;342}343344GodotShape2D *shape = GodotPhysicsServer2D::godot_singleton->shape_owner.get_or_null(p_parameters.shape_rid);345ERR_FAIL_NULL_V(shape, false);346347Rect2 aabb = p_parameters.transform.xform(shape->get_aabb());348aabb = aabb.merge(Rect2(aabb.position + p_parameters.motion, aabb.size)); //motion349aabb = aabb.grow(p_parameters.margin);350351int amount = space->broadphase->cull_aabb(aabb, space->intersection_query_results, GodotSpace2D::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results);352353bool collided = false;354r_result_count = 0;355356GodotPhysicsServer2D::CollCbkData cbk;357cbk.max = p_result_max;358cbk.amount = 0;359cbk.passed = 0;360cbk.ptr = r_results;361GodotCollisionSolver2D::CallbackResult cbkres = GodotPhysicsServer2D::_shape_col_cbk;362363GodotPhysicsServer2D::CollCbkData *cbkptr = &cbk;364365for (int i = 0; i < amount; i++) {366if (!_can_collide_with(space->intersection_query_results[i], p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas)) {367continue;368}369370const GodotCollisionObject2D *col_obj = space->intersection_query_results[i];371372if (p_parameters.exclude.has(col_obj->get_self())) {373continue;374}375376int shape_idx = space->intersection_query_subindex_results[i];377378cbk.valid_dir = Vector2();379cbk.valid_depth = 0;380381if (GodotCollisionSolver2D::solve(shape, p_parameters.transform, p_parameters.motion, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), Vector2(), cbkres, cbkptr, nullptr, p_parameters.margin)) {382collided = cbk.amount > 0;383}384}385386r_result_count = cbk.amount;387388return collided;389}390391struct _RestCallbackData2D {392const GodotCollisionObject2D *object = nullptr;393const GodotCollisionObject2D *best_object = nullptr;394int local_shape = 0;395int best_local_shape = 0;396int shape = 0;397int best_shape = 0;398Vector2 best_contact;399Vector2 best_normal;400real_t best_len = 0.0;401Vector2 valid_dir;402real_t valid_depth = 0.0;403real_t min_allowed_depth = 0.0;404};405406static void _rest_cbk_result(const Vector2 &p_point_A, const Vector2 &p_point_B, void *p_userdata) {407_RestCallbackData2D *rd = static_cast<_RestCallbackData2D *>(p_userdata);408409Vector2 contact_rel = p_point_B - p_point_A;410real_t len = contact_rel.length();411412if (len < rd->min_allowed_depth) {413return;414}415416if (len <= rd->best_len) {417return;418}419420Vector2 normal = contact_rel / len;421422if (rd->valid_dir != Vector2()) {423if (len > rd->valid_depth) {424return;425}426427if (rd->valid_dir.dot(normal) > -CMP_EPSILON) {428return;429}430}431432rd->best_len = len;433rd->best_contact = p_point_B;434rd->best_normal = normal;435rd->best_object = rd->object;436rd->best_shape = rd->shape;437rd->best_local_shape = rd->local_shape;438}439440bool GodotPhysicsDirectSpaceState2D::rest_info(const ShapeParameters &p_parameters, ShapeRestInfo *r_info) {441GodotShape2D *shape = GodotPhysicsServer2D::godot_singleton->shape_owner.get_or_null(p_parameters.shape_rid);442ERR_FAIL_NULL_V(shape, false);443444real_t margin = MAX(p_parameters.margin, TEST_MOTION_MARGIN_MIN_VALUE);445446Rect2 aabb = p_parameters.transform.xform(shape->get_aabb());447aabb = aabb.merge(Rect2(aabb.position + p_parameters.motion, aabb.size)); //motion448aabb = aabb.grow(margin);449450int amount = space->broadphase->cull_aabb(aabb, space->intersection_query_results, GodotSpace2D::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results);451452_RestCallbackData2D rcd;453454// Allowed depth can't be lower than motion length, in order to handle contacts at low speed.455real_t motion_length = p_parameters.motion.length();456real_t min_contact_depth = margin * TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR;457rcd.min_allowed_depth = MIN(motion_length, min_contact_depth);458459for (int i = 0; i < amount; i++) {460if (!_can_collide_with(space->intersection_query_results[i], p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas)) {461continue;462}463464const GodotCollisionObject2D *col_obj = space->intersection_query_results[i];465466if (p_parameters.exclude.has(col_obj->get_self())) {467continue;468}469470int shape_idx = space->intersection_query_subindex_results[i];471472rcd.valid_dir = Vector2();473rcd.object = col_obj;474rcd.shape = shape_idx;475rcd.local_shape = 0;476bool sc = GodotCollisionSolver2D::solve(shape, p_parameters.transform, p_parameters.motion, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), Vector2(), _rest_cbk_result, &rcd, nullptr, margin);477if (!sc) {478continue;479}480}481482if (rcd.best_len == 0 || !rcd.best_object) {483return false;484}485486r_info->collider_id = rcd.best_object->get_instance_id();487r_info->shape = rcd.best_shape;488r_info->normal = rcd.best_normal;489r_info->point = rcd.best_contact;490r_info->rid = rcd.best_object->get_self();491if (rcd.best_object->get_type() == GodotCollisionObject2D::TYPE_BODY) {492const GodotBody2D *body = static_cast<const GodotBody2D *>(rcd.best_object);493Vector2 rel_vec = r_info->point - (body->get_transform().get_origin() + body->get_center_of_mass());494r_info->linear_velocity = Vector2(-body->get_angular_velocity() * rel_vec.y, body->get_angular_velocity() * rel_vec.x) + body->get_linear_velocity();495496} else {497r_info->linear_velocity = Vector2();498}499500return true;501}502503////////////////////////////////////////////////////////////////////////////////////////////////////////////504505int GodotSpace2D::_cull_aabb_for_body(GodotBody2D *p_body, const Rect2 &p_aabb) {506int amount = broadphase->cull_aabb(p_aabb, intersection_query_results, INTERSECTION_QUERY_MAX, intersection_query_subindex_results);507508for (int i = 0; i < amount; i++) {509bool keep = true;510511if (intersection_query_results[i] == p_body) {512keep = false;513} else if (intersection_query_results[i]->get_type() == GodotCollisionObject2D::TYPE_AREA) {514keep = false;515} else if (!p_body->collides_with(static_cast<GodotBody2D *>(intersection_query_results[i]))) {516keep = false;517} else if (static_cast<GodotBody2D *>(intersection_query_results[i])->has_exception(p_body->get_self()) || p_body->has_exception(intersection_query_results[i]->get_self())) {518keep = false;519}520521if (!keep) {522if (i < amount - 1) {523SWAP(intersection_query_results[i], intersection_query_results[amount - 1]);524SWAP(intersection_query_subindex_results[i], intersection_query_subindex_results[amount - 1]);525}526527amount--;528i--;529}530}531532return amount;533}534535bool GodotSpace2D::test_body_motion(GodotBody2D *p_body, const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult *r_result) {536//give me back regular physics engine logic537//this is madness538//and most people using this function will think539//what it does is simpler than using physics540//this took about a week to get right..541//but is it right? who knows at this point..542543if (r_result) {544r_result->collider_id = ObjectID();545r_result->collider_shape = 0;546}547548Rect2 body_aabb;549550bool shapes_found = false;551552for (int i = 0; i < p_body->get_shape_count(); i++) {553if (p_body->is_shape_disabled(i)) {554continue;555}556557if (!shapes_found) {558body_aabb = p_body->get_shape_aabb(i);559shapes_found = true;560} else {561body_aabb = body_aabb.merge(p_body->get_shape_aabb(i));562}563}564565if (!shapes_found) {566if (r_result) {567*r_result = PhysicsServer2D::MotionResult();568r_result->travel = p_parameters.motion;569}570return false;571}572573real_t margin = MAX(p_parameters.margin, TEST_MOTION_MARGIN_MIN_VALUE);574575// Undo the currently transform the physics server is aware of and apply the provided one576body_aabb = p_parameters.from.xform(p_body->get_inv_transform().xform(body_aabb));577body_aabb = body_aabb.grow(margin);578579static const int max_excluded_shape_pairs = 32;580ExcludedShapeSW excluded_shape_pairs[max_excluded_shape_pairs];581int excluded_shape_pair_count = 0;582583real_t min_contact_depth = margin * TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR;584585real_t motion_length = p_parameters.motion.length();586Vector2 motion_normal = p_parameters.motion / motion_length;587588Transform2D body_transform = p_parameters.from;589590bool recovered = false;591592{593//STEP 1, FREE BODY IF STUCK594595const int max_results = 32;596int recover_attempts = 4;597Vector2 sr[max_results * 2];598real_t priorities[max_results];599600do {601GodotPhysicsServer2D::CollCbkData cbk;602cbk.max = max_results;603cbk.amount = 0;604cbk.passed = 0;605cbk.ptr = sr;606cbk.invalid_by_dir = 0;607excluded_shape_pair_count = 0; //last step is the one valid608609GodotPhysicsServer2D::CollCbkData *cbkptr = &cbk;610GodotCollisionSolver2D::CallbackResult cbkres = GodotPhysicsServer2D::_shape_col_cbk;611int priority_amount = 0;612613bool collided = false;614615int amount = _cull_aabb_for_body(p_body, body_aabb);616617for (int j = 0; j < p_body->get_shape_count(); j++) {618if (p_body->is_shape_disabled(j)) {619continue;620}621622GodotShape2D *body_shape = p_body->get_shape(j);623Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(j);624625for (int i = 0; i < amount; i++) {626const GodotCollisionObject2D *col_obj = intersection_query_results[i];627if (p_parameters.exclude_bodies.has(col_obj->get_self())) {628continue;629}630if (p_parameters.exclude_objects.has(col_obj->get_instance_id())) {631continue;632}633634int shape_idx = intersection_query_subindex_results[i];635636Transform2D col_obj_shape_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);637638if (body_shape->allows_one_way_collision() && col_obj->is_shape_set_as_one_way_collision(shape_idx)) {639cbk.valid_dir = col_obj_shape_xform.columns[1].normalized();640641real_t owc_margin = col_obj->get_shape_one_way_collision_margin(shape_idx);642cbk.valid_depth = MAX(owc_margin, margin); //user specified, but never less than actual margin or it won't work643cbk.invalid_by_dir = 0;644645if (col_obj->get_type() == GodotCollisionObject2D::TYPE_BODY) {646const GodotBody2D *b = static_cast<const GodotBody2D *>(col_obj);647if (b->get_mode() == PhysicsServer2D::BODY_MODE_KINEMATIC || b->get_mode() == PhysicsServer2D::BODY_MODE_RIGID) {648//fix for moving platforms (kinematic and dynamic), margin is increased by how much it moved in the given direction649Vector2 lv = b->get_linear_velocity();650//compute displacement from linear velocity651Vector2 motion = lv * last_step;652real_t motion_len = motion.length();653motion.normalize();654cbk.valid_depth += motion_len * MAX(motion.dot(-cbk.valid_dir), 0.0);655}656}657} else {658cbk.valid_dir = Vector2();659cbk.valid_depth = 0;660cbk.invalid_by_dir = 0;661}662663int current_passed = cbk.passed; //save how many points passed collision664bool did_collide = false;665666GodotShape2D *against_shape = col_obj->get_shape(shape_idx);667if (GodotCollisionSolver2D::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj_shape_xform, Vector2(), cbkres, cbkptr, nullptr, margin)) {668did_collide = cbk.passed > current_passed; //more passed, so collision actually existed669}670while (cbk.amount > priority_amount) {671priorities[priority_amount] = col_obj->get_collision_priority();672priority_amount++;673}674675if (!did_collide && cbk.invalid_by_dir > 0) {676//this shape must be excluded677if (excluded_shape_pair_count < max_excluded_shape_pairs) {678ExcludedShapeSW esp;679esp.local_shape = body_shape;680esp.against_object = col_obj;681esp.against_shape_index = shape_idx;682excluded_shape_pairs[excluded_shape_pair_count++] = esp;683}684}685686if (did_collide) {687collided = true;688}689}690}691692if (!collided) {693break;694}695696real_t inv_total_weight = 0.0;697for (int i = 0; i < cbk.amount; i++) {698inv_total_weight += priorities[i];699}700inv_total_weight = Math::is_zero_approx(inv_total_weight) ? 1.0 : (real_t)cbk.amount / inv_total_weight;701702recovered = true;703704Vector2 recover_motion;705for (int i = 0; i < cbk.amount; i++) {706Vector2 a = sr[i * 2 + 0];707Vector2 b = sr[i * 2 + 1];708709// Compute plane on b towards a.710Vector2 n = (a - b).normalized();711real_t d = n.dot(b);712713// Compute depth on recovered motion.714real_t depth = n.dot(a + recover_motion) - d;715if (depth > min_contact_depth + CMP_EPSILON) {716// Only recover if there is penetration.717recover_motion -= n * (depth - min_contact_depth) * 0.4 * priorities[i] * inv_total_weight;718}719}720721if (recover_motion == Vector2()) {722collided = false;723break;724}725726body_transform.columns[2] += recover_motion;727body_aabb.position += recover_motion;728729recover_attempts--;730731} while (recover_attempts);732}733734real_t safe = 1.0;735real_t unsafe = 1.0;736int best_shape = -1;737738{739// STEP 2 ATTEMPT MOTION740741Rect2 motion_aabb = body_aabb;742motion_aabb.position += p_parameters.motion;743motion_aabb = motion_aabb.merge(body_aabb);744745int amount = _cull_aabb_for_body(p_body, motion_aabb);746747for (int body_shape_idx = 0; body_shape_idx < p_body->get_shape_count(); body_shape_idx++) {748if (p_body->is_shape_disabled(body_shape_idx)) {749continue;750}751752GodotShape2D *body_shape = p_body->get_shape(body_shape_idx);753754// Colliding separation rays allows to properly snap to the ground,755// otherwise it's not needed in regular motion.756if (!p_parameters.collide_separation_ray && (body_shape->get_type() == PhysicsServer2D::SHAPE_SEPARATION_RAY)) {757// When slide on slope is on, separation ray shape acts like a regular shape.758if (!static_cast<GodotSeparationRayShape2D *>(body_shape)->get_slide_on_slope()) {759continue;760}761}762763Transform2D body_shape_xform = body_transform * p_body->get_shape_transform(body_shape_idx);764765bool stuck = false;766767real_t best_safe = 1;768real_t best_unsafe = 1;769770for (int i = 0; i < amount; i++) {771const GodotCollisionObject2D *col_obj = intersection_query_results[i];772if (p_parameters.exclude_bodies.has(col_obj->get_self())) {773continue;774}775if (p_parameters.exclude_objects.has(col_obj->get_instance_id())) {776continue;777}778779int col_shape_idx = intersection_query_subindex_results[i];780GodotShape2D *against_shape = col_obj->get_shape(col_shape_idx);781782bool excluded = false;783784for (int k = 0; k < excluded_shape_pair_count; k++) {785if (excluded_shape_pairs[k].local_shape == body_shape && excluded_shape_pairs[k].against_object == col_obj && excluded_shape_pairs[k].against_shape_index == col_shape_idx) {786excluded = true;787break;788}789}790791if (excluded) {792continue;793}794795Transform2D col_obj_shape_xform = col_obj->get_transform() * col_obj->get_shape_transform(col_shape_idx);796//test initial overlap, does it collide if going all the way?797if (!GodotCollisionSolver2D::solve(body_shape, body_shape_xform, p_parameters.motion, against_shape, col_obj_shape_xform, Vector2(), nullptr, nullptr, nullptr, 0)) {798continue;799}800801//test initial overlap802if (GodotCollisionSolver2D::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj_shape_xform, Vector2(), nullptr, nullptr, nullptr, 0)) {803if (body_shape->allows_one_way_collision() && col_obj->is_shape_set_as_one_way_collision(col_shape_idx)) {804Vector2 direction = col_obj_shape_xform.columns[1].normalized();805if (motion_normal.dot(direction) < 0) {806continue;807}808}809810stuck = true;811break;812}813814//just do kinematic solving815real_t low = 0.0;816real_t hi = 1.0;817real_t fraction_coeff = 0.5;818for (int k = 0; k < 8; k++) { //steps should be customizable..819real_t fraction = low + (hi - low) * fraction_coeff;820821Vector2 sep = motion_normal; //important optimization for this to work fast enough822bool collided = GodotCollisionSolver2D::solve(body_shape, body_shape_xform, p_parameters.motion * fraction, against_shape, col_obj_shape_xform, Vector2(), nullptr, nullptr, &sep, 0);823824if (collided) {825hi = fraction;826if ((k == 0) || (low > 0.0)) { // Did it not collide before?827// When alternating or first iteration, use dichotomy.828fraction_coeff = 0.5;829} else {830// When colliding again, converge faster towards low fraction831// for more accurate results with long motions that collide near the start.832fraction_coeff = 0.25;833}834} else {835low = fraction;836if ((k == 0) || (hi < 1.0)) { // Did it collide before?837// When alternating or first iteration, use dichotomy.838fraction_coeff = 0.5;839} else {840// When not colliding again, converge faster towards high fraction841// for more accurate results with long motions that collide near the end.842fraction_coeff = 0.75;843}844}845}846847if (body_shape->allows_one_way_collision() && col_obj->is_shape_set_as_one_way_collision(col_shape_idx)) {848Vector2 cd[2];849GodotPhysicsServer2D::CollCbkData cbk;850cbk.max = 1;851cbk.amount = 0;852cbk.passed = 0;853cbk.ptr = cd;854cbk.valid_dir = col_obj_shape_xform.columns[1].normalized();855856cbk.valid_depth = 10e20;857858Vector2 sep = motion_normal; //important optimization for this to work fast enough859bool collided = GodotCollisionSolver2D::solve(body_shape, body_shape_xform, p_parameters.motion * (hi + contact_max_allowed_penetration), col_obj->get_shape(col_shape_idx), col_obj_shape_xform, Vector2(), GodotPhysicsServer2D::_shape_col_cbk, &cbk, &sep, 0);860if (!collided || cbk.amount == 0) {861continue;862}863}864865if (low < best_safe) {866best_safe = low;867best_unsafe = hi;868}869}870871if (stuck) {872safe = 0;873unsafe = 0;874best_shape = body_shape_idx; //sadly it's the best875break;876}877if (best_safe == 1.0) {878continue;879}880if (best_safe < safe) {881safe = best_safe;882unsafe = best_unsafe;883best_shape = body_shape_idx;884}885}886}887888bool collided = false;889890if ((p_parameters.recovery_as_collision && recovered) || (safe < 1)) {891if (safe >= 1) {892best_shape = -1; //no best shape with cast, reset to -1893}894895//it collided, let's get the rest info in unsafe advance896Transform2D ugt = body_transform;897ugt.columns[2] += p_parameters.motion * unsafe;898899_RestCallbackData2D rcd;900901// Allowed depth can't be lower than motion length, in order to handle contacts at low speed.902rcd.min_allowed_depth = MIN(motion_length, min_contact_depth);903904body_aabb.position += p_parameters.motion * unsafe;905int amount = _cull_aabb_for_body(p_body, body_aabb);906907int from_shape = best_shape != -1 ? best_shape : 0;908int to_shape = best_shape != -1 ? best_shape + 1 : p_body->get_shape_count();909910for (int j = from_shape; j < to_shape; j++) {911if (p_body->is_shape_disabled(j)) {912continue;913}914915Transform2D body_shape_xform = ugt * p_body->get_shape_transform(j);916GodotShape2D *body_shape = p_body->get_shape(j);917918for (int i = 0; i < amount; i++) {919const GodotCollisionObject2D *col_obj = intersection_query_results[i];920if (p_parameters.exclude_bodies.has(col_obj->get_self())) {921continue;922}923if (p_parameters.exclude_objects.has(col_obj->get_instance_id())) {924continue;925}926927int shape_idx = intersection_query_subindex_results[i];928929GodotShape2D *against_shape = col_obj->get_shape(shape_idx);930931bool excluded = false;932for (int k = 0; k < excluded_shape_pair_count; k++) {933if (excluded_shape_pairs[k].local_shape == body_shape && excluded_shape_pairs[k].against_object == col_obj && excluded_shape_pairs[k].against_shape_index == shape_idx) {934excluded = true;935break;936}937}938if (excluded) {939continue;940}941942Transform2D col_obj_shape_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);943944if (body_shape->allows_one_way_collision() && col_obj->is_shape_set_as_one_way_collision(shape_idx)) {945rcd.valid_dir = col_obj_shape_xform.columns[1].normalized();946947real_t owc_margin = col_obj->get_shape_one_way_collision_margin(shape_idx);948rcd.valid_depth = MAX(owc_margin, margin); //user specified, but never less than actual margin or it won't work949950if (col_obj->get_type() == GodotCollisionObject2D::TYPE_BODY) {951const GodotBody2D *b = static_cast<const GodotBody2D *>(col_obj);952if (b->get_mode() == PhysicsServer2D::BODY_MODE_KINEMATIC || b->get_mode() == PhysicsServer2D::BODY_MODE_RIGID) {953//fix for moving platforms (kinematic and dynamic), margin is increased by how much it moved in the given direction954Vector2 lv = b->get_linear_velocity();955//compute displacement from linear velocity956Vector2 motion = lv * last_step;957real_t motion_len = motion.length();958motion.normalize();959rcd.valid_depth += motion_len * MAX(motion.dot(-rcd.valid_dir), 0.0);960}961}962} else {963rcd.valid_dir = Vector2();964rcd.valid_depth = 0;965}966967rcd.object = col_obj;968rcd.shape = shape_idx;969rcd.local_shape = j;970bool sc = GodotCollisionSolver2D::solve(body_shape, body_shape_xform, Vector2(), against_shape, col_obj_shape_xform, Vector2(), _rest_cbk_result, &rcd, nullptr, margin);971if (!sc) {972continue;973}974}975}976977if (rcd.best_len != 0) {978if (r_result) {979r_result->collider = rcd.best_object->get_self();980r_result->collider_id = rcd.best_object->get_instance_id();981r_result->collider_shape = rcd.best_shape;982r_result->collision_local_shape = rcd.best_local_shape;983r_result->collision_normal = rcd.best_normal;984r_result->collision_point = rcd.best_contact;985r_result->collision_depth = rcd.best_len;986r_result->collision_safe_fraction = safe;987r_result->collision_unsafe_fraction = unsafe;988989const GodotBody2D *body = static_cast<const GodotBody2D *>(rcd.best_object);990Vector2 rel_vec = r_result->collision_point - (body->get_transform().get_origin() + body->get_center_of_mass());991r_result->collider_velocity = Vector2(-body->get_angular_velocity() * rel_vec.y, body->get_angular_velocity() * rel_vec.x) + body->get_linear_velocity();992993r_result->travel = safe * p_parameters.motion;994r_result->remainder = p_parameters.motion - safe * p_parameters.motion;995r_result->travel += (body_transform.get_origin() - p_parameters.from.get_origin());996}997998collided = true;999}1000}10011002if (!collided && r_result) {1003r_result->travel = p_parameters.motion;1004r_result->remainder = Vector2();1005r_result->travel += (body_transform.get_origin() - p_parameters.from.get_origin());1006}10071008return collided;1009}10101011// Assumes a valid collision pair, this should have been checked beforehand in the BVH or octree.1012void *GodotSpace2D::_broadphase_pair(GodotCollisionObject2D *A, int p_subindex_A, GodotCollisionObject2D *B, int p_subindex_B, void *p_self) {1013GodotCollisionObject2D::Type type_A = A->get_type();1014GodotCollisionObject2D::Type type_B = B->get_type();1015if (type_A > type_B) {1016SWAP(A, B);1017SWAP(p_subindex_A, p_subindex_B);1018SWAP(type_A, type_B);1019}10201021GodotSpace2D *self = static_cast<GodotSpace2D *>(p_self);1022self->collision_pairs++;10231024if (type_A == GodotCollisionObject2D::TYPE_AREA) {1025GodotArea2D *area = static_cast<GodotArea2D *>(A);1026if (type_B == GodotCollisionObject2D::TYPE_AREA) {1027GodotArea2D *area_b = static_cast<GodotArea2D *>(B);1028GodotArea2Pair2D *area2_pair = memnew(GodotArea2Pair2D(area_b, p_subindex_B, area, p_subindex_A));1029return area2_pair;1030} else {1031GodotBody2D *body = static_cast<GodotBody2D *>(B);1032GodotAreaPair2D *area_pair = memnew(GodotAreaPair2D(body, p_subindex_B, area, p_subindex_A));1033return area_pair;1034}10351036} else {1037GodotBodyPair2D *b = memnew(GodotBodyPair2D(static_cast<GodotBody2D *>(A), p_subindex_A, static_cast<GodotBody2D *>(B), p_subindex_B));1038return b;1039}1040}10411042void GodotSpace2D::_broadphase_unpair(GodotCollisionObject2D *A, int p_subindex_A, GodotCollisionObject2D *B, int p_subindex_B, void *p_data, void *p_self) {1043if (!p_data) {1044return;1045}10461047GodotSpace2D *self = static_cast<GodotSpace2D *>(p_self);1048self->collision_pairs--;1049GodotConstraint2D *c = static_cast<GodotConstraint2D *>(p_data);1050memdelete(c);1051}10521053const SelfList<GodotBody2D>::List &GodotSpace2D::get_active_body_list() const {1054return active_list;1055}10561057void GodotSpace2D::body_add_to_active_list(SelfList<GodotBody2D> *p_body) {1058active_list.add(p_body);1059}10601061void GodotSpace2D::body_remove_from_active_list(SelfList<GodotBody2D> *p_body) {1062active_list.remove(p_body);1063}10641065void GodotSpace2D::body_add_to_mass_properties_update_list(SelfList<GodotBody2D> *p_body) {1066mass_properties_update_list.add(p_body);1067}10681069void GodotSpace2D::body_remove_from_mass_properties_update_list(SelfList<GodotBody2D> *p_body) {1070mass_properties_update_list.remove(p_body);1071}10721073GodotBroadPhase2D *GodotSpace2D::get_broadphase() {1074return broadphase;1075}10761077void GodotSpace2D::add_object(GodotCollisionObject2D *p_object) {1078ERR_FAIL_COND(objects.has(p_object));1079objects.insert(p_object);1080}10811082void GodotSpace2D::remove_object(GodotCollisionObject2D *p_object) {1083ERR_FAIL_COND(!objects.has(p_object));1084objects.erase(p_object);1085}10861087const HashSet<GodotCollisionObject2D *> &GodotSpace2D::get_objects() const {1088return objects;1089}10901091void GodotSpace2D::body_add_to_state_query_list(SelfList<GodotBody2D> *p_body) {1092state_query_list.add(p_body);1093}10941095void GodotSpace2D::body_remove_from_state_query_list(SelfList<GodotBody2D> *p_body) {1096state_query_list.remove(p_body);1097}10981099void GodotSpace2D::area_add_to_monitor_query_list(SelfList<GodotArea2D> *p_area) {1100monitor_query_list.add(p_area);1101}11021103void GodotSpace2D::area_remove_from_monitor_query_list(SelfList<GodotArea2D> *p_area) {1104monitor_query_list.remove(p_area);1105}11061107void GodotSpace2D::area_add_to_moved_list(SelfList<GodotArea2D> *p_area) {1108area_moved_list.add(p_area);1109}11101111void GodotSpace2D::area_remove_from_moved_list(SelfList<GodotArea2D> *p_area) {1112area_moved_list.remove(p_area);1113}11141115const SelfList<GodotArea2D>::List &GodotSpace2D::get_moved_area_list() const {1116return area_moved_list;1117}11181119void GodotSpace2D::call_queries() {1120while (state_query_list.first()) {1121GodotBody2D *b = state_query_list.first()->self();1122state_query_list.remove(state_query_list.first());1123b->call_queries();1124}11251126while (monitor_query_list.first()) {1127GodotArea2D *a = monitor_query_list.first()->self();1128monitor_query_list.remove(monitor_query_list.first());1129a->call_queries();1130}1131}11321133void GodotSpace2D::setup() {1134contact_debug_count = 0;11351136while (mass_properties_update_list.first()) {1137mass_properties_update_list.first()->self()->update_mass_properties();1138mass_properties_update_list.remove(mass_properties_update_list.first());1139}1140}11411142void GodotSpace2D::update() {1143broadphase->update();1144}11451146void GodotSpace2D::set_param(PhysicsServer2D::SpaceParameter p_param, real_t p_value) {1147switch (p_param) {1148case PhysicsServer2D::SPACE_PARAM_CONTACT_RECYCLE_RADIUS:1149contact_recycle_radius = p_value;1150break;1151case PhysicsServer2D::SPACE_PARAM_CONTACT_MAX_SEPARATION:1152contact_max_separation = p_value;1153break;1154case PhysicsServer2D::SPACE_PARAM_CONTACT_MAX_ALLOWED_PENETRATION:1155contact_max_allowed_penetration = p_value;1156break;1157case PhysicsServer2D::SPACE_PARAM_CONTACT_DEFAULT_BIAS:1158contact_bias = p_value;1159break;1160case PhysicsServer2D::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_THRESHOLD:1161body_linear_velocity_sleep_threshold = p_value;1162break;1163case PhysicsServer2D::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD:1164body_angular_velocity_sleep_threshold = p_value;1165break;1166case PhysicsServer2D::SPACE_PARAM_BODY_TIME_TO_SLEEP:1167body_time_to_sleep = p_value;1168break;1169case PhysicsServer2D::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS:1170constraint_bias = p_value;1171break;1172case PhysicsServer2D::SPACE_PARAM_SOLVER_ITERATIONS:1173solver_iterations = p_value;1174break;1175}1176}11771178real_t GodotSpace2D::get_param(PhysicsServer2D::SpaceParameter p_param) const {1179switch (p_param) {1180case PhysicsServer2D::SPACE_PARAM_CONTACT_RECYCLE_RADIUS:1181return contact_recycle_radius;1182case PhysicsServer2D::SPACE_PARAM_CONTACT_MAX_SEPARATION:1183return contact_max_separation;1184case PhysicsServer2D::SPACE_PARAM_CONTACT_MAX_ALLOWED_PENETRATION:1185return contact_max_allowed_penetration;1186case PhysicsServer2D::SPACE_PARAM_CONTACT_DEFAULT_BIAS:1187return contact_bias;1188case PhysicsServer2D::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_THRESHOLD:1189return body_linear_velocity_sleep_threshold;1190case PhysicsServer2D::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD:1191return body_angular_velocity_sleep_threshold;1192case PhysicsServer2D::SPACE_PARAM_BODY_TIME_TO_SLEEP:1193return body_time_to_sleep;1194case PhysicsServer2D::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS:1195return constraint_bias;1196case PhysicsServer2D::SPACE_PARAM_SOLVER_ITERATIONS:1197return solver_iterations;1198}1199return 0;1200}12011202void GodotSpace2D::lock() {1203locked = true;1204}12051206void GodotSpace2D::unlock() {1207locked = false;1208}12091210bool GodotSpace2D::is_locked() const {1211return locked;1212}12131214GodotPhysicsDirectSpaceState2D *GodotSpace2D::get_direct_state() {1215return direct_access;1216}12171218GodotSpace2D::GodotSpace2D() {1219body_linear_velocity_sleep_threshold = GLOBAL_GET("physics/2d/sleep_threshold_linear");1220body_angular_velocity_sleep_threshold = GLOBAL_GET("physics/2d/sleep_threshold_angular");1221body_time_to_sleep = GLOBAL_GET("physics/2d/time_before_sleep");1222solver_iterations = GLOBAL_GET("physics/2d/solver/solver_iterations");1223contact_recycle_radius = GLOBAL_GET("physics/2d/solver/contact_recycle_radius");1224contact_max_separation = GLOBAL_GET("physics/2d/solver/contact_max_separation");1225contact_max_allowed_penetration = GLOBAL_GET("physics/2d/solver/contact_max_allowed_penetration");1226contact_bias = GLOBAL_GET("physics/2d/solver/default_contact_bias");1227constraint_bias = GLOBAL_GET("physics/2d/solver/default_constraint_bias");12281229broadphase = GodotBroadPhase2D::create_func();1230broadphase->set_pair_callback(_broadphase_pair, this);1231broadphase->set_unpair_callback(_broadphase_unpair, this);12321233direct_access = memnew(GodotPhysicsDirectSpaceState2D);1234direct_access->space = this;1235}12361237GodotSpace2D::~GodotSpace2D() {1238memdelete(broadphase);1239memdelete(direct_access);1240}124112421243