Path: blob/master/modules/godot_physics_3d/godot_space_3d.cpp
10277 views
/**************************************************************************/1/* godot_space_3d.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_3d.h"3132#include "godot_collision_solver_3d.h"33#include "godot_physics_server_3d.h"3435#include "core/config/project_settings.h"36#include "godot_area_pair_3d.h"37#include "godot_body_pair_3d.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(GodotCollisionObject3D *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() == GodotCollisionObject3D::TYPE_AREA && !p_collide_with_areas) {48return false;49}5051if (p_object->get_type() == GodotCollisionObject3D::TYPE_BODY && !p_collide_with_bodies) {52return false;53}5455if (p_object->get_type() == GodotCollisionObject3D::TYPE_SOFT_BODY && !p_collide_with_bodies) {56return false;57}5859return true;60}6162int GodotPhysicsDirectSpaceState3D::intersect_point(const PointParameters &p_parameters, ShapeResult *r_results, int p_result_max) {63ERR_FAIL_COND_V(space->locked, false);64int amount = space->broadphase->cull_point(p_parameters.position, space->intersection_query_results, GodotSpace3D::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results);65int cc = 0;6667//Transform3D ai = p_xform.affine_inverse();6869for (int i = 0; i < amount; i++) {70if (cc >= p_result_max) {71break;72}7374if (!_can_collide_with(space->intersection_query_results[i], p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas)) {75continue;76}7778//area can't be picked by ray (default)7980if (p_parameters.exclude.has(space->intersection_query_results[i]->get_self())) {81continue;82}8384const GodotCollisionObject3D *col_obj = space->intersection_query_results[i];85int shape_idx = space->intersection_query_subindex_results[i];8687Transform3D inv_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);88inv_xform.affine_invert();8990if (!col_obj->get_shape(shape_idx)->intersect_point(inv_xform.xform(p_parameters.position))) {91continue;92}9394r_results[cc].collider_id = col_obj->get_instance_id();95if (r_results[cc].collider_id.is_valid()) {96r_results[cc].collider = ObjectDB::get_instance(r_results[cc].collider_id);97} else {98r_results[cc].collider = nullptr;99}100r_results[cc].rid = col_obj->get_self();101r_results[cc].shape = shape_idx;102103cc++;104}105106return cc;107}108109bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parameters, RayResult &r_result) {110ERR_FAIL_COND_V(space->locked, false);111112Vector3 begin, end;113Vector3 normal;114begin = p_parameters.from;115end = p_parameters.to;116normal = (end - begin).normalized();117118int amount = space->broadphase->cull_segment(begin, end, space->intersection_query_results, GodotSpace3D::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results);119120//todo, create another array that references results, compute AABBs and check closest point to ray origin, sort, and stop evaluating results when beyond first collision121122bool collided = false;123Vector3 res_point, res_normal;124int res_face_index = -1;125int res_shape = -1;126const GodotCollisionObject3D *res_obj = nullptr;127real_t min_d = 1e10;128129for (int i = 0; i < amount; i++) {130if (!_can_collide_with(space->intersection_query_results[i], p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas)) {131continue;132}133134if (p_parameters.pick_ray && !(space->intersection_query_results[i]->is_ray_pickable())) {135continue;136}137138if (p_parameters.exclude.has(space->intersection_query_results[i]->get_self())) {139continue;140}141142const GodotCollisionObject3D *col_obj = space->intersection_query_results[i];143144int shape_idx = space->intersection_query_subindex_results[i];145Transform3D inv_xform = col_obj->get_shape_inv_transform(shape_idx) * col_obj->get_inv_transform();146147Vector3 local_from = inv_xform.xform(begin);148Vector3 local_to = inv_xform.xform(end);149150const GodotShape3D *shape = col_obj->get_shape(shape_idx);151152Vector3 shape_point, shape_normal;153int shape_face_index = -1;154155if (shape->intersect_point(local_from)) {156if (p_parameters.hit_from_inside) {157// Hit shape at starting point.158min_d = 0;159res_point = begin;160res_normal = Vector3();161res_shape = shape_idx;162res_obj = col_obj;163collided = true;164break;165} else {166// Ignore shape when starting inside.167continue;168}169}170171if (shape->intersect_segment(local_from, local_to, shape_point, shape_normal, shape_face_index, p_parameters.hit_back_faces)) {172Transform3D xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);173shape_point = xform.xform(shape_point);174175real_t ld = normal.dot(shape_point);176177if (ld < min_d) {178min_d = ld;179res_point = shape_point;180res_normal = inv_xform.basis.xform_inv(shape_normal).normalized();181res_face_index = shape_face_index;182res_shape = shape_idx;183res_obj = col_obj;184collided = true;185}186}187}188189if (!collided) {190return false;191}192ERR_FAIL_NULL_V(res_obj, false); // Shouldn't happen but silences warning.193194r_result.collider_id = res_obj->get_instance_id();195if (r_result.collider_id.is_valid()) {196r_result.collider = ObjectDB::get_instance(r_result.collider_id);197} else {198r_result.collider = nullptr;199}200r_result.normal = res_normal;201r_result.face_index = res_face_index;202r_result.position = res_point;203r_result.rid = res_obj->get_self();204r_result.shape = res_shape;205206return true;207}208209int GodotPhysicsDirectSpaceState3D::intersect_shape(const ShapeParameters &p_parameters, ShapeResult *r_results, int p_result_max) {210if (p_result_max <= 0) {211return 0;212}213214GodotShape3D *shape = GodotPhysicsServer3D::godot_singleton->shape_owner.get_or_null(p_parameters.shape_rid);215ERR_FAIL_NULL_V(shape, 0);216217AABB aabb = p_parameters.transform.xform(shape->get_aabb());218219int amount = space->broadphase->cull_aabb(aabb, space->intersection_query_results, GodotSpace3D::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results);220221int cc = 0;222223//Transform3D ai = p_xform.affine_inverse();224225for (int i = 0; i < amount; i++) {226if (cc >= p_result_max) {227break;228}229230if (!_can_collide_with(space->intersection_query_results[i], p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas)) {231continue;232}233234//area can't be picked by ray (default)235236if (p_parameters.exclude.has(space->intersection_query_results[i]->get_self())) {237continue;238}239240const GodotCollisionObject3D *col_obj = space->intersection_query_results[i];241int shape_idx = space->intersection_query_subindex_results[i];242243if (!GodotCollisionSolver3D::solve_static(shape, p_parameters.transform, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), nullptr, nullptr, nullptr, p_parameters.margin, 0)) {244continue;245}246247if (r_results) {248r_results[cc].collider_id = col_obj->get_instance_id();249if (r_results[cc].collider_id.is_valid()) {250r_results[cc].collider = ObjectDB::get_instance(r_results[cc].collider_id);251} else {252r_results[cc].collider = nullptr;253}254r_results[cc].rid = col_obj->get_self();255r_results[cc].shape = shape_idx;256}257258cc++;259}260261return cc;262}263264bool GodotPhysicsDirectSpaceState3D::cast_motion(const ShapeParameters &p_parameters, real_t &p_closest_safe, real_t &p_closest_unsafe, ShapeRestInfo *r_info) {265GodotShape3D *shape = GodotPhysicsServer3D::godot_singleton->shape_owner.get_or_null(p_parameters.shape_rid);266ERR_FAIL_NULL_V(shape, false);267268AABB aabb = p_parameters.transform.xform(shape->get_aabb());269aabb = aabb.merge(AABB(aabb.position + p_parameters.motion, aabb.size)); //motion270aabb = aabb.grow(p_parameters.margin);271272int amount = space->broadphase->cull_aabb(aabb, space->intersection_query_results, GodotSpace3D::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results);273274real_t best_safe = 1;275real_t best_unsafe = 1;276277Transform3D xform_inv = p_parameters.transform.affine_inverse();278GodotMotionShape3D mshape;279mshape.shape = shape;280mshape.motion = xform_inv.basis.xform(p_parameters.motion);281282bool best_first = true;283284Vector3 motion_normal = p_parameters.motion.normalized();285286Vector3 closest_A, closest_B;287288for (int i = 0; i < amount; i++) {289if (!_can_collide_with(space->intersection_query_results[i], p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas)) {290continue;291}292293if (p_parameters.exclude.has(space->intersection_query_results[i]->get_self())) {294continue; //ignore excluded295}296297const GodotCollisionObject3D *col_obj = space->intersection_query_results[i];298int shape_idx = space->intersection_query_subindex_results[i];299300Vector3 point_A, point_B;301Vector3 sep_axis = motion_normal;302303Transform3D col_obj_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);304//test initial overlap, does it collide if going all the way?305if (GodotCollisionSolver3D::solve_distance(&mshape, p_parameters.transform, col_obj->get_shape(shape_idx), col_obj_xform, point_A, point_B, aabb, &sep_axis)) {306continue;307}308309//test initial overlap, ignore objects it's inside of.310sep_axis = motion_normal;311312if (!GodotCollisionSolver3D::solve_distance(shape, p_parameters.transform, col_obj->get_shape(shape_idx), col_obj_xform, point_A, point_B, aabb, &sep_axis)) {313continue;314}315316//just do kinematic solving317real_t low = 0.0;318real_t hi = 1.0;319real_t fraction_coeff = 0.5;320for (int j = 0; j < 8; j++) { //steps should be customizable..321real_t fraction = low + (hi - low) * fraction_coeff;322323mshape.motion = xform_inv.basis.xform(p_parameters.motion * fraction);324325Vector3 lA, lB;326Vector3 sep = motion_normal; //important optimization for this to work fast enough327bool collided = !GodotCollisionSolver3D::solve_distance(&mshape, p_parameters.transform, col_obj->get_shape(shape_idx), col_obj_xform, lA, lB, aabb, &sep);328329if (collided) {330hi = fraction;331if ((j == 0) || (low > 0.0)) { // Did it not collide before?332// When alternating or first iteration, use dichotomy.333fraction_coeff = 0.5;334} else {335// When colliding again, converge faster towards low fraction336// for more accurate results with long motions that collide near the start.337fraction_coeff = 0.25;338}339} else {340point_A = lA;341point_B = lB;342low = fraction;343if ((j == 0) || (hi < 1.0)) { // Did it collide before?344// When alternating or first iteration, use dichotomy.345fraction_coeff = 0.5;346} else {347// When not colliding again, converge faster towards high fraction348// for more accurate results with long motions that collide near the end.349fraction_coeff = 0.75;350}351}352}353354if (low < best_safe) {355best_first = true; //force reset356best_safe = low;357best_unsafe = hi;358}359360if (r_info && (best_first || (point_A.distance_squared_to(point_B) < closest_A.distance_squared_to(closest_B) && low <= best_safe))) {361closest_A = point_A;362closest_B = point_B;363r_info->collider_id = col_obj->get_instance_id();364r_info->rid = col_obj->get_self();365r_info->shape = shape_idx;366r_info->point = closest_B;367r_info->normal = (closest_A - closest_B).normalized();368best_first = false;369if (col_obj->get_type() == GodotCollisionObject3D::TYPE_BODY) {370const GodotBody3D *body = static_cast<const GodotBody3D *>(col_obj);371Vector3 rel_vec = closest_B - (body->get_transform().origin + body->get_center_of_mass());372r_info->linear_velocity = body->get_linear_velocity() + (body->get_angular_velocity()).cross(rel_vec);373}374}375}376377p_closest_safe = best_safe;378p_closest_unsafe = best_unsafe;379380return true;381}382383bool GodotPhysicsDirectSpaceState3D::collide_shape(const ShapeParameters &p_parameters, Vector3 *r_results, int p_result_max, int &r_result_count) {384if (p_result_max <= 0) {385return false;386}387388GodotShape3D *shape = GodotPhysicsServer3D::godot_singleton->shape_owner.get_or_null(p_parameters.shape_rid);389ERR_FAIL_NULL_V(shape, false);390391AABB aabb = p_parameters.transform.xform(shape->get_aabb());392aabb = aabb.grow(p_parameters.margin);393394int amount = space->broadphase->cull_aabb(aabb, space->intersection_query_results, GodotSpace3D::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results);395396bool collided = false;397r_result_count = 0;398399GodotPhysicsServer3D::CollCbkData cbk;400cbk.max = p_result_max;401cbk.amount = 0;402cbk.ptr = r_results;403GodotCollisionSolver3D::CallbackResult cbkres = GodotPhysicsServer3D::_shape_col_cbk;404405GodotPhysicsServer3D::CollCbkData *cbkptr = &cbk;406407for (int i = 0; i < amount; i++) {408if (!_can_collide_with(space->intersection_query_results[i], p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas)) {409continue;410}411412const GodotCollisionObject3D *col_obj = space->intersection_query_results[i];413414if (p_parameters.exclude.has(col_obj->get_self())) {415continue;416}417418int shape_idx = space->intersection_query_subindex_results[i];419420if (GodotCollisionSolver3D::solve_static(shape, p_parameters.transform, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), cbkres, cbkptr, nullptr, p_parameters.margin)) {421collided = true;422}423}424425r_result_count = cbk.amount;426427return collided;428}429430struct _RestResultData {431const GodotCollisionObject3D *object = nullptr;432int local_shape = 0;433int shape = 0;434Vector3 contact;435Vector3 normal;436real_t len = 0.0;437};438439struct _RestCallbackData {440const GodotCollisionObject3D *object = nullptr;441int local_shape = 0;442int shape = 0;443444real_t min_allowed_depth = 0.0;445446_RestResultData best_result;447448int max_results = 0;449int result_count = 0;450_RestResultData *other_results = nullptr;451};452453static void _rest_cbk_result(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal, void *p_userdata) {454_RestCallbackData *rd = static_cast<_RestCallbackData *>(p_userdata);455456Vector3 contact_rel = p_point_B - p_point_A;457real_t len = contact_rel.length();458if (len < rd->min_allowed_depth) {459return;460}461462bool is_best_result = (len > rd->best_result.len);463464if (rd->other_results && rd->result_count > 0) {465// Consider as new result by default.466int prev_result_count = rd->result_count++;467468int result_index = 0;469real_t tested_len = is_best_result ? rd->best_result.len : len;470for (; result_index < prev_result_count - 1; ++result_index) {471if (tested_len > rd->other_results[result_index].len) {472// Reusing a previous result.473rd->result_count--;474break;475}476}477478if (result_index < rd->max_results - 1) {479_RestResultData &result = rd->other_results[result_index];480481if (is_best_result) {482// Keep the previous best result as separate result.483result = rd->best_result;484} else {485// Keep this result as separate result.486result.len = len;487result.contact = p_point_B;488result.normal = normal;489result.object = rd->object;490result.shape = rd->shape;491result.local_shape = rd->local_shape;492}493} else {494// Discarding this result.495rd->result_count--;496}497} else if (is_best_result) {498rd->result_count = 1;499}500501if (!is_best_result) {502return;503}504505rd->best_result.len = len;506rd->best_result.contact = p_point_B;507rd->best_result.normal = normal;508rd->best_result.object = rd->object;509rd->best_result.shape = rd->shape;510rd->best_result.local_shape = rd->local_shape;511}512513bool GodotPhysicsDirectSpaceState3D::rest_info(const ShapeParameters &p_parameters, ShapeRestInfo *r_info) {514GodotShape3D *shape = GodotPhysicsServer3D::godot_singleton->shape_owner.get_or_null(p_parameters.shape_rid);515ERR_FAIL_NULL_V(shape, false);516517real_t margin = MAX(p_parameters.margin, TEST_MOTION_MARGIN_MIN_VALUE);518519AABB aabb = p_parameters.transform.xform(shape->get_aabb());520aabb = aabb.grow(margin);521522int amount = space->broadphase->cull_aabb(aabb, space->intersection_query_results, GodotSpace3D::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results);523524_RestCallbackData rcd;525526// Allowed depth can't be lower than motion length, in order to handle contacts at low speed.527real_t motion_length = p_parameters.motion.length();528real_t min_contact_depth = margin * TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR;529rcd.min_allowed_depth = MIN(motion_length, min_contact_depth);530531for (int i = 0; i < amount; i++) {532if (!_can_collide_with(space->intersection_query_results[i], p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas)) {533continue;534}535536const GodotCollisionObject3D *col_obj = space->intersection_query_results[i];537538if (p_parameters.exclude.has(col_obj->get_self())) {539continue;540}541542int shape_idx = space->intersection_query_subindex_results[i];543544rcd.object = col_obj;545rcd.shape = shape_idx;546bool sc = GodotCollisionSolver3D::solve_static(shape, p_parameters.transform, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), _rest_cbk_result, &rcd, nullptr, margin);547if (!sc) {548continue;549}550}551552if (rcd.best_result.len == 0 || !rcd.best_result.object) {553return false;554}555556r_info->collider_id = rcd.best_result.object->get_instance_id();557r_info->shape = rcd.best_result.shape;558r_info->normal = rcd.best_result.normal;559r_info->point = rcd.best_result.contact;560r_info->rid = rcd.best_result.object->get_self();561if (rcd.best_result.object->get_type() == GodotCollisionObject3D::TYPE_BODY) {562const GodotBody3D *body = static_cast<const GodotBody3D *>(rcd.best_result.object);563Vector3 rel_vec = rcd.best_result.contact - (body->get_transform().origin + body->get_center_of_mass());564r_info->linear_velocity = body->get_linear_velocity() + (body->get_angular_velocity()).cross(rel_vec);565566} else {567r_info->linear_velocity = Vector3();568}569570return true;571}572573Vector3 GodotPhysicsDirectSpaceState3D::get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const {574GodotCollisionObject3D *obj = GodotPhysicsServer3D::godot_singleton->area_owner.get_or_null(p_object);575if (!obj) {576obj = GodotPhysicsServer3D::godot_singleton->body_owner.get_or_null(p_object);577}578ERR_FAIL_NULL_V(obj, Vector3());579580ERR_FAIL_COND_V(obj->get_space() != space, Vector3());581582real_t min_distance = 1e20;583Vector3 min_point;584585bool shapes_found = false;586587for (int i = 0; i < obj->get_shape_count(); i++) {588if (obj->is_shape_disabled(i)) {589continue;590}591592Transform3D shape_xform = obj->get_transform() * obj->get_shape_transform(i);593GodotShape3D *shape = obj->get_shape(i);594595Vector3 point = shape->get_closest_point_to(shape_xform.affine_inverse().xform(p_point));596point = shape_xform.xform(point);597598real_t dist = point.distance_to(p_point);599if (dist < min_distance) {600min_distance = dist;601min_point = point;602}603shapes_found = true;604}605606if (!shapes_found) {607return obj->get_transform().origin; //no shapes found, use distance to origin.608} else {609return min_point;610}611}612613GodotPhysicsDirectSpaceState3D::GodotPhysicsDirectSpaceState3D() {614space = nullptr;615}616617////////////////////////////////////////////////////////////////////////////////////////////////////////////618619int GodotSpace3D::_cull_aabb_for_body(GodotBody3D *p_body, const AABB &p_aabb) {620int amount = broadphase->cull_aabb(p_aabb, intersection_query_results, INTERSECTION_QUERY_MAX, intersection_query_subindex_results);621622for (int i = 0; i < amount; i++) {623bool keep = true;624625if (intersection_query_results[i] == p_body) {626keep = false;627} else if (intersection_query_results[i]->get_type() == GodotCollisionObject3D::TYPE_AREA) {628keep = false;629} else if (intersection_query_results[i]->get_type() == GodotCollisionObject3D::TYPE_SOFT_BODY) {630keep = false;631} else if (!p_body->collides_with(static_cast<GodotBody3D *>(intersection_query_results[i]))) {632keep = false;633} else if (static_cast<GodotBody3D *>(intersection_query_results[i])->has_exception(p_body->get_self()) || p_body->has_exception(intersection_query_results[i]->get_self())) {634keep = false;635}636637if (!keep) {638if (i < amount - 1) {639SWAP(intersection_query_results[i], intersection_query_results[amount - 1]);640SWAP(intersection_query_subindex_results[i], intersection_query_subindex_results[amount - 1]);641}642643amount--;644i--;645}646}647648return amount;649}650651bool GodotSpace3D::test_body_motion(GodotBody3D *p_body, const PhysicsServer3D::MotionParameters &p_parameters, PhysicsServer3D::MotionResult *r_result) {652//give me back regular physics engine logic653//this is madness654//and most people using this function will think655//what it does is simpler than using physics656//this took about a week to get right..657//but is it right? who knows at this point..658659ERR_FAIL_COND_V(p_parameters.max_collisions < 0 || p_parameters.max_collisions > PhysicsServer3D::MotionResult::MAX_COLLISIONS, false);660661if (r_result) {662*r_result = PhysicsServer3D::MotionResult();663}664665AABB body_aabb;666bool shapes_found = false;667668for (int i = 0; i < p_body->get_shape_count(); i++) {669if (p_body->is_shape_disabled(i)) {670continue;671}672673if (!shapes_found) {674body_aabb = p_body->get_shape_aabb(i);675shapes_found = true;676} else {677body_aabb = body_aabb.merge(p_body->get_shape_aabb(i));678}679}680681if (!shapes_found) {682if (r_result) {683r_result->travel = p_parameters.motion;684}685686return false;687}688689real_t margin = MAX(p_parameters.margin, TEST_MOTION_MARGIN_MIN_VALUE);690691// Undo the currently transform the physics server is aware of and apply the provided one692body_aabb = p_parameters.from.xform(p_body->get_inv_transform().xform(body_aabb));693body_aabb = body_aabb.grow(margin);694695real_t min_contact_depth = margin * TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR;696697real_t motion_length = p_parameters.motion.length();698Vector3 motion_normal = p_parameters.motion / motion_length;699700Transform3D body_transform = p_parameters.from;701702bool recovered = false;703704{705//STEP 1, FREE BODY IF STUCK706707const int max_results = 32;708int recover_attempts = 4;709Vector3 sr[max_results * 2];710real_t priorities[max_results];711712do {713GodotPhysicsServer3D::CollCbkData cbk;714cbk.max = max_results;715cbk.amount = 0;716cbk.ptr = sr;717718GodotPhysicsServer3D::CollCbkData *cbkptr = &cbk;719GodotCollisionSolver3D::CallbackResult cbkres = GodotPhysicsServer3D::_shape_col_cbk;720int priority_amount = 0;721722bool collided = false;723724int amount = _cull_aabb_for_body(p_body, body_aabb);725726for (int j = 0; j < p_body->get_shape_count(); j++) {727if (p_body->is_shape_disabled(j)) {728continue;729}730731Transform3D body_shape_xform = body_transform * p_body->get_shape_transform(j);732GodotShape3D *body_shape = p_body->get_shape(j);733734for (int i = 0; i < amount; i++) {735const GodotCollisionObject3D *col_obj = intersection_query_results[i];736if (p_parameters.exclude_bodies.has(col_obj->get_self())) {737continue;738}739if (p_parameters.exclude_objects.has(col_obj->get_instance_id())) {740continue;741}742743int shape_idx = intersection_query_subindex_results[i];744745if (GodotCollisionSolver3D::solve_static(body_shape, body_shape_xform, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), cbkres, cbkptr, nullptr, margin)) {746collided = cbk.amount > 0;747}748while (cbk.amount > priority_amount) {749priorities[priority_amount] = col_obj->get_collision_priority();750priority_amount++;751}752}753}754755if (!collided) {756break;757}758759real_t inv_total_weight = 0.0;760for (int i = 0; i < cbk.amount; i++) {761inv_total_weight += priorities[i];762}763inv_total_weight = Math::is_zero_approx(inv_total_weight) ? 1.0 : (real_t)cbk.amount / inv_total_weight;764765recovered = true;766767Vector3 recover_motion;768for (int i = 0; i < cbk.amount; i++) {769Vector3 a = sr[i * 2 + 0];770Vector3 b = sr[i * 2 + 1];771772// Compute plane on b towards a.773Vector3 n = (a - b).normalized();774real_t d = n.dot(b);775776// Compute depth on recovered motion.777real_t depth = n.dot(a + recover_motion) - d;778if (depth > min_contact_depth + CMP_EPSILON) {779// Only recover if there is penetration.780recover_motion -= n * (depth - min_contact_depth) * 0.4 * priorities[i] * inv_total_weight;781}782}783784if (recover_motion == Vector3()) {785collided = false;786break;787}788789body_transform.origin += recover_motion;790body_aabb.position += recover_motion;791792recover_attempts--;793794} while (recover_attempts);795}796797real_t safe = 1.0;798real_t unsafe = 1.0;799int best_shape = -1;800801{802// STEP 2 ATTEMPT MOTION803804AABB motion_aabb = body_aabb;805motion_aabb.position += p_parameters.motion;806motion_aabb = motion_aabb.merge(body_aabb);807808int amount = _cull_aabb_for_body(p_body, motion_aabb);809810for (int j = 0; j < p_body->get_shape_count(); j++) {811if (p_body->is_shape_disabled(j)) {812continue;813}814815GodotShape3D *body_shape = p_body->get_shape(j);816817// Colliding separation rays allows to properly snap to the ground,818// otherwise it's not needed in regular motion.819if (!p_parameters.collide_separation_ray && (body_shape->get_type() == PhysicsServer3D::SHAPE_SEPARATION_RAY)) {820// When slide on slope is on, separation ray shape acts like a regular shape.821if (!static_cast<GodotSeparationRayShape3D *>(body_shape)->get_slide_on_slope()) {822continue;823}824}825826Transform3D body_shape_xform = body_transform * p_body->get_shape_transform(j);827828Transform3D body_shape_xform_inv = body_shape_xform.affine_inverse();829GodotMotionShape3D mshape;830mshape.shape = body_shape;831mshape.motion = body_shape_xform_inv.basis.xform(p_parameters.motion);832833bool stuck = false;834835real_t best_safe = 1;836real_t best_unsafe = 1;837838for (int i = 0; i < amount; i++) {839const GodotCollisionObject3D *col_obj = intersection_query_results[i];840if (p_parameters.exclude_bodies.has(col_obj->get_self())) {841continue;842}843if (p_parameters.exclude_objects.has(col_obj->get_instance_id())) {844continue;845}846847int shape_idx = intersection_query_subindex_results[i];848849//test initial overlap, does it collide if going all the way?850Vector3 point_A, point_B;851Vector3 sep_axis = motion_normal;852853Transform3D col_obj_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);854//test initial overlap, does it collide if going all the way?855if (GodotCollisionSolver3D::solve_distance(&mshape, body_shape_xform, col_obj->get_shape(shape_idx), col_obj_xform, point_A, point_B, motion_aabb, &sep_axis)) {856continue;857}858sep_axis = motion_normal;859860if (!GodotCollisionSolver3D::solve_distance(body_shape, body_shape_xform, col_obj->get_shape(shape_idx), col_obj_xform, point_A, point_B, motion_aabb, &sep_axis)) {861stuck = true;862break;863}864865//just do kinematic solving866real_t low = 0.0;867real_t hi = 1.0;868real_t fraction_coeff = 0.5;869for (int k = 0; k < 8; k++) { //steps should be customizable..870real_t fraction = low + (hi - low) * fraction_coeff;871872mshape.motion = body_shape_xform_inv.basis.xform(p_parameters.motion * fraction);873874Vector3 lA, lB;875Vector3 sep = motion_normal; //important optimization for this to work fast enough876bool collided = !GodotCollisionSolver3D::solve_distance(&mshape, body_shape_xform, col_obj->get_shape(shape_idx), col_obj_xform, lA, lB, motion_aabb, &sep);877878if (collided) {879hi = fraction;880if ((k == 0) || (low > 0.0)) { // Did it not collide before?881// When alternating or first iteration, use dichotomy.882fraction_coeff = 0.5;883} else {884// When colliding again, converge faster towards low fraction885// for more accurate results with long motions that collide near the start.886fraction_coeff = 0.25;887}888} else {889point_A = lA;890point_B = lB;891low = fraction;892if ((k == 0) || (hi < 1.0)) { // Did it collide before?893// When alternating or first iteration, use dichotomy.894fraction_coeff = 0.5;895} else {896// When not colliding again, converge faster towards high fraction897// for more accurate results with long motions that collide near the end.898fraction_coeff = 0.75;899}900}901}902903if (low < best_safe) {904best_safe = low;905best_unsafe = hi;906}907}908909if (stuck) {910safe = 0;911unsafe = 0;912best_shape = j; //sadly it's the best913break;914}915if (best_safe == 1.0) {916continue;917}918if (best_safe < safe) {919safe = best_safe;920unsafe = best_unsafe;921best_shape = j;922}923}924}925926bool collided = false;927if ((p_parameters.recovery_as_collision && recovered) || (safe < 1)) {928if (safe >= 1) {929best_shape = -1; //no best shape with cast, reset to -1930}931932//it collided, let's get the rest info in unsafe advance933Transform3D ugt = body_transform;934ugt.origin += p_parameters.motion * unsafe;935936_RestResultData results[PhysicsServer3D::MotionResult::MAX_COLLISIONS];937938_RestCallbackData rcd;939if (p_parameters.max_collisions > 1) {940rcd.max_results = p_parameters.max_collisions;941rcd.other_results = results;942}943944// Allowed depth can't be lower than motion length, in order to handle contacts at low speed.945rcd.min_allowed_depth = MIN(motion_length, min_contact_depth);946947body_aabb.position += p_parameters.motion * unsafe;948int amount = _cull_aabb_for_body(p_body, body_aabb);949950int from_shape = best_shape != -1 ? best_shape : 0;951int to_shape = best_shape != -1 ? best_shape + 1 : p_body->get_shape_count();952953for (int j = from_shape; j < to_shape; j++) {954if (p_body->is_shape_disabled(j)) {955continue;956}957958Transform3D body_shape_xform = ugt * p_body->get_shape_transform(j);959GodotShape3D *body_shape = p_body->get_shape(j);960961for (int i = 0; i < amount; i++) {962const GodotCollisionObject3D *col_obj = intersection_query_results[i];963if (p_parameters.exclude_bodies.has(col_obj->get_self())) {964continue;965}966if (p_parameters.exclude_objects.has(col_obj->get_instance_id())) {967continue;968}969970int shape_idx = intersection_query_subindex_results[i];971972rcd.object = col_obj;973rcd.shape = shape_idx;974rcd.local_shape = j;975bool sc = GodotCollisionSolver3D::solve_static(body_shape, body_shape_xform, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), _rest_cbk_result, &rcd, nullptr, margin);976if (!sc) {977continue;978}979}980}981982if (rcd.result_count > 0) {983if (r_result) {984for (int collision_index = 0; collision_index < rcd.result_count; ++collision_index) {985const _RestResultData &result = (collision_index > 0) ? rcd.other_results[collision_index - 1] : rcd.best_result;986987PhysicsServer3D::MotionCollision &collision = r_result->collisions[collision_index];988989collision.collider = result.object->get_self();990collision.collider_id = result.object->get_instance_id();991collision.collider_shape = result.shape;992collision.local_shape = result.local_shape;993collision.normal = result.normal;994collision.position = result.contact;995collision.depth = result.len;996997const GodotBody3D *body = static_cast<const GodotBody3D *>(result.object);998999Vector3 rel_vec = result.contact - (body->get_transform().origin + body->get_center_of_mass());1000collision.collider_velocity = body->get_linear_velocity() + (body->get_angular_velocity()).cross(rel_vec);1001collision.collider_angular_velocity = body->get_angular_velocity();1002}10031004r_result->travel = safe * p_parameters.motion;1005r_result->remainder = p_parameters.motion - safe * p_parameters.motion;1006r_result->travel += (body_transform.get_origin() - p_parameters.from.get_origin());10071008r_result->collision_safe_fraction = safe;1009r_result->collision_unsafe_fraction = unsafe;10101011r_result->collision_count = rcd.result_count;1012r_result->collision_depth = rcd.best_result.len;1013}10141015collided = true;1016}1017}10181019if (!collided && r_result) {1020r_result->travel = p_parameters.motion;1021r_result->remainder = Vector3();1022r_result->travel += (body_transform.get_origin() - p_parameters.from.get_origin());10231024r_result->collision_safe_fraction = 1.0;1025r_result->collision_unsafe_fraction = 1.0;1026r_result->collision_depth = 0.0;1027}10281029return collided;1030}10311032// Assumes a valid collision pair, this should have been checked beforehand in the BVH or octree.1033void *GodotSpace3D::_broadphase_pair(GodotCollisionObject3D *A, int p_subindex_A, GodotCollisionObject3D *B, int p_subindex_B, void *p_self) {1034GodotCollisionObject3D::Type type_A = A->get_type();1035GodotCollisionObject3D::Type type_B = B->get_type();1036if (type_A > type_B) {1037SWAP(A, B);1038SWAP(p_subindex_A, p_subindex_B);1039SWAP(type_A, type_B);1040}10411042GodotSpace3D *self = static_cast<GodotSpace3D *>(p_self);10431044self->collision_pairs++;10451046if (type_A == GodotCollisionObject3D::TYPE_AREA) {1047GodotArea3D *area = static_cast<GodotArea3D *>(A);1048if (type_B == GodotCollisionObject3D::TYPE_AREA) {1049GodotArea3D *area_b = static_cast<GodotArea3D *>(B);1050GodotArea2Pair3D *area2_pair = memnew(GodotArea2Pair3D(area_b, p_subindex_B, area, p_subindex_A));1051return area2_pair;1052} else if (type_B == GodotCollisionObject3D::TYPE_SOFT_BODY) {1053GodotSoftBody3D *softbody = static_cast<GodotSoftBody3D *>(B);1054GodotAreaSoftBodyPair3D *soft_area_pair = memnew(GodotAreaSoftBodyPair3D(softbody, p_subindex_B, area, p_subindex_A));1055return soft_area_pair;1056} else {1057GodotBody3D *body = static_cast<GodotBody3D *>(B);1058GodotAreaPair3D *area_pair = memnew(GodotAreaPair3D(body, p_subindex_B, area, p_subindex_A));1059return area_pair;1060}1061} else if (type_A == GodotCollisionObject3D::TYPE_BODY) {1062if (type_B == GodotCollisionObject3D::TYPE_SOFT_BODY) {1063GodotBodySoftBodyPair3D *soft_pair = memnew(GodotBodySoftBodyPair3D(static_cast<GodotBody3D *>(A), p_subindex_A, static_cast<GodotSoftBody3D *>(B)));1064return soft_pair;1065} else {1066GodotBodyPair3D *b = memnew(GodotBodyPair3D(static_cast<GodotBody3D *>(A), p_subindex_A, static_cast<GodotBody3D *>(B), p_subindex_B));1067return b;1068}1069} else {1070// Soft Body/Soft Body, not supported.1071}10721073return nullptr;1074}10751076void GodotSpace3D::_broadphase_unpair(GodotCollisionObject3D *A, int p_subindex_A, GodotCollisionObject3D *B, int p_subindex_B, void *p_data, void *p_self) {1077if (!p_data) {1078return;1079}10801081GodotSpace3D *self = static_cast<GodotSpace3D *>(p_self);1082self->collision_pairs--;1083GodotConstraint3D *c = static_cast<GodotConstraint3D *>(p_data);1084memdelete(c);1085}10861087const SelfList<GodotBody3D>::List &GodotSpace3D::get_active_body_list() const {1088return active_list;1089}10901091void GodotSpace3D::body_add_to_active_list(SelfList<GodotBody3D> *p_body) {1092active_list.add(p_body);1093}10941095void GodotSpace3D::body_remove_from_active_list(SelfList<GodotBody3D> *p_body) {1096active_list.remove(p_body);1097}10981099void GodotSpace3D::body_add_to_mass_properties_update_list(SelfList<GodotBody3D> *p_body) {1100mass_properties_update_list.add(p_body);1101}11021103void GodotSpace3D::body_remove_from_mass_properties_update_list(SelfList<GodotBody3D> *p_body) {1104mass_properties_update_list.remove(p_body);1105}11061107GodotBroadPhase3D *GodotSpace3D::get_broadphase() {1108return broadphase;1109}11101111void GodotSpace3D::add_object(GodotCollisionObject3D *p_object) {1112ERR_FAIL_COND(objects.has(p_object));1113objects.insert(p_object);1114}11151116void GodotSpace3D::remove_object(GodotCollisionObject3D *p_object) {1117ERR_FAIL_COND(!objects.has(p_object));1118objects.erase(p_object);1119}11201121const HashSet<GodotCollisionObject3D *> &GodotSpace3D::get_objects() const {1122return objects;1123}11241125void GodotSpace3D::body_add_to_state_query_list(SelfList<GodotBody3D> *p_body) {1126state_query_list.add(p_body);1127}11281129void GodotSpace3D::body_remove_from_state_query_list(SelfList<GodotBody3D> *p_body) {1130state_query_list.remove(p_body);1131}11321133void GodotSpace3D::area_add_to_monitor_query_list(SelfList<GodotArea3D> *p_area) {1134monitor_query_list.add(p_area);1135}11361137void GodotSpace3D::area_remove_from_monitor_query_list(SelfList<GodotArea3D> *p_area) {1138monitor_query_list.remove(p_area);1139}11401141void GodotSpace3D::area_add_to_moved_list(SelfList<GodotArea3D> *p_area) {1142area_moved_list.add(p_area);1143}11441145void GodotSpace3D::area_remove_from_moved_list(SelfList<GodotArea3D> *p_area) {1146area_moved_list.remove(p_area);1147}11481149const SelfList<GodotArea3D>::List &GodotSpace3D::get_moved_area_list() const {1150return area_moved_list;1151}11521153const SelfList<GodotSoftBody3D>::List &GodotSpace3D::get_active_soft_body_list() const {1154return active_soft_body_list;1155}11561157void GodotSpace3D::soft_body_add_to_active_list(SelfList<GodotSoftBody3D> *p_soft_body) {1158active_soft_body_list.add(p_soft_body);1159}11601161void GodotSpace3D::soft_body_remove_from_active_list(SelfList<GodotSoftBody3D> *p_soft_body) {1162active_soft_body_list.remove(p_soft_body);1163}11641165void GodotSpace3D::call_queries() {1166while (state_query_list.first()) {1167GodotBody3D *b = state_query_list.first()->self();1168state_query_list.remove(state_query_list.first());1169b->call_queries();1170}11711172while (monitor_query_list.first()) {1173GodotArea3D *a = monitor_query_list.first()->self();1174monitor_query_list.remove(monitor_query_list.first());1175a->call_queries();1176}1177}11781179void GodotSpace3D::setup() {1180contact_debug_count = 0;1181while (mass_properties_update_list.first()) {1182mass_properties_update_list.first()->self()->update_mass_properties();1183mass_properties_update_list.remove(mass_properties_update_list.first());1184}1185}11861187void GodotSpace3D::update() {1188broadphase->update();1189}11901191void GodotSpace3D::set_param(PhysicsServer3D::SpaceParameter p_param, real_t p_value) {1192switch (p_param) {1193case PhysicsServer3D::SPACE_PARAM_CONTACT_RECYCLE_RADIUS:1194contact_recycle_radius = p_value;1195break;1196case PhysicsServer3D::SPACE_PARAM_CONTACT_MAX_SEPARATION:1197contact_max_separation = p_value;1198break;1199case PhysicsServer3D::SPACE_PARAM_CONTACT_MAX_ALLOWED_PENETRATION:1200contact_max_allowed_penetration = p_value;1201break;1202case PhysicsServer3D::SPACE_PARAM_CONTACT_DEFAULT_BIAS:1203contact_bias = p_value;1204break;1205case PhysicsServer3D::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_THRESHOLD:1206body_linear_velocity_sleep_threshold = p_value;1207break;1208case PhysicsServer3D::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD:1209body_angular_velocity_sleep_threshold = p_value;1210break;1211case PhysicsServer3D::SPACE_PARAM_BODY_TIME_TO_SLEEP:1212body_time_to_sleep = p_value;1213break;1214case PhysicsServer3D::SPACE_PARAM_SOLVER_ITERATIONS:1215solver_iterations = p_value;1216break;1217}1218}12191220real_t GodotSpace3D::get_param(PhysicsServer3D::SpaceParameter p_param) const {1221switch (p_param) {1222case PhysicsServer3D::SPACE_PARAM_CONTACT_RECYCLE_RADIUS:1223return contact_recycle_radius;1224case PhysicsServer3D::SPACE_PARAM_CONTACT_MAX_SEPARATION:1225return contact_max_separation;1226case PhysicsServer3D::SPACE_PARAM_CONTACT_MAX_ALLOWED_PENETRATION:1227return contact_max_allowed_penetration;1228case PhysicsServer3D::SPACE_PARAM_CONTACT_DEFAULT_BIAS:1229return contact_bias;1230case PhysicsServer3D::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_THRESHOLD:1231return body_linear_velocity_sleep_threshold;1232case PhysicsServer3D::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD:1233return body_angular_velocity_sleep_threshold;1234case PhysicsServer3D::SPACE_PARAM_BODY_TIME_TO_SLEEP:1235return body_time_to_sleep;1236case PhysicsServer3D::SPACE_PARAM_SOLVER_ITERATIONS:1237return solver_iterations;1238}1239return 0;1240}12411242void GodotSpace3D::lock() {1243locked = true;1244}12451246void GodotSpace3D::unlock() {1247locked = false;1248}12491250bool GodotSpace3D::is_locked() const {1251return locked;1252}12531254GodotPhysicsDirectSpaceState3D *GodotSpace3D::get_direct_state() {1255return direct_access;1256}12571258GodotSpace3D::GodotSpace3D() {1259body_linear_velocity_sleep_threshold = GLOBAL_GET("physics/3d/sleep_threshold_linear");1260body_angular_velocity_sleep_threshold = GLOBAL_GET("physics/3d/sleep_threshold_angular");1261body_time_to_sleep = GLOBAL_GET("physics/3d/time_before_sleep");1262solver_iterations = GLOBAL_GET("physics/3d/solver/solver_iterations");1263contact_recycle_radius = GLOBAL_GET("physics/3d/solver/contact_recycle_radius");1264contact_max_separation = GLOBAL_GET("physics/3d/solver/contact_max_separation");1265contact_max_allowed_penetration = GLOBAL_GET("physics/3d/solver/contact_max_allowed_penetration");1266contact_bias = GLOBAL_GET("physics/3d/solver/default_contact_bias");12671268broadphase = GodotBroadPhase3D::create_func();1269broadphase->set_pair_callback(_broadphase_pair, this);1270broadphase->set_unpair_callback(_broadphase_unpair, this);12711272direct_access = memnew(GodotPhysicsDirectSpaceState3D);1273direct_access->space = this;1274}12751276GodotSpace3D::~GodotSpace3D() {1277memdelete(broadphase);1278memdelete(direct_access);1279}128012811282