Path: blob/master/modules/godot_physics_2d/godot_collision_solver_2d_sat.cpp
10277 views
/**************************************************************************/1/* godot_collision_solver_2d_sat.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_collision_solver_2d_sat.h"3132#include "core/math/geometry_2d.h"3334struct _CollectorCallback2D {35GodotCollisionSolver2D::CallbackResult callback = nullptr;36void *userdata = nullptr;37bool swap = false;38bool collided = false;39Vector2 normal;40Vector2 *sep_axis = nullptr;4142_FORCE_INLINE_ void call(const Vector2 &p_point_A, const Vector2 &p_point_B) {43if (swap) {44callback(p_point_B, p_point_A, userdata);45} else {46callback(p_point_A, p_point_B, userdata);47}48}49};5051typedef void (*GenerateContactsFunc)(const Vector2 *, int, const Vector2 *, int, _CollectorCallback2D *);5253_FORCE_INLINE_ static void _generate_contacts_point_point(const Vector2 *p_points_A, int p_point_count_A, const Vector2 *p_points_B, int p_point_count_B, _CollectorCallback2D *p_collector) {54#ifdef DEBUG_ENABLED55ERR_FAIL_COND(p_point_count_A != 1);56ERR_FAIL_COND(p_point_count_B != 1);57#endif5859p_collector->call(*p_points_A, *p_points_B);60}6162_FORCE_INLINE_ static void _generate_contacts_point_edge(const Vector2 *p_points_A, int p_point_count_A, const Vector2 *p_points_B, int p_point_count_B, _CollectorCallback2D *p_collector) {63#ifdef DEBUG_ENABLED64ERR_FAIL_COND(p_point_count_A != 1);65ERR_FAIL_COND(p_point_count_B != 2);66#endif6768Vector2 closest_B = Geometry2D::get_closest_point_to_segment_uncapped(*p_points_A, p_points_B[0], p_points_B[1]);69p_collector->call(*p_points_A, closest_B);70}7172struct _generate_contacts_Pair {73bool a = false;74int idx = 0;75real_t d = 0.0;76_FORCE_INLINE_ bool operator<(const _generate_contacts_Pair &l) const { return d < l.d; }77};7879_FORCE_INLINE_ static void _generate_contacts_edge_edge(const Vector2 *p_points_A, int p_point_count_A, const Vector2 *p_points_B, int p_point_count_B, _CollectorCallback2D *p_collector) {80#ifdef DEBUG_ENABLED81ERR_FAIL_COND(p_point_count_A != 2);82ERR_FAIL_COND(p_point_count_B != 2); // circle is actually a 4x3 matrix83#endif8485Vector2 n = p_collector->normal;86Vector2 t = n.orthogonal();87real_t dA = n.dot(p_points_A[0]);88real_t dB = n.dot(p_points_B[0]);8990_generate_contacts_Pair dvec[4];9192dvec[0].d = t.dot(p_points_A[0]);93dvec[0].a = true;94dvec[0].idx = 0;95dvec[1].d = t.dot(p_points_A[1]);96dvec[1].a = true;97dvec[1].idx = 1;98dvec[2].d = t.dot(p_points_B[0]);99dvec[2].a = false;100dvec[2].idx = 0;101dvec[3].d = t.dot(p_points_B[1]);102dvec[3].a = false;103dvec[3].idx = 1;104105SortArray<_generate_contacts_Pair> sa;106sa.sort(dvec, 4);107108for (int i = 1; i <= 2; i++) {109if (dvec[i].a) {110Vector2 a = p_points_A[dvec[i].idx];111Vector2 b = n.plane_project(dB, a);112if (n.dot(a) > n.dot(b) - CMP_EPSILON) {113continue;114}115p_collector->call(a, b);116} else {117Vector2 b = p_points_B[dvec[i].idx];118Vector2 a = n.plane_project(dA, b);119if (n.dot(a) > n.dot(b) - CMP_EPSILON) {120continue;121}122p_collector->call(a, b);123}124}125}126127static void _generate_contacts_from_supports(const Vector2 *p_points_A, int p_point_count_A, const Vector2 *p_points_B, int p_point_count_B, _CollectorCallback2D *p_collector) {128#ifdef DEBUG_ENABLED129ERR_FAIL_COND(p_point_count_A < 1);130ERR_FAIL_COND(p_point_count_B < 1);131#endif132133static const GenerateContactsFunc generate_contacts_func_table[2][2] = {134{135_generate_contacts_point_point,136_generate_contacts_point_edge,137},138{139nullptr,140_generate_contacts_edge_edge,141}142};143144int pointcount_B = 0;145int pointcount_A = 0;146const Vector2 *points_A = nullptr;147const Vector2 *points_B = nullptr;148149if (p_point_count_A > p_point_count_B) {150//swap151p_collector->swap = !p_collector->swap;152p_collector->normal = -p_collector->normal;153154pointcount_B = p_point_count_A;155pointcount_A = p_point_count_B;156points_A = p_points_B;157points_B = p_points_A;158} else {159pointcount_B = p_point_count_B;160pointcount_A = p_point_count_A;161points_A = p_points_A;162points_B = p_points_B;163}164165int version_A = (pointcount_A > 2 ? 2 : pointcount_A) - 1;166int version_B = (pointcount_B > 2 ? 2 : pointcount_B) - 1;167168GenerateContactsFunc contacts_func = generate_contacts_func_table[version_A][version_B];169ERR_FAIL_NULL(contacts_func);170contacts_func(points_A, pointcount_A, points_B, pointcount_B, p_collector);171}172173template <typename ShapeA, typename ShapeB, bool castA = false, bool castB = false, bool withMargin = false>174class SeparatorAxisTest2D {175const ShapeA *shape_A = nullptr;176const ShapeB *shape_B = nullptr;177const Transform2D *transform_A = nullptr;178const Transform2D *transform_B = nullptr;179real_t best_depth = 1e15;180Vector2 best_axis;181#ifdef DEBUG_ENABLED182int best_axis_count = 0;183int best_axis_index = -1;184#endif185Vector2 motion_A;186Vector2 motion_B;187real_t margin_A = 0.0;188real_t margin_B = 0.0;189_CollectorCallback2D *callback;190191public:192_FORCE_INLINE_ bool test_previous_axis() {193if (callback && callback->sep_axis && *callback->sep_axis != Vector2()) {194return test_axis(*callback->sep_axis);195} else {196#ifdef DEBUG_ENABLED197best_axis_count++;198#endif199}200return true;201}202203_FORCE_INLINE_ bool test_cast() {204if (castA) {205Vector2 na = motion_A.normalized();206if (!test_axis(na)) {207return false;208}209if (!test_axis(na.orthogonal())) {210return false;211}212}213214if (castB) {215Vector2 nb = motion_B.normalized();216if (!test_axis(nb)) {217return false;218}219if (!test_axis(nb.orthogonal())) {220return false;221}222}223224return true;225}226227_FORCE_INLINE_ bool test_axis(const Vector2 &p_axis) {228Vector2 axis = p_axis;229230if (Math::is_zero_approx(axis.x) &&231Math::is_zero_approx(axis.y)) {232// strange case, try an upwards separator233axis = Vector2(0.0, 1.0);234}235236real_t min_A = 0.0, max_A = 0.0, min_B = 0.0, max_B = 0.0;237238if (castA) {239shape_A->project_range_cast(motion_A, axis, *transform_A, min_A, max_A);240} else {241shape_A->project_range(axis, *transform_A, min_A, max_A);242}243244if (castB) {245shape_B->project_range_cast(motion_B, axis, *transform_B, min_B, max_B);246} else {247shape_B->project_range(axis, *transform_B, min_B, max_B);248}249250if (withMargin) {251min_A -= margin_A;252max_A += margin_A;253min_B -= margin_B;254max_B += margin_B;255}256257min_B -= (max_A - min_A) * 0.5;258max_B += (max_A - min_A) * 0.5;259260real_t dmin = min_B - (min_A + max_A) * 0.5;261real_t dmax = max_B - (min_A + max_A) * 0.5;262263if (dmin > 0.0 || dmax < 0.0) {264if (callback && callback->sep_axis) {265*callback->sep_axis = axis;266}267#ifdef DEBUG_ENABLED268best_axis_count++;269#endif270271return false; // doesn't contain 0272}273274//use the smallest depth275276dmin = Math::abs(dmin);277278if (dmax < dmin) {279if (dmax < best_depth) {280best_depth = dmax;281best_axis = axis;282#ifdef DEBUG_ENABLED283best_axis_index = best_axis_count;284#endif285}286} else {287if (dmin < best_depth) {288best_depth = dmin;289best_axis = -axis; // keep it as A axis290#ifdef DEBUG_ENABLED291best_axis_index = best_axis_count;292#endif293}294}295296#ifdef DEBUG_ENABLED297best_axis_count++;298#endif299300return true;301}302303_FORCE_INLINE_ void generate_contacts() {304// nothing to do, don't generate305if (best_axis == Vector2(0.0, 0.0)) {306return;307}308309if (callback) {310callback->collided = true;311312if (!callback->callback) {313return; //only collide, no callback314}315}316static const int max_supports = 2;317318Vector2 supports_A[max_supports];319int support_count_A;320if (castA) {321shape_A->get_supports_transformed_cast(motion_A, -best_axis, *transform_A, supports_A, support_count_A);322} else {323shape_A->get_supports(transform_A->basis_xform_inv(-best_axis).normalized(), supports_A, support_count_A);324for (int i = 0; i < support_count_A; i++) {325supports_A[i] = transform_A->xform(supports_A[i]);326}327}328329if (withMargin) {330for (int i = 0; i < support_count_A; i++) {331supports_A[i] += -best_axis * margin_A;332}333}334335Vector2 supports_B[max_supports];336int support_count_B;337if (castB) {338shape_B->get_supports_transformed_cast(motion_B, best_axis, *transform_B, supports_B, support_count_B);339} else {340shape_B->get_supports(transform_B->basis_xform_inv(best_axis).normalized(), supports_B, support_count_B);341for (int i = 0; i < support_count_B; i++) {342supports_B[i] = transform_B->xform(supports_B[i]);343}344}345346if (withMargin) {347for (int i = 0; i < support_count_B; i++) {348supports_B[i] += best_axis * margin_B;349}350}351if (callback) {352callback->normal = best_axis;353_generate_contacts_from_supports(supports_A, support_count_A, supports_B, support_count_B, callback);354355if (callback->sep_axis && *callback->sep_axis != Vector2()) {356*callback->sep_axis = Vector2(); //invalidate previous axis (no test)357}358}359}360361_FORCE_INLINE_ SeparatorAxisTest2D(const ShapeA *p_shape_A, const Transform2D &p_transform_a, const ShapeB *p_shape_B, const Transform2D &p_transform_b, _CollectorCallback2D *p_collector, const Vector2 &p_motion_A = Vector2(), const Vector2 &p_motion_B = Vector2(), real_t p_margin_A = 0, real_t p_margin_B = 0) {362margin_A = p_margin_A;363margin_B = p_margin_B;364shape_A = p_shape_A;365shape_B = p_shape_B;366transform_A = &p_transform_a;367transform_B = &p_transform_b;368motion_A = p_motion_A;369motion_B = p_motion_B;370callback = p_collector;371}372};373374/****** SAT TESTS *******/375376#define TEST_POINT(m_a, m_b) \377((!separator.test_axis(((m_a) - (m_b)).normalized())) || \378(castA && !separator.test_axis(((m_a) + p_motion_a - (m_b)).normalized())) || \379(castB && !separator.test_axis(((m_a) - ((m_b) + p_motion_b)).normalized())) || \380(castA && castB && !separator.test_axis(((m_a) + p_motion_a - ((m_b) + p_motion_b)).normalized())))381382typedef void (*CollisionFunc)(const GodotShape2D *, const Transform2D &, const GodotShape2D *, const Transform2D &, _CollectorCallback2D *p_collector, const Vector2 &, const Vector2 &, real_t, real_t);383384template <bool castA, bool castB, bool withMargin>385static void _collision_segment_segment(const GodotShape2D *p_a, const Transform2D &p_transform_a, const GodotShape2D *p_b, const Transform2D &p_transform_b, _CollectorCallback2D *p_collector, const Vector2 &p_motion_a, const Vector2 &p_motion_b, real_t p_margin_A, real_t p_margin_B) {386const GodotSegmentShape2D *segment_A = static_cast<const GodotSegmentShape2D *>(p_a);387const GodotSegmentShape2D *segment_B = static_cast<const GodotSegmentShape2D *>(p_b);388389SeparatorAxisTest2D<GodotSegmentShape2D, GodotSegmentShape2D, castA, castB, withMargin> separator(segment_A, p_transform_a, segment_B, p_transform_b, p_collector, p_motion_a, p_motion_b, p_margin_A, p_margin_B);390391if (!separator.test_previous_axis()) {392return;393}394//this collision is kind of pointless395396if (!separator.test_cast()) {397return;398}399400if (!separator.test_axis(segment_A->get_xformed_normal(p_transform_a))) {401return;402}403if (!separator.test_axis(segment_B->get_xformed_normal(p_transform_b))) {404return;405}406407if (withMargin) {408//points grow to circles409410if (TEST_POINT(p_transform_a.xform(segment_A->get_a()), p_transform_b.xform(segment_B->get_a()))) {411return;412}413if (TEST_POINT(p_transform_a.xform(segment_A->get_a()), p_transform_b.xform(segment_B->get_b()))) {414return;415}416if (TEST_POINT(p_transform_a.xform(segment_A->get_b()), p_transform_b.xform(segment_B->get_a()))) {417return;418}419if (TEST_POINT(p_transform_a.xform(segment_A->get_b()), p_transform_b.xform(segment_B->get_b()))) {420return;421}422}423424separator.generate_contacts();425}426427template <bool castA, bool castB, bool withMargin>428static void _collision_segment_circle(const GodotShape2D *p_a, const Transform2D &p_transform_a, const GodotShape2D *p_b, const Transform2D &p_transform_b, _CollectorCallback2D *p_collector, const Vector2 &p_motion_a, const Vector2 &p_motion_b, real_t p_margin_A, real_t p_margin_B) {429const GodotSegmentShape2D *segment_A = static_cast<const GodotSegmentShape2D *>(p_a);430const GodotCircleShape2D *circle_B = static_cast<const GodotCircleShape2D *>(p_b);431432SeparatorAxisTest2D<GodotSegmentShape2D, GodotCircleShape2D, castA, castB, withMargin> separator(segment_A, p_transform_a, circle_B, p_transform_b, p_collector, p_motion_a, p_motion_b, p_margin_A, p_margin_B);433434if (!separator.test_previous_axis()) {435return;436}437438if (!separator.test_cast()) {439return;440}441442//segment normal443if (!separator.test_axis(444(p_transform_a.xform(segment_A->get_b()) - p_transform_a.xform(segment_A->get_a())).normalized().orthogonal())) {445return;446}447448//endpoint a vs circle449if (TEST_POINT(p_transform_a.xform(segment_A->get_a()), p_transform_b.get_origin())) {450return;451}452//endpoint b vs circle453if (TEST_POINT(p_transform_a.xform(segment_A->get_b()), p_transform_b.get_origin())) {454return;455}456457separator.generate_contacts();458}459460template <bool castA, bool castB, bool withMargin>461static void _collision_segment_rectangle(const GodotShape2D *p_a, const Transform2D &p_transform_a, const GodotShape2D *p_b, const Transform2D &p_transform_b, _CollectorCallback2D *p_collector, const Vector2 &p_motion_a, const Vector2 &p_motion_b, real_t p_margin_A, real_t p_margin_B) {462const GodotSegmentShape2D *segment_A = static_cast<const GodotSegmentShape2D *>(p_a);463const GodotRectangleShape2D *rectangle_B = static_cast<const GodotRectangleShape2D *>(p_b);464465SeparatorAxisTest2D<GodotSegmentShape2D, GodotRectangleShape2D, castA, castB, withMargin> separator(segment_A, p_transform_a, rectangle_B, p_transform_b, p_collector, p_motion_a, p_motion_b, p_margin_A, p_margin_B);466467if (!separator.test_previous_axis()) {468return;469}470471if (!separator.test_cast()) {472return;473}474475if (!separator.test_axis(segment_A->get_xformed_normal(p_transform_a))) {476return;477}478479if (!separator.test_axis(p_transform_b.columns[0].normalized())) {480return;481}482483if (!separator.test_axis(p_transform_b.columns[1].normalized())) {484return;485}486487if (withMargin) {488Transform2D inv = p_transform_b.affine_inverse();489490Vector2 a = p_transform_a.xform(segment_A->get_a());491Vector2 b = p_transform_a.xform(segment_A->get_b());492493if (!separator.test_axis(rectangle_B->get_circle_axis(p_transform_b, inv, a))) {494return;495}496if (!separator.test_axis(rectangle_B->get_circle_axis(p_transform_b, inv, b))) {497return;498}499500if constexpr (castA) {501if (!separator.test_axis(rectangle_B->get_circle_axis(p_transform_b, inv, a + p_motion_a))) {502return;503}504if (!separator.test_axis(rectangle_B->get_circle_axis(p_transform_b, inv, b + p_motion_a))) {505return;506}507}508509if constexpr (castB) {510if (!separator.test_axis(rectangle_B->get_circle_axis(p_transform_b, inv, a - p_motion_b))) {511return;512}513if (!separator.test_axis(rectangle_B->get_circle_axis(p_transform_b, inv, b - p_motion_b))) {514return;515}516}517518if constexpr (castA && castB) {519if (!separator.test_axis(rectangle_B->get_circle_axis(p_transform_b, inv, a - p_motion_b + p_motion_a))) {520return;521}522if (!separator.test_axis(rectangle_B->get_circle_axis(p_transform_b, inv, b - p_motion_b + p_motion_a))) {523return;524}525}526}527528separator.generate_contacts();529}530531template <bool castA, bool castB, bool withMargin>532static void _collision_segment_capsule(const GodotShape2D *p_a, const Transform2D &p_transform_a, const GodotShape2D *p_b, const Transform2D &p_transform_b, _CollectorCallback2D *p_collector, const Vector2 &p_motion_a, const Vector2 &p_motion_b, real_t p_margin_A, real_t p_margin_B) {533const GodotSegmentShape2D *segment_A = static_cast<const GodotSegmentShape2D *>(p_a);534const GodotCapsuleShape2D *capsule_B = static_cast<const GodotCapsuleShape2D *>(p_b);535536SeparatorAxisTest2D<GodotSegmentShape2D, GodotCapsuleShape2D, castA, castB, withMargin> separator(segment_A, p_transform_a, capsule_B, p_transform_b, p_collector, p_motion_a, p_motion_b, p_margin_A, p_margin_B);537538if (!separator.test_previous_axis()) {539return;540}541542if (!separator.test_cast()) {543return;544}545546if (!separator.test_axis(segment_A->get_xformed_normal(p_transform_a))) {547return;548}549550if (!separator.test_axis(p_transform_b.columns[0].normalized())) {551return;552}553554real_t capsule_dir = capsule_B->get_height() * 0.5 - capsule_B->get_radius();555556if (TEST_POINT(p_transform_a.xform(segment_A->get_a()), (p_transform_b.get_origin() + p_transform_b.columns[1] * capsule_dir))) {557return;558}559if (TEST_POINT(p_transform_a.xform(segment_A->get_a()), (p_transform_b.get_origin() - p_transform_b.columns[1] * capsule_dir))) {560return;561}562if (TEST_POINT(p_transform_a.xform(segment_A->get_b()), (p_transform_b.get_origin() + p_transform_b.columns[1] * capsule_dir))) {563return;564}565if (TEST_POINT(p_transform_a.xform(segment_A->get_b()), (p_transform_b.get_origin() - p_transform_b.columns[1] * capsule_dir))) {566return;567}568569separator.generate_contacts();570}571572template <bool castA, bool castB, bool withMargin>573static void _collision_segment_convex_polygon(const GodotShape2D *p_a, const Transform2D &p_transform_a, const GodotShape2D *p_b, const Transform2D &p_transform_b, _CollectorCallback2D *p_collector, const Vector2 &p_motion_a, const Vector2 &p_motion_b, real_t p_margin_A, real_t p_margin_B) {574const GodotSegmentShape2D *segment_A = static_cast<const GodotSegmentShape2D *>(p_a);575const GodotConvexPolygonShape2D *convex_B = static_cast<const GodotConvexPolygonShape2D *>(p_b);576577SeparatorAxisTest2D<GodotSegmentShape2D, GodotConvexPolygonShape2D, castA, castB, withMargin> separator(segment_A, p_transform_a, convex_B, p_transform_b, p_collector, p_motion_a, p_motion_b, p_margin_A, p_margin_B);578579if (!separator.test_previous_axis()) {580return;581}582583if (!separator.test_cast()) {584return;585}586587if (!separator.test_axis(segment_A->get_xformed_normal(p_transform_a))) {588return;589}590591for (int i = 0; i < convex_B->get_point_count(); i++) {592if (!separator.test_axis(convex_B->get_xformed_segment_normal(p_transform_b, i))) {593return;594}595596if (withMargin) {597if (TEST_POINT(p_transform_a.xform(segment_A->get_a()), p_transform_b.xform(convex_B->get_point(i)))) {598return;599}600if (TEST_POINT(p_transform_a.xform(segment_A->get_b()), p_transform_b.xform(convex_B->get_point(i)))) {601return;602}603}604}605606separator.generate_contacts();607}608609/////////610611template <bool castA, bool castB, bool withMargin>612static void _collision_circle_circle(const GodotShape2D *p_a, const Transform2D &p_transform_a, const GodotShape2D *p_b, const Transform2D &p_transform_b, _CollectorCallback2D *p_collector, const Vector2 &p_motion_a, const Vector2 &p_motion_b, real_t p_margin_A, real_t p_margin_B) {613const GodotCircleShape2D *circle_A = static_cast<const GodotCircleShape2D *>(p_a);614const GodotCircleShape2D *circle_B = static_cast<const GodotCircleShape2D *>(p_b);615616SeparatorAxisTest2D<GodotCircleShape2D, GodotCircleShape2D, castA, castB, withMargin> separator(circle_A, p_transform_a, circle_B, p_transform_b, p_collector, p_motion_a, p_motion_b, p_margin_A, p_margin_B);617618if (!separator.test_previous_axis()) {619return;620}621622if (!separator.test_cast()) {623return;624}625626if (TEST_POINT(p_transform_a.get_origin(), p_transform_b.get_origin())) {627return;628}629630separator.generate_contacts();631}632633template <bool castA, bool castB, bool withMargin>634static void _collision_circle_rectangle(const GodotShape2D *p_a, const Transform2D &p_transform_a, const GodotShape2D *p_b, const Transform2D &p_transform_b, _CollectorCallback2D *p_collector, const Vector2 &p_motion_a, const Vector2 &p_motion_b, real_t p_margin_A, real_t p_margin_B) {635const GodotCircleShape2D *circle_A = static_cast<const GodotCircleShape2D *>(p_a);636const GodotRectangleShape2D *rectangle_B = static_cast<const GodotRectangleShape2D *>(p_b);637638SeparatorAxisTest2D<GodotCircleShape2D, GodotRectangleShape2D, castA, castB, withMargin> separator(circle_A, p_transform_a, rectangle_B, p_transform_b, p_collector, p_motion_a, p_motion_b, p_margin_A, p_margin_B);639640if (!separator.test_previous_axis()) {641return;642}643644if (!separator.test_cast()) {645return;646}647648const Vector2 &sphere = p_transform_a.columns[2];649const Vector2 *axis = &p_transform_b.columns[0];650//const Vector2& half_extents = rectangle_B->get_half_extents();651652if (!separator.test_axis(axis[0].normalized())) {653return;654}655656if (!separator.test_axis(axis[1].normalized())) {657return;658}659660Transform2D binv = p_transform_b.affine_inverse();661{662if (!separator.test_axis(rectangle_B->get_circle_axis(p_transform_b, binv, sphere))) {663return;664}665}666667if constexpr (castA) {668Vector2 sphereofs = sphere + p_motion_a;669if (!separator.test_axis(rectangle_B->get_circle_axis(p_transform_b, binv, sphereofs))) {670return;671}672}673674if constexpr (castB) {675Vector2 sphereofs = sphere - p_motion_b;676if (!separator.test_axis(rectangle_B->get_circle_axis(p_transform_b, binv, sphereofs))) {677return;678}679}680681if constexpr (castA && castB) {682Vector2 sphereofs = sphere - p_motion_b + p_motion_a;683if (!separator.test_axis(rectangle_B->get_circle_axis(p_transform_b, binv, sphereofs))) {684return;685}686}687688separator.generate_contacts();689}690691template <bool castA, bool castB, bool withMargin>692static void _collision_circle_capsule(const GodotShape2D *p_a, const Transform2D &p_transform_a, const GodotShape2D *p_b, const Transform2D &p_transform_b, _CollectorCallback2D *p_collector, const Vector2 &p_motion_a, const Vector2 &p_motion_b, real_t p_margin_A, real_t p_margin_B) {693const GodotCircleShape2D *circle_A = static_cast<const GodotCircleShape2D *>(p_a);694const GodotCapsuleShape2D *capsule_B = static_cast<const GodotCapsuleShape2D *>(p_b);695696SeparatorAxisTest2D<GodotCircleShape2D, GodotCapsuleShape2D, castA, castB, withMargin> separator(circle_A, p_transform_a, capsule_B, p_transform_b, p_collector, p_motion_a, p_motion_b, p_margin_A, p_margin_B);697698if (!separator.test_previous_axis()) {699return;700}701702if (!separator.test_cast()) {703return;704}705706//capsule axis707if (!separator.test_axis(p_transform_b.columns[0].normalized())) {708return;709}710711real_t capsule_dir = capsule_B->get_height() * 0.5 - capsule_B->get_radius();712713//capsule endpoints714if (TEST_POINT(p_transform_a.get_origin(), (p_transform_b.get_origin() + p_transform_b.columns[1] * capsule_dir))) {715return;716}717if (TEST_POINT(p_transform_a.get_origin(), (p_transform_b.get_origin() - p_transform_b.columns[1] * capsule_dir))) {718return;719}720721separator.generate_contacts();722}723724template <bool castA, bool castB, bool withMargin>725static void _collision_circle_convex_polygon(const GodotShape2D *p_a, const Transform2D &p_transform_a, const GodotShape2D *p_b, const Transform2D &p_transform_b, _CollectorCallback2D *p_collector, const Vector2 &p_motion_a, const Vector2 &p_motion_b, real_t p_margin_A, real_t p_margin_B) {726const GodotCircleShape2D *circle_A = static_cast<const GodotCircleShape2D *>(p_a);727const GodotConvexPolygonShape2D *convex_B = static_cast<const GodotConvexPolygonShape2D *>(p_b);728729SeparatorAxisTest2D<GodotCircleShape2D, GodotConvexPolygonShape2D, castA, castB, withMargin> separator(circle_A, p_transform_a, convex_B, p_transform_b, p_collector, p_motion_a, p_motion_b, p_margin_A, p_margin_B);730731if (!separator.test_previous_axis()) {732return;733}734735if (!separator.test_cast()) {736return;737}738739//poly faces and poly points vs circle740for (int i = 0; i < convex_B->get_point_count(); i++) {741if (TEST_POINT(p_transform_a.get_origin(), p_transform_b.xform(convex_B->get_point(i)))) {742return;743}744745if (!separator.test_axis(convex_B->get_xformed_segment_normal(p_transform_b, i))) {746return;747}748}749750separator.generate_contacts();751}752753/////////754755template <bool castA, bool castB, bool withMargin>756static void _collision_rectangle_rectangle(const GodotShape2D *p_a, const Transform2D &p_transform_a, const GodotShape2D *p_b, const Transform2D &p_transform_b, _CollectorCallback2D *p_collector, const Vector2 &p_motion_a, const Vector2 &p_motion_b, real_t p_margin_A, real_t p_margin_B) {757const GodotRectangleShape2D *rectangle_A = static_cast<const GodotRectangleShape2D *>(p_a);758const GodotRectangleShape2D *rectangle_B = static_cast<const GodotRectangleShape2D *>(p_b);759760SeparatorAxisTest2D<GodotRectangleShape2D, GodotRectangleShape2D, castA, castB, withMargin> separator(rectangle_A, p_transform_a, rectangle_B, p_transform_b, p_collector, p_motion_a, p_motion_b, p_margin_A, p_margin_B);761762if (!separator.test_previous_axis()) {763return;764}765766if (!separator.test_cast()) {767return;768}769770//box faces A771if (!separator.test_axis(p_transform_a.columns[0].normalized())) {772return;773}774775if (!separator.test_axis(p_transform_a.columns[1].normalized())) {776return;777}778779//box faces B780if (!separator.test_axis(p_transform_b.columns[0].normalized())) {781return;782}783784if (!separator.test_axis(p_transform_b.columns[1].normalized())) {785return;786}787788if constexpr (withMargin) {789Transform2D invA = p_transform_a.affine_inverse();790Transform2D invB = p_transform_b.affine_inverse();791792if (!separator.test_axis(rectangle_A->get_box_axis(p_transform_a, invA, rectangle_B, p_transform_b, invB))) {793return;794}795796if constexpr (castA || castB) {797Transform2D aofs = p_transform_a;798aofs.columns[2] += p_motion_a;799800Transform2D bofs = p_transform_b;801bofs.columns[2] += p_motion_b;802803[[maybe_unused]] Transform2D aofsinv = aofs.affine_inverse();804[[maybe_unused]] Transform2D bofsinv = bofs.affine_inverse();805806if constexpr (castA) {807if (!separator.test_axis(rectangle_A->get_box_axis(aofs, aofsinv, rectangle_B, p_transform_b, invB))) {808return;809}810}811812if constexpr (castB) {813if (!separator.test_axis(rectangle_A->get_box_axis(p_transform_a, invA, rectangle_B, bofs, bofsinv))) {814return;815}816}817818if constexpr (castA && castB) {819if (!separator.test_axis(rectangle_A->get_box_axis(aofs, aofsinv, rectangle_B, bofs, bofsinv))) {820return;821}822}823}824}825826separator.generate_contacts();827}828829template <bool castA, bool castB, bool withMargin>830static void _collision_rectangle_capsule(const GodotShape2D *p_a, const Transform2D &p_transform_a, const GodotShape2D *p_b, const Transform2D &p_transform_b, _CollectorCallback2D *p_collector, const Vector2 &p_motion_a, const Vector2 &p_motion_b, real_t p_margin_A, real_t p_margin_B) {831const GodotRectangleShape2D *rectangle_A = static_cast<const GodotRectangleShape2D *>(p_a);832const GodotCapsuleShape2D *capsule_B = static_cast<const GodotCapsuleShape2D *>(p_b);833834SeparatorAxisTest2D<GodotRectangleShape2D, GodotCapsuleShape2D, castA, castB, withMargin> separator(rectangle_A, p_transform_a, capsule_B, p_transform_b, p_collector, p_motion_a, p_motion_b, p_margin_A, p_margin_B);835836if (!separator.test_previous_axis()) {837return;838}839840if (!separator.test_cast()) {841return;842}843844//box faces845if (!separator.test_axis(p_transform_a.columns[0].normalized())) {846return;847}848849if (!separator.test_axis(p_transform_a.columns[1].normalized())) {850return;851}852853//capsule axis854if (!separator.test_axis(p_transform_b.columns[0].normalized())) {855return;856}857858//box endpoints to capsule circles859860Transform2D boxinv = p_transform_a.affine_inverse();861862real_t capsule_dir = capsule_B->get_height() * 0.5 - capsule_B->get_radius();863864for (int i = 0; i < 2; i++) {865{866Vector2 capsule_endpoint = p_transform_b.get_origin() + p_transform_b.columns[1] * capsule_dir;867868if (!separator.test_axis(rectangle_A->get_circle_axis(p_transform_a, boxinv, capsule_endpoint))) {869return;870}871}872873if constexpr (castA) {874Vector2 capsule_endpoint = p_transform_b.get_origin() + p_transform_b.columns[1] * capsule_dir;875capsule_endpoint -= p_motion_a;876877if (!separator.test_axis(rectangle_A->get_circle_axis(p_transform_a, boxinv, capsule_endpoint))) {878return;879}880}881882if constexpr (castB) {883Vector2 capsule_endpoint = p_transform_b.get_origin() + p_transform_b.columns[1] * capsule_dir;884capsule_endpoint += p_motion_b;885886if (!separator.test_axis(rectangle_A->get_circle_axis(p_transform_a, boxinv, capsule_endpoint))) {887return;888}889}890891if constexpr (castA && castB) {892Vector2 capsule_endpoint = p_transform_b.get_origin() + p_transform_b.columns[1] * capsule_dir;893capsule_endpoint -= p_motion_a;894capsule_endpoint += p_motion_b;895896if (!separator.test_axis(rectangle_A->get_circle_axis(p_transform_a, boxinv, capsule_endpoint))) {897return;898}899}900901capsule_dir *= -1.0;902}903904separator.generate_contacts();905}906907template <bool castA, bool castB, bool withMargin>908static void _collision_rectangle_convex_polygon(const GodotShape2D *p_a, const Transform2D &p_transform_a, const GodotShape2D *p_b, const Transform2D &p_transform_b, _CollectorCallback2D *p_collector, const Vector2 &p_motion_a, const Vector2 &p_motion_b, real_t p_margin_A, real_t p_margin_B) {909const GodotRectangleShape2D *rectangle_A = static_cast<const GodotRectangleShape2D *>(p_a);910const GodotConvexPolygonShape2D *convex_B = static_cast<const GodotConvexPolygonShape2D *>(p_b);911912SeparatorAxisTest2D<GodotRectangleShape2D, GodotConvexPolygonShape2D, castA, castB, withMargin> separator(rectangle_A, p_transform_a, convex_B, p_transform_b, p_collector, p_motion_a, p_motion_b, p_margin_A, p_margin_B);913914if (!separator.test_previous_axis()) {915return;916}917918if (!separator.test_cast()) {919return;920}921922//box faces923if (!separator.test_axis(p_transform_a.columns[0].normalized())) {924return;925}926927if (!separator.test_axis(p_transform_a.columns[1].normalized())) {928return;929}930931//convex faces932Transform2D boxinv;933if constexpr (withMargin) {934boxinv = p_transform_a.affine_inverse();935}936for (int i = 0; i < convex_B->get_point_count(); i++) {937if (!separator.test_axis(convex_B->get_xformed_segment_normal(p_transform_b, i))) {938return;939}940941if constexpr (withMargin) {942//all points vs all points need to be tested if margin exist943if (!separator.test_axis(rectangle_A->get_circle_axis(p_transform_a, boxinv, p_transform_b.xform(convex_B->get_point(i))))) {944return;945}946if constexpr (castA) {947if (!separator.test_axis(rectangle_A->get_circle_axis(p_transform_a, boxinv, p_transform_b.xform(convex_B->get_point(i)) - p_motion_a))) {948return;949}950}951if constexpr (castB) {952if (!separator.test_axis(rectangle_A->get_circle_axis(p_transform_a, boxinv, p_transform_b.xform(convex_B->get_point(i)) + p_motion_b))) {953return;954}955}956if constexpr (castA && castB) {957if (!separator.test_axis(rectangle_A->get_circle_axis(p_transform_a, boxinv, p_transform_b.xform(convex_B->get_point(i)) + p_motion_b - p_motion_a))) {958return;959}960}961}962}963964separator.generate_contacts();965}966967/////////968969template <bool castA, bool castB, bool withMargin>970static void _collision_capsule_capsule(const GodotShape2D *p_a, const Transform2D &p_transform_a, const GodotShape2D *p_b, const Transform2D &p_transform_b, _CollectorCallback2D *p_collector, const Vector2 &p_motion_a, const Vector2 &p_motion_b, real_t p_margin_A, real_t p_margin_B) {971const GodotCapsuleShape2D *capsule_A = static_cast<const GodotCapsuleShape2D *>(p_a);972const GodotCapsuleShape2D *capsule_B = static_cast<const GodotCapsuleShape2D *>(p_b);973974SeparatorAxisTest2D<GodotCapsuleShape2D, GodotCapsuleShape2D, castA, castB, withMargin> separator(capsule_A, p_transform_a, capsule_B, p_transform_b, p_collector, p_motion_a, p_motion_b, p_margin_A, p_margin_B);975976if (!separator.test_previous_axis()) {977return;978}979980if (!separator.test_cast()) {981return;982}983984//capsule axis985986if (!separator.test_axis(p_transform_b.columns[0].normalized())) {987return;988}989990if (!separator.test_axis(p_transform_a.columns[0].normalized())) {991return;992}993994//capsule endpoints995996real_t capsule_dir_A = capsule_A->get_height() * 0.5 - capsule_A->get_radius();997for (int i = 0; i < 2; i++) {998Vector2 capsule_endpoint_A = p_transform_a.get_origin() + p_transform_a.columns[1] * capsule_dir_A;9991000real_t capsule_dir_B = capsule_B->get_height() * 0.5 - capsule_B->get_radius();1001for (int j = 0; j < 2; j++) {1002Vector2 capsule_endpoint_B = p_transform_b.get_origin() + p_transform_b.columns[1] * capsule_dir_B;10031004if (TEST_POINT(capsule_endpoint_A, capsule_endpoint_B)) {1005return;1006}10071008capsule_dir_B *= -1.0;1009}10101011capsule_dir_A *= -1.0;1012}10131014separator.generate_contacts();1015}10161017template <bool castA, bool castB, bool withMargin>1018static void _collision_capsule_convex_polygon(const GodotShape2D *p_a, const Transform2D &p_transform_a, const GodotShape2D *p_b, const Transform2D &p_transform_b, _CollectorCallback2D *p_collector, const Vector2 &p_motion_a, const Vector2 &p_motion_b, real_t p_margin_A, real_t p_margin_B) {1019const GodotCapsuleShape2D *capsule_A = static_cast<const GodotCapsuleShape2D *>(p_a);1020const GodotConvexPolygonShape2D *convex_B = static_cast<const GodotConvexPolygonShape2D *>(p_b);10211022SeparatorAxisTest2D<GodotCapsuleShape2D, GodotConvexPolygonShape2D, castA, castB, withMargin> separator(capsule_A, p_transform_a, convex_B, p_transform_b, p_collector, p_motion_a, p_motion_b, p_margin_A, p_margin_B);10231024if (!separator.test_previous_axis()) {1025return;1026}10271028if (!separator.test_cast()) {1029return;1030}10311032//capsule axis10331034if (!separator.test_axis(p_transform_a.columns[0].normalized())) {1035return;1036}10371038//poly vs capsule1039for (int i = 0; i < convex_B->get_point_count(); i++) {1040Vector2 cpoint = p_transform_b.xform(convex_B->get_point(i));10411042real_t capsule_dir = capsule_A->get_height() * 0.5 - capsule_A->get_radius();1043for (int j = 0; j < 2; j++) {1044Vector2 capsule_endpoint_A = p_transform_a.get_origin() + p_transform_a.columns[1] * capsule_dir;10451046if (TEST_POINT(capsule_endpoint_A, cpoint)) {1047return;1048}10491050capsule_dir *= -1.0;1051}10521053if (!separator.test_axis(convex_B->get_xformed_segment_normal(p_transform_b, i))) {1054return;1055}1056}10571058separator.generate_contacts();1059}10601061/////////10621063template <bool castA, bool castB, bool withMargin>1064static void _collision_convex_polygon_convex_polygon(const GodotShape2D *p_a, const Transform2D &p_transform_a, const GodotShape2D *p_b, const Transform2D &p_transform_b, _CollectorCallback2D *p_collector, const Vector2 &p_motion_a, const Vector2 &p_motion_b, real_t p_margin_A, real_t p_margin_B) {1065const GodotConvexPolygonShape2D *convex_A = static_cast<const GodotConvexPolygonShape2D *>(p_a);1066const GodotConvexPolygonShape2D *convex_B = static_cast<const GodotConvexPolygonShape2D *>(p_b);10671068SeparatorAxisTest2D<GodotConvexPolygonShape2D, GodotConvexPolygonShape2D, castA, castB, withMargin> separator(convex_A, p_transform_a, convex_B, p_transform_b, p_collector, p_motion_a, p_motion_b, p_margin_A, p_margin_B);10691070if (!separator.test_previous_axis()) {1071return;1072}10731074if (!separator.test_cast()) {1075return;1076}10771078for (int i = 0; i < convex_A->get_point_count(); i++) {1079if (!separator.test_axis(convex_A->get_xformed_segment_normal(p_transform_a, i))) {1080return;1081}1082}10831084for (int i = 0; i < convex_B->get_point_count(); i++) {1085if (!separator.test_axis(convex_B->get_xformed_segment_normal(p_transform_b, i))) {1086return;1087}1088}10891090if (withMargin) {1091for (int i = 0; i < convex_A->get_point_count(); i++) {1092for (int j = 0; j < convex_B->get_point_count(); j++) {1093if (TEST_POINT(p_transform_a.xform(convex_A->get_point(i)), p_transform_b.xform(convex_B->get_point(j)))) {1094return;1095}1096}1097}1098}10991100separator.generate_contacts();1101}11021103////////11041105bool sat_2d_calculate_penetration(const GodotShape2D *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const GodotShape2D *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, GodotCollisionSolver2D::CallbackResult p_result_callback, void *p_userdata, bool p_swap, Vector2 *sep_axis, real_t p_margin_A, real_t p_margin_B) {1106PhysicsServer2D::ShapeType type_A = p_shape_A->get_type();11071108ERR_FAIL_COND_V(type_A == PhysicsServer2D::SHAPE_WORLD_BOUNDARY, false);1109ERR_FAIL_COND_V(type_A == PhysicsServer2D::SHAPE_SEPARATION_RAY, false);1110ERR_FAIL_COND_V(p_shape_A->is_concave(), false);11111112PhysicsServer2D::ShapeType type_B = p_shape_B->get_type();11131114ERR_FAIL_COND_V(type_B == PhysicsServer2D::SHAPE_WORLD_BOUNDARY, false);1115ERR_FAIL_COND_V(type_B == PhysicsServer2D::SHAPE_SEPARATION_RAY, false);1116ERR_FAIL_COND_V(p_shape_B->is_concave(), false);11171118static const CollisionFunc collision_table[5][5] = {1119{ _collision_segment_segment<false, false, false>,1120_collision_segment_circle<false, false, false>,1121_collision_segment_rectangle<false, false, false>,1122_collision_segment_capsule<false, false, false>,1123_collision_segment_convex_polygon<false, false, false> },1124{ nullptr,1125_collision_circle_circle<false, false, false>,1126_collision_circle_rectangle<false, false, false>,1127_collision_circle_capsule<false, false, false>,1128_collision_circle_convex_polygon<false, false, false> },1129{ nullptr,1130nullptr,1131_collision_rectangle_rectangle<false, false, false>,1132_collision_rectangle_capsule<false, false, false>,1133_collision_rectangle_convex_polygon<false, false, false> },1134{ nullptr,1135nullptr,1136nullptr,1137_collision_capsule_capsule<false, false, false>,1138_collision_capsule_convex_polygon<false, false, false> },1139{ nullptr,1140nullptr,1141nullptr,1142nullptr,1143_collision_convex_polygon_convex_polygon<false, false, false> }11441145};11461147static const CollisionFunc collision_table_castA[5][5] = {1148{ _collision_segment_segment<true, false, false>,1149_collision_segment_circle<true, false, false>,1150_collision_segment_rectangle<true, false, false>,1151_collision_segment_capsule<true, false, false>,1152_collision_segment_convex_polygon<true, false, false> },1153{ nullptr,1154_collision_circle_circle<true, false, false>,1155_collision_circle_rectangle<true, false, false>,1156_collision_circle_capsule<true, false, false>,1157_collision_circle_convex_polygon<true, false, false> },1158{ nullptr,1159nullptr,1160_collision_rectangle_rectangle<true, false, false>,1161_collision_rectangle_capsule<true, false, false>,1162_collision_rectangle_convex_polygon<true, false, false> },1163{ nullptr,1164nullptr,1165nullptr,1166_collision_capsule_capsule<true, false, false>,1167_collision_capsule_convex_polygon<true, false, false> },1168{ nullptr,1169nullptr,1170nullptr,1171nullptr,1172_collision_convex_polygon_convex_polygon<true, false, false> }11731174};11751176static const CollisionFunc collision_table_castB[5][5] = {1177{ _collision_segment_segment<false, true, false>,1178_collision_segment_circle<false, true, false>,1179_collision_segment_rectangle<false, true, false>,1180_collision_segment_capsule<false, true, false>,1181_collision_segment_convex_polygon<false, true, false> },1182{ nullptr,1183_collision_circle_circle<false, true, false>,1184_collision_circle_rectangle<false, true, false>,1185_collision_circle_capsule<false, true, false>,1186_collision_circle_convex_polygon<false, true, false> },1187{ nullptr,1188nullptr,1189_collision_rectangle_rectangle<false, true, false>,1190_collision_rectangle_capsule<false, true, false>,1191_collision_rectangle_convex_polygon<false, true, false> },1192{ nullptr,1193nullptr,1194nullptr,1195_collision_capsule_capsule<false, true, false>,1196_collision_capsule_convex_polygon<false, true, false> },1197{ nullptr,1198nullptr,1199nullptr,1200nullptr,1201_collision_convex_polygon_convex_polygon<false, true, false> }12021203};12041205static const CollisionFunc collision_table_castA_castB[5][5] = {1206{ _collision_segment_segment<true, true, false>,1207_collision_segment_circle<true, true, false>,1208_collision_segment_rectangle<true, true, false>,1209_collision_segment_capsule<true, true, false>,1210_collision_segment_convex_polygon<true, true, false> },1211{ nullptr,1212_collision_circle_circle<true, true, false>,1213_collision_circle_rectangle<true, true, false>,1214_collision_circle_capsule<true, true, false>,1215_collision_circle_convex_polygon<true, true, false> },1216{ nullptr,1217nullptr,1218_collision_rectangle_rectangle<true, true, false>,1219_collision_rectangle_capsule<true, true, false>,1220_collision_rectangle_convex_polygon<true, true, false> },1221{ nullptr,1222nullptr,1223nullptr,1224_collision_capsule_capsule<true, true, false>,1225_collision_capsule_convex_polygon<true, true, false> },1226{ nullptr,1227nullptr,1228nullptr,1229nullptr,1230_collision_convex_polygon_convex_polygon<true, true, false> }12311232};12331234static const CollisionFunc collision_table_margin[5][5] = {1235{ _collision_segment_segment<false, false, true>,1236_collision_segment_circle<false, false, true>,1237_collision_segment_rectangle<false, false, true>,1238_collision_segment_capsule<false, false, true>,1239_collision_segment_convex_polygon<false, false, true> },1240{ nullptr,1241_collision_circle_circle<false, false, true>,1242_collision_circle_rectangle<false, false, true>,1243_collision_circle_capsule<false, false, true>,1244_collision_circle_convex_polygon<false, false, true> },1245{ nullptr,1246nullptr,1247_collision_rectangle_rectangle<false, false, true>,1248_collision_rectangle_capsule<false, false, true>,1249_collision_rectangle_convex_polygon<false, false, true> },1250{ nullptr,1251nullptr,1252nullptr,1253_collision_capsule_capsule<false, false, true>,1254_collision_capsule_convex_polygon<false, false, true> },1255{ nullptr,1256nullptr,1257nullptr,1258nullptr,1259_collision_convex_polygon_convex_polygon<false, false, true> }12601261};12621263static const CollisionFunc collision_table_castA_margin[5][5] = {1264{ _collision_segment_segment<true, false, true>,1265_collision_segment_circle<true, false, true>,1266_collision_segment_rectangle<true, false, true>,1267_collision_segment_capsule<true, false, true>,1268_collision_segment_convex_polygon<true, false, true> },1269{ nullptr,1270_collision_circle_circle<true, false, true>,1271_collision_circle_rectangle<true, false, true>,1272_collision_circle_capsule<true, false, true>,1273_collision_circle_convex_polygon<true, false, true> },1274{ nullptr,1275nullptr,1276_collision_rectangle_rectangle<true, false, true>,1277_collision_rectangle_capsule<true, false, true>,1278_collision_rectangle_convex_polygon<true, false, true> },1279{ nullptr,1280nullptr,1281nullptr,1282_collision_capsule_capsule<true, false, true>,1283_collision_capsule_convex_polygon<true, false, true> },1284{ nullptr,1285nullptr,1286nullptr,1287nullptr,1288_collision_convex_polygon_convex_polygon<true, false, true> }12891290};12911292static const CollisionFunc collision_table_castB_margin[5][5] = {1293{ _collision_segment_segment<false, true, true>,1294_collision_segment_circle<false, true, true>,1295_collision_segment_rectangle<false, true, true>,1296_collision_segment_capsule<false, true, true>,1297_collision_segment_convex_polygon<false, true, true> },1298{ nullptr,1299_collision_circle_circle<false, true, true>,1300_collision_circle_rectangle<false, true, true>,1301_collision_circle_capsule<false, true, true>,1302_collision_circle_convex_polygon<false, true, true> },1303{ nullptr,1304nullptr,1305_collision_rectangle_rectangle<false, true, true>,1306_collision_rectangle_capsule<false, true, true>,1307_collision_rectangle_convex_polygon<false, true, true> },1308{ nullptr,1309nullptr,1310nullptr,1311_collision_capsule_capsule<false, true, true>,1312_collision_capsule_convex_polygon<false, true, true> },1313{ nullptr,1314nullptr,1315nullptr,1316nullptr,1317_collision_convex_polygon_convex_polygon<false, true, true> }13181319};13201321static const CollisionFunc collision_table_castA_castB_margin[5][5] = {1322{ _collision_segment_segment<true, true, true>,1323_collision_segment_circle<true, true, true>,1324_collision_segment_rectangle<true, true, true>,1325_collision_segment_capsule<true, true, true>,1326_collision_segment_convex_polygon<true, true, true> },1327{ nullptr,1328_collision_circle_circle<true, true, true>,1329_collision_circle_rectangle<true, true, true>,1330_collision_circle_capsule<true, true, true>,1331_collision_circle_convex_polygon<true, true, true> },1332{ nullptr,1333nullptr,1334_collision_rectangle_rectangle<true, true, true>,1335_collision_rectangle_capsule<true, true, true>,1336_collision_rectangle_convex_polygon<true, true, true> },1337{ nullptr,1338nullptr,1339nullptr,1340_collision_capsule_capsule<true, true, true>,1341_collision_capsule_convex_polygon<true, true, true> },1342{ nullptr,1343nullptr,1344nullptr,1345nullptr,1346_collision_convex_polygon_convex_polygon<true, true, true> }13471348};13491350_CollectorCallback2D callback;1351callback.callback = p_result_callback;1352callback.swap = p_swap;1353callback.userdata = p_userdata;1354callback.collided = false;1355callback.sep_axis = sep_axis;13561357const GodotShape2D *A = p_shape_A;1358const GodotShape2D *B = p_shape_B;1359const Transform2D *transform_A = &p_transform_A;1360const Transform2D *transform_B = &p_transform_B;1361const Vector2 *motion_A = &p_motion_A;1362const Vector2 *motion_B = &p_motion_B;1363real_t margin_A = p_margin_A, margin_B = p_margin_B;13641365if (type_A > type_B) {1366SWAP(A, B);1367SWAP(transform_A, transform_B);1368SWAP(type_A, type_B);1369SWAP(motion_A, motion_B);1370SWAP(margin_A, margin_B);1371callback.swap = !callback.swap;1372}13731374CollisionFunc collision_func;13751376if (p_margin_A || p_margin_B) {1377if (*motion_A == Vector2() && *motion_B == Vector2()) {1378collision_func = collision_table_margin[type_A - 2][type_B - 2];1379} else if (*motion_A != Vector2() && *motion_B == Vector2()) {1380collision_func = collision_table_castA_margin[type_A - 2][type_B - 2];1381} else if (*motion_A == Vector2() && *motion_B != Vector2()) {1382collision_func = collision_table_castB_margin[type_A - 2][type_B - 2];1383} else {1384collision_func = collision_table_castA_castB_margin[type_A - 2][type_B - 2];1385}1386} else {1387if (*motion_A == Vector2() && *motion_B == Vector2()) {1388collision_func = collision_table[type_A - 2][type_B - 2];1389} else if (*motion_A != Vector2() && *motion_B == Vector2()) {1390collision_func = collision_table_castA[type_A - 2][type_B - 2];1391} else if (*motion_A == Vector2() && *motion_B != Vector2()) {1392collision_func = collision_table_castB[type_A - 2][type_B - 2];1393} else {1394collision_func = collision_table_castA_castB[type_A - 2][type_B - 2];1395}1396}13971398ERR_FAIL_NULL_V(collision_func, false);13991400collision_func(A, *transform_A, B, *transform_B, &callback, *motion_A, *motion_B, margin_A, margin_B);14011402return callback.collided;1403}140414051406