Path: blob/master/modules/godot_physics_3d/godot_collision_solver_3d_sat.cpp
10278 views
/**************************************************************************/1/* godot_collision_solver_3d_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_3d_sat.h"3132#include "gjk_epa.h"3334#include "core/math/geometry_3d.h"3536#define fallback_collision_solver gjk_epa_calculate_penetration3738#define _BACKFACE_NORMAL_THRESHOLD -0.00023940// Cylinder SAT analytic methods and face-circle contact points for cylinder-trimesh and cylinder-box collision are based on ODE colliders.4142/*43* Cylinder-trimesh and Cylinder-box colliders by Alen Ladavac44* Ported to ODE by Nguyen Binh45*/4647/*************************************************************************48* *49* Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. *50* All rights reserved. Email: [email protected] Web: www.q12.org *51* *52* This library is free software; you can redistribute it and/or *53* modify it under the terms of EITHER: *54* (1) The GNU Lesser General Public License as published by the Free *55* Software Foundation; either version 2.1 of the License, or (at *56* your option) any later version. The text of the GNU Lesser *57* General Public License is included with this library in the *58* file LICENSE.TXT. *59* (2) The BSD-style license that is included with this library in *60* the file LICENSE-BSD.TXT. *61* *62* This library is distributed in the hope that it will be useful, *63* but WITHOUT ANY WARRANTY; without even the implied warranty of *64* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files *65* LICENSE.TXT and LICENSE-BSD.TXT for more details. *66* *67*************************************************************************/6869struct _CollectorCallback {70GodotCollisionSolver3D::CallbackResult callback = nullptr;71void *userdata = nullptr;72bool swap = false;73bool collided = false;74Vector3 normal;75Vector3 *prev_axis = nullptr;7677_FORCE_INLINE_ void call(const Vector3 &p_point_A, const Vector3 &p_point_B, Vector3 p_normal) {78if (p_normal.dot(p_point_B - p_point_A) < 0) {79p_normal = -p_normal;80}81if (swap) {82callback(p_point_B, 0, p_point_A, 0, -p_normal, userdata);83} else {84callback(p_point_A, 0, p_point_B, 0, p_normal, userdata);85}86}87};8889typedef void (*GenerateContactsFunc)(const Vector3 *, int, const Vector3 *, int, _CollectorCallback *);9091static void _generate_contacts_point_point(const Vector3 *p_points_A, int p_point_count_A, const Vector3 *p_points_B, int p_point_count_B, _CollectorCallback *p_callback) {92#ifdef DEBUG_ENABLED93ERR_FAIL_COND(p_point_count_A != 1);94ERR_FAIL_COND(p_point_count_B != 1);95#endif9697p_callback->call(*p_points_A, *p_points_B, p_callback->normal);98}99100static void _generate_contacts_point_edge(const Vector3 *p_points_A, int p_point_count_A, const Vector3 *p_points_B, int p_point_count_B, _CollectorCallback *p_callback) {101#ifdef DEBUG_ENABLED102ERR_FAIL_COND(p_point_count_A != 1);103ERR_FAIL_COND(p_point_count_B != 2);104#endif105106Vector3 closest_B = Geometry3D::get_closest_point_to_segment_uncapped(*p_points_A, p_points_B[0], p_points_B[1]);107p_callback->call(*p_points_A, closest_B, p_callback->normal);108}109110static void _generate_contacts_point_face(const Vector3 *p_points_A, int p_point_count_A, const Vector3 *p_points_B, int p_point_count_B, _CollectorCallback *p_callback) {111#ifdef DEBUG_ENABLED112ERR_FAIL_COND(p_point_count_A != 1);113ERR_FAIL_COND(p_point_count_B < 3);114#endif115116Plane plane(p_points_B[0], p_points_B[1], p_points_B[2]);117Vector3 closest_B = plane.project(*p_points_A);118p_callback->call(*p_points_A, closest_B, plane.get_normal());119}120121static void _generate_contacts_point_circle(const Vector3 *p_points_A, int p_point_count_A, const Vector3 *p_points_B, int p_point_count_B, _CollectorCallback *p_callback) {122#ifdef DEBUG_ENABLED123ERR_FAIL_COND(p_point_count_A != 1);124ERR_FAIL_COND(p_point_count_B != 3);125#endif126127Plane plane(p_points_B[0], p_points_B[1], p_points_B[2]);128Vector3 closest_B = plane.project(*p_points_A);129p_callback->call(*p_points_A, closest_B, plane.get_normal());130}131132static void _generate_contacts_edge_edge(const Vector3 *p_points_A, int p_point_count_A, const Vector3 *p_points_B, int p_point_count_B, _CollectorCallback *p_callback) {133#ifdef DEBUG_ENABLED134ERR_FAIL_COND(p_point_count_A != 2);135ERR_FAIL_COND(p_point_count_B != 2); // circle is actually a 4x3 matrix136#endif137138Vector3 rel_A = p_points_A[1] - p_points_A[0];139Vector3 rel_B = p_points_B[1] - p_points_B[0];140141Vector3 c = rel_A.cross(rel_B).cross(rel_B);142143if (Math::is_zero_approx(rel_A.dot(c))) {144// should handle somehow..145//ERR_PRINT("TODO FIX");146//return;147148Vector3 axis = rel_A.normalized(); //make an axis149Vector3 base_A = p_points_A[0] - axis * axis.dot(p_points_A[0]);150Vector3 base_B = p_points_B[0] - axis * axis.dot(p_points_B[0]);151152//sort all 4 points in axis153real_t dvec[4] = { axis.dot(p_points_A[0]), axis.dot(p_points_A[1]), axis.dot(p_points_B[0]), axis.dot(p_points_B[1]) };154155SortArray<real_t> sa;156sa.sort(dvec, 4);157158//use the middle ones as contacts159p_callback->call(base_A + axis * dvec[1], base_B + axis * dvec[1], p_callback->normal);160p_callback->call(base_A + axis * dvec[2], base_B + axis * dvec[2], p_callback->normal);161162return;163}164165real_t d = (c.dot(p_points_B[0]) - p_points_A[0].dot(c)) / rel_A.dot(c);166167if (d < 0.0) {168d = 0.0;169} else if (d > 1.0) {170d = 1.0;171}172173const Vector3 closest_A = p_points_A[0] + rel_A * d;174const Vector3 closest_B = Geometry3D::get_closest_point_to_segment_uncapped(closest_A, p_points_B[0], p_points_B[1]);175// The normal should be perpendicular to both edges.176Vector3 normal = rel_A.cross(rel_B);177real_t normal_len = normal.length();178if (normal_len > 1e-3) {179normal /= normal_len;180} else {181normal = p_callback->normal;182}183p_callback->call(closest_A, closest_B, normal);184}185186static void _generate_contacts_edge_circle(const Vector3 *p_points_A, int p_point_count_A, const Vector3 *p_points_B, int p_point_count_B, _CollectorCallback *p_callback) {187#ifdef DEBUG_ENABLED188ERR_FAIL_COND(p_point_count_A != 2);189ERR_FAIL_COND(p_point_count_B != 3);190#endif191192const Vector3 &circle_B_pos = p_points_B[0];193Vector3 circle_B_line_1 = p_points_B[1] - circle_B_pos;194Vector3 circle_B_line_2 = p_points_B[2] - circle_B_pos;195196real_t circle_B_radius = circle_B_line_1.length();197Vector3 circle_B_normal = circle_B_line_1.cross(circle_B_line_2).normalized();198199Plane circle_plane(circle_B_normal, circle_B_pos);200201static const int max_clip = 2;202Vector3 contact_points[max_clip];203int num_points = 0;204205// Project edge point in circle plane.206const Vector3 &edge_A_1 = p_points_A[0];207Vector3 proj_point_1 = circle_plane.project(edge_A_1);208209Vector3 dist_vec = proj_point_1 - circle_B_pos;210real_t dist_sq = dist_vec.length_squared();211212// Point 1 is inside disk, add as contact point.213if (dist_sq <= circle_B_radius * circle_B_radius) {214contact_points[num_points] = edge_A_1;215++num_points;216}217218const Vector3 &edge_A_2 = p_points_A[1];219Vector3 proj_point_2 = circle_plane.project(edge_A_2);220221Vector3 dist_vec_2 = proj_point_2 - circle_B_pos;222real_t dist_sq_2 = dist_vec_2.length_squared();223224// Point 2 is inside disk, add as contact point.225if (dist_sq_2 <= circle_B_radius * circle_B_radius) {226contact_points[num_points] = edge_A_2;227++num_points;228}229230if (num_points < 2) {231Vector3 line_vec = proj_point_2 - proj_point_1;232real_t line_length_sq = line_vec.length_squared();233234// Create a quadratic formula of the form ax^2 + bx + c = 0235real_t a, b, c;236237a = line_length_sq;238b = 2.0 * dist_vec.dot(line_vec);239c = dist_sq - circle_B_radius * circle_B_radius;240241// Solve for t.242real_t sqrtterm = b * b - 4.0 * a * c;243244// If the term we intend to square root is less than 0 then the answer won't be real,245// so the line doesn't intersect.246if (sqrtterm >= 0) {247sqrtterm = Math::sqrt(sqrtterm);248249Vector3 edge_dir = edge_A_2 - edge_A_1;250251real_t fraction_1 = (-b - sqrtterm) / (2.0 * a);252if ((fraction_1 > 0.0) && (fraction_1 < 1.0)) {253Vector3 face_point_1 = edge_A_1 + fraction_1 * edge_dir;254ERR_FAIL_COND(num_points >= max_clip);255contact_points[num_points] = face_point_1;256++num_points;257}258259real_t fraction_2 = (-b + sqrtterm) / (2.0 * a);260if ((fraction_2 > 0.0) && (fraction_2 < 1.0) && !Math::is_equal_approx(fraction_1, fraction_2)) {261Vector3 face_point_2 = edge_A_1 + fraction_2 * edge_dir;262ERR_FAIL_COND(num_points >= max_clip);263contact_points[num_points] = face_point_2;264++num_points;265}266}267}268269// Generate contact points.270for (int i = 0; i < num_points; i++) {271const Vector3 &contact_point_A = contact_points[i];272273real_t d = circle_plane.distance_to(contact_point_A);274Vector3 closest_B = contact_point_A - circle_plane.normal * d;275276if (p_callback->normal.dot(contact_point_A) >= p_callback->normal.dot(closest_B)) {277continue;278}279280p_callback->call(contact_point_A, closest_B, circle_plane.get_normal());281}282}283284static void _generate_contacts_face_face(const Vector3 *p_points_A, int p_point_count_A, const Vector3 *p_points_B, int p_point_count_B, _CollectorCallback *p_callback) {285#ifdef DEBUG_ENABLED286ERR_FAIL_COND(p_point_count_A < 2);287ERR_FAIL_COND(p_point_count_B < 3);288#endif289290static const int max_clip = 32;291292Vector3 _clipbuf1[max_clip];293Vector3 _clipbuf2[max_clip];294Vector3 *clipbuf_src = _clipbuf1;295Vector3 *clipbuf_dst = _clipbuf2;296int clipbuf_len = p_point_count_A;297298// copy A points to clipbuf_src299for (int i = 0; i < p_point_count_A; i++) {300clipbuf_src[i] = p_points_A[i];301}302303Plane plane_B(p_points_B[0], p_points_B[1], p_points_B[2]);304305// go through all of B points306for (int i = 0; i < p_point_count_B; i++) {307int i_n = (i + 1) % p_point_count_B;308309Vector3 edge0_B = p_points_B[i];310Vector3 edge1_B = p_points_B[i_n];311312Vector3 clip_normal = (edge0_B - edge1_B).cross(plane_B.normal).normalized();313// make a clip plane314315Plane clip(clip_normal, edge0_B);316// avoid double clip if A is edge317int dst_idx = 0;318bool edge = clipbuf_len == 2;319for (int j = 0; j < clipbuf_len; j++) {320int j_n = (j + 1) % clipbuf_len;321322Vector3 edge0_A = clipbuf_src[j];323Vector3 edge1_A = clipbuf_src[j_n];324325real_t dist0 = clip.distance_to(edge0_A);326real_t dist1 = clip.distance_to(edge1_A);327328if (dist0 <= 0) { // behind plane329330ERR_FAIL_COND(dst_idx >= max_clip);331clipbuf_dst[dst_idx++] = clipbuf_src[j];332}333334// check for different sides and non coplanar335//if ( (dist0*dist1) < -CMP_EPSILON && !(edge && j)) {336if ((dist0 * dist1) < 0 && !(edge && j)) {337// calculate intersection338Vector3 rel = edge1_A - edge0_A;339real_t den = clip.normal.dot(rel);340real_t dist = -(clip.normal.dot(edge0_A) - clip.d) / den;341Vector3 inters = edge0_A + rel * dist;342343ERR_FAIL_COND(dst_idx >= max_clip);344clipbuf_dst[dst_idx] = inters;345dst_idx++;346}347}348349clipbuf_len = dst_idx;350SWAP(clipbuf_src, clipbuf_dst);351}352353// generate contacts354//Plane plane_A(p_points_A[0],p_points_A[1],p_points_A[2]);355356for (int i = 0; i < clipbuf_len; i++) {357real_t d = plane_B.distance_to(clipbuf_src[i]);358359Vector3 closest_B = clipbuf_src[i] - plane_B.normal * d;360361if (p_callback->normal.dot(clipbuf_src[i]) >= p_callback->normal.dot(closest_B)) {362continue;363}364365p_callback->call(clipbuf_src[i], closest_B, plane_B.get_normal());366}367}368369static void _generate_contacts_face_circle(const Vector3 *p_points_A, int p_point_count_A, const Vector3 *p_points_B, int p_point_count_B, _CollectorCallback *p_callback) {370#ifdef DEBUG_ENABLED371ERR_FAIL_COND(p_point_count_A < 3);372ERR_FAIL_COND(p_point_count_B != 3);373#endif374375const Vector3 &circle_B_pos = p_points_B[0];376Vector3 circle_B_line_1 = p_points_B[1] - circle_B_pos;377Vector3 circle_B_line_2 = p_points_B[2] - circle_B_pos;378379// Clip face with circle segments.380static const int circle_segments = 8;381Vector3 circle_points[circle_segments];382383real_t angle_delta = 2.0 * Math::PI / circle_segments;384385for (int i = 0; i < circle_segments; ++i) {386Vector3 point_pos = circle_B_pos;387point_pos += circle_B_line_1 * Math::cos(i * angle_delta);388point_pos += circle_B_line_2 * Math::sin(i * angle_delta);389circle_points[i] = point_pos;390}391392_generate_contacts_face_face(p_points_A, p_point_count_A, circle_points, circle_segments, p_callback);393394// Clip face with circle plane.395Vector3 circle_B_normal = circle_B_line_1.cross(circle_B_line_2).normalized();396397Plane circle_plane(circle_B_normal, circle_B_pos);398399static const int max_clip = 32;400Vector3 contact_points[max_clip];401int num_points = 0;402403for (int i = 0; i < p_point_count_A; i++) {404int i_n = (i + 1) % p_point_count_A;405406const Vector3 &edge0_A = p_points_A[i];407const Vector3 &edge1_A = p_points_A[i_n];408409real_t dist0 = circle_plane.distance_to(edge0_A);410real_t dist1 = circle_plane.distance_to(edge1_A);411412// First point in front of plane, generate contact point.413if (dist0 * circle_plane.d >= 0) {414ERR_FAIL_COND(num_points >= max_clip);415contact_points[num_points] = edge0_A;416++num_points;417}418419// Points on different sides, generate contact point.420if (dist0 * dist1 < 0) {421// calculate intersection422Vector3 rel = edge1_A - edge0_A;423real_t den = circle_plane.normal.dot(rel);424real_t dist = -(circle_plane.normal.dot(edge0_A) - circle_plane.d) / den;425Vector3 inters = edge0_A + rel * dist;426427ERR_FAIL_COND(num_points >= max_clip);428contact_points[num_points] = inters;429++num_points;430}431}432433// Generate contact points.434for (int i = 0; i < num_points; i++) {435const Vector3 &contact_point_A = contact_points[i];436437real_t d = circle_plane.distance_to(contact_point_A);438Vector3 closest_B = contact_point_A - circle_plane.normal * d;439440if (p_callback->normal.dot(contact_point_A) >= p_callback->normal.dot(closest_B)) {441continue;442}443444p_callback->call(contact_point_A, closest_B, circle_plane.get_normal());445}446}447448static void _generate_contacts_circle_circle(const Vector3 *p_points_A, int p_point_count_A, const Vector3 *p_points_B, int p_point_count_B, _CollectorCallback *p_callback) {449#ifdef DEBUG_ENABLED450ERR_FAIL_COND(p_point_count_A != 3);451ERR_FAIL_COND(p_point_count_B != 3);452#endif453454const Vector3 &circle_A_pos = p_points_A[0];455Vector3 circle_A_line_1 = p_points_A[1] - circle_A_pos;456Vector3 circle_A_line_2 = p_points_A[2] - circle_A_pos;457458real_t circle_A_radius = circle_A_line_1.length();459Vector3 circle_A_normal = circle_A_line_1.cross(circle_A_line_2).normalized();460461const Vector3 &circle_B_pos = p_points_B[0];462Vector3 circle_B_line_1 = p_points_B[1] - circle_B_pos;463Vector3 circle_B_line_2 = p_points_B[2] - circle_B_pos;464465real_t circle_B_radius = circle_B_line_1.length();466Vector3 circle_B_normal = circle_B_line_1.cross(circle_B_line_2).normalized();467468static const int max_clip = 4;469Vector3 contact_points[max_clip];470int num_points = 0;471472Vector3 centers_diff = circle_B_pos - circle_A_pos;473Vector3 norm_proj = circle_A_normal.dot(centers_diff) * circle_A_normal;474Vector3 comp_proj = centers_diff - norm_proj;475real_t proj_dist = comp_proj.length();476if (!Math::is_zero_approx(proj_dist)) {477comp_proj /= proj_dist;478if ((proj_dist > circle_A_radius - circle_B_radius) && (proj_dist > circle_B_radius - circle_A_radius)) {479// Circles are overlapping, use the 2 points of intersection as contacts.480real_t radius_a_sqr = circle_A_radius * circle_A_radius;481real_t radius_b_sqr = circle_B_radius * circle_B_radius;482real_t d_sqr = proj_dist * proj_dist;483real_t s = (1.0 + (radius_a_sqr - radius_b_sqr) / d_sqr) * 0.5;484real_t h = Math::sqrt(MAX(radius_a_sqr - d_sqr * s * s, 0.0));485Vector3 midpoint = circle_A_pos + s * comp_proj * proj_dist;486Vector3 h_vec = h * circle_A_normal.cross(comp_proj);487488Vector3 point_A = midpoint + h_vec;489contact_points[num_points] = point_A;490++num_points;491492point_A = midpoint - h_vec;493contact_points[num_points] = point_A;494++num_points;495496// Add 2 points from circle A and B along the line between the centers.497point_A = circle_A_pos + comp_proj * circle_A_radius;498contact_points[num_points] = point_A;499++num_points;500501point_A = circle_B_pos - comp_proj * circle_B_radius - norm_proj;502contact_points[num_points] = point_A;503++num_points;504} // Otherwise one circle is inside the other one, use 3 arbitrary equidistant points.505} // Otherwise circles are concentric, use 3 arbitrary equidistant points.506507if (num_points == 0) {508// Generate equidistant points.509if (circle_A_radius < circle_B_radius) {510// Circle A inside circle B.511for (int i = 0; i < 3; ++i) {512Vector3 circle_A_point = circle_A_pos;513circle_A_point += circle_A_line_1 * Math::cos(2.0 * Math::PI * i / 3.0);514circle_A_point += circle_A_line_2 * Math::sin(2.0 * Math::PI * i / 3.0);515516contact_points[num_points] = circle_A_point;517++num_points;518}519} else {520// Circle B inside circle A.521for (int i = 0; i < 3; ++i) {522Vector3 circle_B_point = circle_B_pos;523circle_B_point += circle_B_line_1 * Math::cos(2.0 * Math::PI * i / 3.0);524circle_B_point += circle_B_line_2 * Math::sin(2.0 * Math::PI * i / 3.0);525526Vector3 circle_A_point = circle_B_point - norm_proj;527528contact_points[num_points] = circle_A_point;529++num_points;530}531}532}533534Plane circle_B_plane(circle_B_normal, circle_B_pos);535536// Generate contact points.537for (int i = 0; i < num_points; i++) {538const Vector3 &contact_point_A = contact_points[i];539540real_t d = circle_B_plane.distance_to(contact_point_A);541Vector3 closest_B = contact_point_A - circle_B_plane.normal * d;542543if (p_callback->normal.dot(contact_point_A) >= p_callback->normal.dot(closest_B)) {544continue;545}546547p_callback->call(contact_point_A, closest_B, circle_B_plane.get_normal());548}549}550551static void _generate_contacts_from_supports(const Vector3 *p_points_A, int p_point_count_A, GodotShape3D::FeatureType p_feature_type_A, const Vector3 *p_points_B, int p_point_count_B, GodotShape3D::FeatureType p_feature_type_B, _CollectorCallback *p_callback) {552#ifdef DEBUG_ENABLED553ERR_FAIL_COND(p_point_count_A < 1);554ERR_FAIL_COND(p_point_count_B < 1);555#endif556557static const GenerateContactsFunc generate_contacts_func_table[4][4] = {558{559_generate_contacts_point_point,560_generate_contacts_point_edge,561_generate_contacts_point_face,562_generate_contacts_point_circle,563},564{565nullptr,566_generate_contacts_edge_edge,567_generate_contacts_face_face,568_generate_contacts_edge_circle,569},570{571nullptr,572nullptr,573_generate_contacts_face_face,574_generate_contacts_face_circle,575},576{577nullptr,578nullptr,579nullptr,580_generate_contacts_circle_circle,581},582};583584int pointcount_B;585int pointcount_A;586const Vector3 *points_A;587const Vector3 *points_B;588int version_A;589int version_B;590591if (p_feature_type_A > p_feature_type_B) {592//swap593p_callback->swap = !p_callback->swap;594p_callback->normal = -p_callback->normal;595596pointcount_B = p_point_count_A;597pointcount_A = p_point_count_B;598points_A = p_points_B;599points_B = p_points_A;600version_A = p_feature_type_B;601version_B = p_feature_type_A;602} else {603pointcount_B = p_point_count_B;604pointcount_A = p_point_count_A;605points_A = p_points_A;606points_B = p_points_B;607version_A = p_feature_type_A;608version_B = p_feature_type_B;609}610611GenerateContactsFunc contacts_func = generate_contacts_func_table[version_A][version_B];612ERR_FAIL_NULL(contacts_func);613contacts_func(points_A, pointcount_A, points_B, pointcount_B, p_callback);614}615616template <typename ShapeA, typename ShapeB, bool withMargin = false>617class SeparatorAxisTest {618const ShapeA *shape_A = nullptr;619const ShapeB *shape_B = nullptr;620const Transform3D *transform_A = nullptr;621const Transform3D *transform_B = nullptr;622real_t best_depth = 1e15;623_CollectorCallback *callback = nullptr;624real_t margin_A = 0.0;625real_t margin_B = 0.0;626Vector3 separator_axis;627628public:629Vector3 best_axis;630631_FORCE_INLINE_ bool test_previous_axis() {632if (callback && callback->prev_axis && *callback->prev_axis != Vector3()) {633return test_axis(*callback->prev_axis);634} else {635return true;636}637}638639_FORCE_INLINE_ bool test_axis(const Vector3 &p_axis) {640Vector3 axis = p_axis;641642if (axis.is_zero_approx()) {643// strange case, try an upwards separator644axis = Vector3(0.0, 1.0, 0.0);645}646647real_t min_A = 0.0, max_A = 0.0, min_B = 0.0, max_B = 0.0;648649shape_A->project_range(axis, *transform_A, min_A, max_A);650shape_B->project_range(axis, *transform_B, min_B, max_B);651652if (withMargin) {653min_A -= margin_A;654max_A += margin_A;655min_B -= margin_B;656max_B += margin_B;657}658659min_B -= (max_A - min_A) * 0.5;660max_B += (max_A - min_A) * 0.5;661662min_B -= (min_A + max_A) * 0.5;663max_B -= (min_A + max_A) * 0.5;664665if (min_B > 0.0 || max_B < 0.0) {666separator_axis = axis;667return false; // doesn't contain 0668}669670//use the smallest depth671672if (min_B < 0.0) { // could be +0.0, we don't want it to become -0.0673min_B = -min_B;674}675676if (max_B < min_B) {677if (max_B < best_depth) {678best_depth = max_B;679best_axis = axis;680}681} else {682if (min_B < best_depth) {683best_depth = min_B;684best_axis = -axis; // keep it as A axis685}686}687688return true;689}690691static _FORCE_INLINE_ void test_contact_points(const Vector3 &p_point_A, int p_index_A, const Vector3 &p_point_B, int p_index_B, const Vector3 &normal, void *p_userdata) {692SeparatorAxisTest<ShapeA, ShapeB, withMargin> *separator = (SeparatorAxisTest<ShapeA, ShapeB, withMargin> *)p_userdata;693Vector3 axis = (p_point_B - p_point_A);694real_t depth = axis.length();695696// Filter out bogus directions with a threshold and re-testing axis.697if (separator->best_depth - depth > 0.001) {698separator->test_axis(axis / depth);699}700}701702_FORCE_INLINE_ void generate_contacts() {703// nothing to do, don't generate704if (best_axis == Vector3(0.0, 0.0, 0.0)) {705return;706}707708if (!callback->callback) {709//just was checking intersection?710callback->collided = true;711if (callback->prev_axis) {712*callback->prev_axis = best_axis;713}714return;715}716717static const int max_supports = 16;718719Vector3 supports_A[max_supports];720int support_count_A;721GodotShape3D::FeatureType support_type_A;722shape_A->get_supports(transform_A->basis.xform_inv(-best_axis).normalized(), max_supports, supports_A, support_count_A, support_type_A);723for (int i = 0; i < support_count_A; i++) {724supports_A[i] = transform_A->xform(supports_A[i]);725}726727if (withMargin) {728for (int i = 0; i < support_count_A; i++) {729supports_A[i] += -best_axis * margin_A;730}731}732733Vector3 supports_B[max_supports];734int support_count_B;735GodotShape3D::FeatureType support_type_B;736shape_B->get_supports(transform_B->basis.xform_inv(best_axis).normalized(), max_supports, supports_B, support_count_B, support_type_B);737for (int i = 0; i < support_count_B; i++) {738supports_B[i] = transform_B->xform(supports_B[i]);739}740741if (withMargin) {742for (int i = 0; i < support_count_B; i++) {743supports_B[i] += best_axis * margin_B;744}745}746747callback->normal = best_axis;748if (callback->prev_axis) {749*callback->prev_axis = best_axis;750}751_generate_contacts_from_supports(supports_A, support_count_A, support_type_A, supports_B, support_count_B, support_type_B, callback);752753callback->collided = true;754}755756_FORCE_INLINE_ SeparatorAxisTest(const ShapeA *p_shape_A, const Transform3D &p_transform_A, const ShapeB *p_shape_B, const Transform3D &p_transform_B, _CollectorCallback *p_callback, real_t p_margin_A = 0, real_t p_margin_B = 0) {757shape_A = p_shape_A;758shape_B = p_shape_B;759transform_A = &p_transform_A;760transform_B = &p_transform_B;761callback = p_callback;762margin_A = p_margin_A;763margin_B = p_margin_B;764}765};766767/****** SAT TESTS *******/768769typedef void (*CollisionFunc)(const GodotShape3D *, const Transform3D &, const GodotShape3D *, const Transform3D &, _CollectorCallback *p_callback, real_t, real_t);770771// Perform analytic sphere-sphere collision and report results to collector772template <bool withMargin>773static void analytic_sphere_collision(const Vector3 &p_origin_a, real_t p_radius_a, const Vector3 &p_origin_b, real_t p_radius_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {774// Expand the spheres by the margins if enabled775if (withMargin) {776p_radius_a += p_margin_a;777p_radius_b += p_margin_b;778}779780// Get the vector from sphere B to A781Vector3 b_to_a = p_origin_a - p_origin_b;782783// Get the length from B to A784real_t b_to_a_len = b_to_a.length();785786// Calculate the sphere overlap, and bail if not overlapping787real_t overlap = p_radius_a + p_radius_b - b_to_a_len;788if (overlap < 0) {789return;790}791792// Report collision793p_collector->collided = true;794795// Bail if there is no callback to receive the A and B collision points.796if (!p_collector->callback) {797return;798}799800// Normalize the B to A vector801if (b_to_a_len < CMP_EPSILON) {802b_to_a = Vector3(0, 1, 0); // Spheres coincident, use arbitrary direction803} else {804b_to_a /= b_to_a_len;805}806807// Report collision points. The operations below are intended to minimize808// floating-point precision errors. This is done by calculating the first809// collision point from the smaller sphere, and then jumping across to810// the larger spheres collision point using the overlap distance. This811// jump is usually small even if the large sphere is massive, and so the812// second point will not suffer from precision errors.813if (p_radius_a < p_radius_b) {814Vector3 point_a = p_origin_a - b_to_a * p_radius_a;815Vector3 point_b = point_a + b_to_a * overlap;816p_collector->call(point_a, point_b, b_to_a); // Consider adding b_to_a vector817} else {818Vector3 point_b = p_origin_b + b_to_a * p_radius_b;819Vector3 point_a = point_b - b_to_a * overlap;820p_collector->call(point_a, point_b, b_to_a); // Consider adding b_to_a vector821}822}823824template <bool withMargin>825static void _collision_sphere_sphere(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {826const GodotSphereShape3D *sphere_A = static_cast<const GodotSphereShape3D *>(p_a);827const GodotSphereShape3D *sphere_B = static_cast<const GodotSphereShape3D *>(p_b);828829// Perform an analytic sphere collision between the two spheres830analytic_sphere_collision<withMargin>(831p_transform_a.origin,832sphere_A->get_radius() * p_transform_a.basis[0].length(),833p_transform_b.origin,834sphere_B->get_radius() * p_transform_b.basis[0].length(),835p_collector,836p_margin_a,837p_margin_b);838}839840template <bool withMargin>841static void _collision_sphere_box(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {842const GodotSphereShape3D *sphere_A = static_cast<const GodotSphereShape3D *>(p_a);843const GodotBoxShape3D *box_B = static_cast<const GodotBoxShape3D *>(p_b);844845// Find the point on the box nearest to the center of the sphere.846847Vector3 center = p_transform_b.affine_inverse().xform(p_transform_a.origin);848Vector3 extents = box_B->get_half_extents();849Vector3 nearest(MIN(MAX(center.x, -extents.x), extents.x),850MIN(MAX(center.y, -extents.y), extents.y),851MIN(MAX(center.z, -extents.z), extents.z));852nearest = p_transform_b.xform(nearest);853854// See if it is inside the sphere.855856Vector3 delta = nearest - p_transform_a.origin;857real_t length = delta.length();858real_t radius = sphere_A->get_radius() * p_transform_a.basis[0].length();859if (length > radius + p_margin_a + p_margin_b) {860return;861}862p_collector->collided = true;863if (!p_collector->callback) {864return;865}866Vector3 axis;867if (length == 0) {868// The box passes through the sphere center. Select an axis based on the box's center.869axis = (p_transform_b.origin - nearest).normalized();870} else {871axis = delta / length;872}873Vector3 point_a = p_transform_a.origin + (radius + p_margin_a) * axis;874Vector3 point_b = (withMargin ? nearest - p_margin_b * axis : nearest);875p_collector->call(point_a, point_b, axis);876}877878template <bool withMargin>879static void _collision_sphere_capsule(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {880const GodotSphereShape3D *sphere_A = static_cast<const GodotSphereShape3D *>(p_a);881const GodotCapsuleShape3D *capsule_B = static_cast<const GodotCapsuleShape3D *>(p_b);882883real_t scale_A = p_transform_a.basis[0].length();884real_t scale_B = p_transform_b.basis[0].length();885886// Construct the capsule segment (ball-center to ball-center)887Vector3 capsule_axis = p_transform_b.basis.get_column(1) * (capsule_B->get_height() * 0.5 - capsule_B->get_radius());888const Vector3 capsule_segment_a = p_transform_b.origin + capsule_axis;889const Vector3 capsule_segment_b = p_transform_b.origin - capsule_axis;890891// Get the capsules closest segment-point to the sphere892Vector3 capsule_closest = Geometry3D::get_closest_point_to_segment(p_transform_a.origin, capsule_segment_a, capsule_segment_b);893894// Perform an analytic sphere collision between the sphere and the sphere-collider in the capsule895analytic_sphere_collision<withMargin>(896p_transform_a.origin,897sphere_A->get_radius() * scale_A,898capsule_closest,899capsule_B->get_radius() * scale_B,900p_collector,901p_margin_a,902p_margin_b);903}904905template <bool withMargin>906static void analytic_sphere_cylinder_collision(real_t p_radius_a, real_t p_radius_b, real_t p_height_b, const Transform3D &p_transform_a, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {907// Find the point on the cylinder nearest to the center of the sphere.908909Vector3 center = p_transform_b.affine_inverse().xform(p_transform_a.origin);910Vector3 nearest = center;911real_t scale_A = p_transform_a.basis[0].length();912real_t r = Math::sqrt(center.x * center.x + center.z * center.z);913if (r > p_radius_b) {914real_t scale = p_radius_b / r;915nearest.x *= scale;916nearest.z *= scale;917}918real_t half_height = p_height_b / 2;919nearest.y = MIN(MAX(center.y, -half_height), half_height);920nearest = p_transform_b.xform(nearest);921922// See if it is inside the sphere.923924Vector3 delta = nearest - p_transform_a.origin;925real_t length = delta.length();926if (length > p_radius_a * scale_A + p_margin_a + p_margin_b) {927return;928}929p_collector->collided = true;930if (!p_collector->callback) {931return;932}933Vector3 axis;934if (length == 0) {935// The cylinder passes through the sphere center. Select an axis based on the cylinder's center.936axis = (p_transform_b.origin - nearest).normalized();937} else {938axis = delta / length;939}940Vector3 point_a = p_transform_a.origin + (p_radius_a * scale_A + p_margin_a) * axis;941Vector3 point_b = (withMargin ? nearest - p_margin_b * axis : nearest);942p_collector->call(point_a, point_b, axis);943}944945template <bool withMargin>946static void _collision_sphere_cylinder(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {947const GodotSphereShape3D *sphere_A = static_cast<const GodotSphereShape3D *>(p_a);948const GodotCylinderShape3D *cylinder_B = static_cast<const GodotCylinderShape3D *>(p_b);949950analytic_sphere_cylinder_collision<withMargin>(sphere_A->get_radius(), cylinder_B->get_radius(), cylinder_B->get_height(), p_transform_a, p_transform_b, p_collector, p_margin_a, p_margin_b);951}952953template <bool withMargin>954static void _collision_sphere_convex_polygon(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {955const GodotSphereShape3D *sphere_A = static_cast<const GodotSphereShape3D *>(p_a);956const GodotConvexPolygonShape3D *convex_polygon_B = static_cast<const GodotConvexPolygonShape3D *>(p_b);957958SeparatorAxisTest<GodotSphereShape3D, GodotConvexPolygonShape3D, withMargin> separator(sphere_A, p_transform_a, convex_polygon_B, p_transform_b, p_collector, p_margin_a, p_margin_b);959960if (!separator.test_previous_axis()) {961return;962}963964const Geometry3D::MeshData &mesh = convex_polygon_B->get_mesh();965966const Geometry3D::MeshData::Face *faces = mesh.faces.ptr();967int face_count = mesh.faces.size();968const Geometry3D::MeshData::Edge *edges = mesh.edges.ptr();969int edge_count = mesh.edges.size();970const Vector3 *vertices = mesh.vertices.ptr();971int vertex_count = mesh.vertices.size();972973// Precalculating this makes the transforms faster.974Basis b_xform_normal = p_transform_b.basis.inverse().transposed();975976// faces of B977for (int i = 0; i < face_count; i++) {978Vector3 axis = b_xform_normal.xform(faces[i].plane.normal).normalized();979980if (!separator.test_axis(axis)) {981return;982}983}984985// edges of B986for (int i = 0; i < edge_count; i++) {987Vector3 v1 = p_transform_b.xform(vertices[edges[i].vertex_a]);988Vector3 v2 = p_transform_b.xform(vertices[edges[i].vertex_b]);989Vector3 v3 = p_transform_a.origin;990991Vector3 n1 = v2 - v1;992Vector3 n2 = v2 - v3;993994Vector3 axis = n1.cross(n2).cross(n1).normalized();995996if (!separator.test_axis(axis)) {997return;998}999}10001001// vertices of B1002for (int i = 0; i < vertex_count; i++) {1003Vector3 v1 = p_transform_b.xform(vertices[i]);1004Vector3 v2 = p_transform_a.origin;10051006Vector3 axis = (v2 - v1).normalized();10071008if (!separator.test_axis(axis)) {1009return;1010}1011}10121013separator.generate_contacts();1014}10151016template <bool withMargin>1017static void _collision_sphere_face(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {1018const GodotSphereShape3D *sphere_A = static_cast<const GodotSphereShape3D *>(p_a);1019const GodotFaceShape3D *face_B = static_cast<const GodotFaceShape3D *>(p_b);10201021SeparatorAxisTest<GodotSphereShape3D, GodotFaceShape3D, withMargin> separator(sphere_A, p_transform_a, face_B, p_transform_b, p_collector, p_margin_a, p_margin_b);10221023Vector3 vertex[3] = {1024p_transform_b.xform(face_B->vertex[0]),1025p_transform_b.xform(face_B->vertex[1]),1026p_transform_b.xform(face_B->vertex[2]),1027};10281029Vector3 normal = (vertex[0] - vertex[2]).cross(vertex[0] - vertex[1]).normalized();10301031if (!separator.test_axis(normal)) {1032return;1033}10341035// edges and points of B1036for (int i = 0; i < 3; i++) {1037Vector3 n1 = vertex[i] - p_transform_a.origin;1038if (n1.dot(normal) < 0.0) {1039n1 *= -1.0;1040}10411042if (!separator.test_axis(n1.normalized())) {1043return;1044}10451046Vector3 n2 = vertex[(i + 1) % 3] - vertex[i];10471048Vector3 axis = n1.cross(n2).cross(n2).normalized();1049if (axis.dot(normal) < 0.0) {1050axis *= -1.0;1051}10521053if (!separator.test_axis(axis)) {1054return;1055}1056}10571058if (!face_B->backface_collision) {1059if (separator.best_axis.dot(normal) < _BACKFACE_NORMAL_THRESHOLD) {1060if (face_B->invert_backface_collision) {1061separator.best_axis = separator.best_axis.bounce(normal);1062} else {1063// Just ignore backface collision.1064return;1065}1066}1067}10681069separator.generate_contacts();1070}10711072template <bool withMargin>1073static void _collision_box_box(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {1074const GodotBoxShape3D *box_A = static_cast<const GodotBoxShape3D *>(p_a);1075const GodotBoxShape3D *box_B = static_cast<const GodotBoxShape3D *>(p_b);10761077SeparatorAxisTest<GodotBoxShape3D, GodotBoxShape3D, withMargin> separator(box_A, p_transform_a, box_B, p_transform_b, p_collector, p_margin_a, p_margin_b);10781079if (!separator.test_previous_axis()) {1080return;1081}10821083// test faces of A10841085for (int i = 0; i < 3; i++) {1086Vector3 axis = p_transform_a.basis.get_column(i).normalized();10871088if (!separator.test_axis(axis)) {1089return;1090}1091}10921093// test faces of B10941095for (int i = 0; i < 3; i++) {1096Vector3 axis = p_transform_b.basis.get_column(i).normalized();10971098if (!separator.test_axis(axis)) {1099return;1100}1101}11021103// test combined edges1104for (int i = 0; i < 3; i++) {1105for (int j = 0; j < 3; j++) {1106Vector3 axis = p_transform_a.basis.get_column(i).cross(p_transform_b.basis.get_column(j));11071108if (Math::is_zero_approx(axis.length_squared())) {1109continue;1110}1111axis.normalize();11121113if (!separator.test_axis(axis)) {1114return;1115}1116}1117}11181119if (withMargin) {1120//add endpoint test between closest vertices and edges11211122// calculate closest point to sphere11231124Vector3 ab_vec = p_transform_b.origin - p_transform_a.origin;11251126Vector3 cnormal_a = p_transform_a.basis.xform_inv(ab_vec);11271128Vector3 support_a = p_transform_a.xform(Vector3(11291130(cnormal_a.x < 0) ? -box_A->get_half_extents().x : box_A->get_half_extents().x,1131(cnormal_a.y < 0) ? -box_A->get_half_extents().y : box_A->get_half_extents().y,1132(cnormal_a.z < 0) ? -box_A->get_half_extents().z : box_A->get_half_extents().z));11331134Vector3 cnormal_b = p_transform_b.basis.xform_inv(-ab_vec);11351136Vector3 support_b = p_transform_b.xform(Vector3(11371138(cnormal_b.x < 0) ? -box_B->get_half_extents().x : box_B->get_half_extents().x,1139(cnormal_b.y < 0) ? -box_B->get_half_extents().y : box_B->get_half_extents().y,1140(cnormal_b.z < 0) ? -box_B->get_half_extents().z : box_B->get_half_extents().z));11411142Vector3 axis_ab = (support_a - support_b);11431144if (!separator.test_axis(axis_ab.normalized())) {1145return;1146}11471148//now try edges, which become cylinders!11491150for (int i = 0; i < 3; i++) {1151//a ->b1152Vector3 axis_a = p_transform_a.basis.get_column(i);11531154if (!separator.test_axis(axis_ab.cross(axis_a).cross(axis_a).normalized())) {1155return;1156}11571158//b ->a1159Vector3 axis_b = p_transform_b.basis.get_column(i);11601161if (!separator.test_axis(axis_ab.cross(axis_b).cross(axis_b).normalized())) {1162return;1163}1164}1165}11661167separator.generate_contacts();1168}11691170template <bool withMargin>1171static void _collision_box_capsule(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {1172const GodotBoxShape3D *box_A = static_cast<const GodotBoxShape3D *>(p_a);1173const GodotCapsuleShape3D *capsule_B = static_cast<const GodotCapsuleShape3D *>(p_b);11741175SeparatorAxisTest<GodotBoxShape3D, GodotCapsuleShape3D, withMargin> separator(box_A, p_transform_a, capsule_B, p_transform_b, p_collector, p_margin_a, p_margin_b);11761177if (!separator.test_previous_axis()) {1178return;1179}11801181// faces of A1182for (int i = 0; i < 3; i++) {1183Vector3 axis = p_transform_a.basis.get_column(i).normalized();11841185if (!separator.test_axis(axis)) {1186return;1187}1188}11891190Vector3 cyl_axis = p_transform_b.basis.get_column(1).normalized();11911192// edges of A, capsule cylinder11931194for (int i = 0; i < 3; i++) {1195// cylinder1196Vector3 box_axis = p_transform_a.basis.get_column(i);1197Vector3 axis = box_axis.cross(cyl_axis);1198if (Math::is_zero_approx(axis.length_squared())) {1199continue;1200}12011202if (!separator.test_axis(axis.normalized())) {1203return;1204}1205}12061207// points of A, capsule cylinder1208// this sure could be made faster somehow..12091210for (int i = 0; i < 2; i++) {1211for (int j = 0; j < 2; j++) {1212for (int k = 0; k < 2; k++) {1213Vector3 he = box_A->get_half_extents();1214he.x *= (i * 2 - 1);1215he.y *= (j * 2 - 1);1216he.z *= (k * 2 - 1);1217Vector3 point = p_transform_a.origin;1218for (int l = 0; l < 3; l++) {1219point += p_transform_a.basis.get_column(l) * he[l];1220}12211222//Vector3 axis = (point - cyl_axis * cyl_axis.dot(point)).normalized();1223Vector3 axis = Plane(cyl_axis).project(point).normalized();12241225if (!separator.test_axis(axis)) {1226return;1227}1228}1229}1230}12311232// capsule balls, edges of A12331234for (int i = 0; i < 2; i++) {1235Vector3 capsule_axis = p_transform_b.basis.get_column(1) * (capsule_B->get_height() * 0.5 - capsule_B->get_radius());12361237Vector3 sphere_pos = p_transform_b.origin + ((i == 0) ? capsule_axis : -capsule_axis);12381239Vector3 cnormal = p_transform_a.xform_inv(sphere_pos);12401241Vector3 cpoint = p_transform_a.xform(Vector3(12421243(cnormal.x < 0) ? -box_A->get_half_extents().x : box_A->get_half_extents().x,1244(cnormal.y < 0) ? -box_A->get_half_extents().y : box_A->get_half_extents().y,1245(cnormal.z < 0) ? -box_A->get_half_extents().z : box_A->get_half_extents().z));12461247// use point to test axis1248Vector3 point_axis = (sphere_pos - cpoint).normalized();12491250if (!separator.test_axis(point_axis)) {1251return;1252}12531254// test edges of A12551256for (int j = 0; j < 3; j++) {1257Vector3 axis = point_axis.cross(p_transform_a.basis.get_column(j)).cross(p_transform_a.basis.get_column(j)).normalized();12581259if (!separator.test_axis(axis)) {1260return;1261}1262}1263}12641265separator.generate_contacts();1266}12671268template <bool withMargin>1269static void _collision_box_cylinder(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {1270const GodotBoxShape3D *box_A = static_cast<const GodotBoxShape3D *>(p_a);1271const GodotCylinderShape3D *cylinder_B = static_cast<const GodotCylinderShape3D *>(p_b);12721273SeparatorAxisTest<GodotBoxShape3D, GodotCylinderShape3D, withMargin> separator(box_A, p_transform_a, cylinder_B, p_transform_b, p_collector, p_margin_a, p_margin_b);12741275if (!separator.test_previous_axis()) {1276return;1277}12781279// Faces of A.1280for (int i = 0; i < 3; i++) {1281Vector3 axis = p_transform_a.basis.get_column(i).normalized();12821283if (!separator.test_axis(axis)) {1284return;1285}1286}12871288Vector3 cyl_axis = p_transform_b.basis.get_column(1).normalized();12891290// Cylinder end caps.1291{1292if (!separator.test_axis(cyl_axis)) {1293return;1294}1295}12961297// Edges of A, cylinder lateral surface.1298for (int i = 0; i < 3; i++) {1299Vector3 box_axis = p_transform_a.basis.get_column(i);1300Vector3 axis = box_axis.cross(cyl_axis);1301if (Math::is_zero_approx(axis.length_squared())) {1302continue;1303}13041305if (!separator.test_axis(axis.normalized())) {1306return;1307}1308}13091310// Gather points of A.1311Vector3 vertices_A[8];1312Vector3 box_extent = box_A->get_half_extents();1313for (int i = 0; i < 2; i++) {1314for (int j = 0; j < 2; j++) {1315for (int k = 0; k < 2; k++) {1316Vector3 extent = box_extent;1317extent.x *= (i * 2 - 1);1318extent.y *= (j * 2 - 1);1319extent.z *= (k * 2 - 1);1320Vector3 &point = vertices_A[i * 2 * 2 + j * 2 + k];1321point = p_transform_a.origin;1322for (int l = 0; l < 3; l++) {1323point += p_transform_a.basis.get_column(l) * extent[l];1324}1325}1326}1327}13281329// Points of A, cylinder lateral surface.1330for (int i = 0; i < 8; i++) {1331const Vector3 &point = vertices_A[i];1332Vector3 axis = Plane(cyl_axis).project(point).normalized();13331334if (!separator.test_axis(axis)) {1335return;1336}1337}13381339// Edges of A, cylinder end caps rim.1340int edges_start_A[12] = { 0, 2, 4, 6, 0, 1, 4, 5, 0, 1, 2, 3 };1341int edges_end_A[12] = { 1, 3, 5, 7, 2, 3, 6, 7, 4, 5, 6, 7 };13421343Vector3 cap_axis = cyl_axis * (cylinder_B->get_height() * 0.5);13441345for (int i = 0; i < 2; i++) {1346Vector3 cap_pos = p_transform_b.origin + ((i == 0) ? cap_axis : -cap_axis);13471348for (int e = 0; e < 12; e++) {1349const Vector3 &edge_start = vertices_A[edges_start_A[e]];1350const Vector3 &edge_end = vertices_A[edges_end_A[e]];13511352Vector3 edge_dir = (edge_end - edge_start);1353edge_dir.normalize();13541355real_t edge_dot = edge_dir.dot(cyl_axis);1356if (Math::is_zero_approx(edge_dot)) {1357// Edge is perpendicular to cylinder axis.1358continue;1359}13601361// Calculate intersection between edge and circle plane.1362Vector3 edge_diff = cap_pos - edge_start;1363real_t diff_dot = edge_diff.dot(cyl_axis);1364Vector3 intersection = edge_start + edge_dir * diff_dot / edge_dot;13651366// Calculate tangent that touches intersection.1367Vector3 tangent = (cap_pos - intersection).cross(cyl_axis);13681369// Axis is orthogonal both to tangent and edge direction.1370Vector3 axis = tangent.cross(edge_dir);13711372if (!separator.test_axis(axis.normalized())) {1373return;1374}1375}1376}13771378separator.generate_contacts();1379}13801381template <bool withMargin>1382static void _collision_box_convex_polygon(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {1383const GodotBoxShape3D *box_A = static_cast<const GodotBoxShape3D *>(p_a);1384const GodotConvexPolygonShape3D *convex_polygon_B = static_cast<const GodotConvexPolygonShape3D *>(p_b);13851386SeparatorAxisTest<GodotBoxShape3D, GodotConvexPolygonShape3D, withMargin> separator(box_A, p_transform_a, convex_polygon_B, p_transform_b, p_collector, p_margin_a, p_margin_b);13871388if (!separator.test_previous_axis()) {1389return;1390}13911392const Geometry3D::MeshData &mesh = convex_polygon_B->get_mesh();13931394const Geometry3D::MeshData::Face *faces = mesh.faces.ptr();1395int face_count = mesh.faces.size();1396const Geometry3D::MeshData::Edge *edges = mesh.edges.ptr();1397int edge_count = mesh.edges.size();1398const Vector3 *vertices = mesh.vertices.ptr();1399int vertex_count = mesh.vertices.size();14001401// faces of A1402for (int i = 0; i < 3; i++) {1403Vector3 axis = p_transform_a.basis.get_column(i).normalized();14041405if (!separator.test_axis(axis)) {1406return;1407}1408}14091410// Precalculating this makes the transforms faster.1411Basis b_xform_normal = p_transform_b.basis.inverse().transposed();14121413// faces of B1414for (int i = 0; i < face_count; i++) {1415Vector3 axis = b_xform_normal.xform(faces[i].plane.normal).normalized();14161417if (!separator.test_axis(axis)) {1418return;1419}1420}14211422// A<->B edges1423for (int i = 0; i < 3; i++) {1424Vector3 e1 = p_transform_a.basis.get_column(i);14251426for (int j = 0; j < edge_count; j++) {1427Vector3 e2 = p_transform_b.basis.xform(vertices[edges[j].vertex_a]) - p_transform_b.basis.xform(vertices[edges[j].vertex_b]);14281429Vector3 axis = e1.cross(e2).normalized();14301431if (!separator.test_axis(axis)) {1432return;1433}1434}1435}14361437if (withMargin) {1438// calculate closest points between vertices and box edges1439for (int v = 0; v < vertex_count; v++) {1440Vector3 vtxb = p_transform_b.xform(vertices[v]);1441Vector3 ab_vec = vtxb - p_transform_a.origin;14421443Vector3 cnormal_a = p_transform_a.basis.xform_inv(ab_vec);14441445Vector3 support_a = p_transform_a.xform(Vector3(14461447(cnormal_a.x < 0) ? -box_A->get_half_extents().x : box_A->get_half_extents().x,1448(cnormal_a.y < 0) ? -box_A->get_half_extents().y : box_A->get_half_extents().y,1449(cnormal_a.z < 0) ? -box_A->get_half_extents().z : box_A->get_half_extents().z));14501451Vector3 axis_ab = support_a - vtxb;14521453if (!separator.test_axis(axis_ab.normalized())) {1454return;1455}14561457//now try edges, which become cylinders!14581459for (int i = 0; i < 3; i++) {1460//a ->b1461Vector3 axis_a = p_transform_a.basis.get_column(i);14621463if (!separator.test_axis(axis_ab.cross(axis_a).cross(axis_a).normalized())) {1464return;1465}1466}1467}14681469//convex edges and box points1470for (int i = 0; i < 2; i++) {1471for (int j = 0; j < 2; j++) {1472for (int k = 0; k < 2; k++) {1473Vector3 he = box_A->get_half_extents();1474he.x *= (i * 2 - 1);1475he.y *= (j * 2 - 1);1476he.z *= (k * 2 - 1);1477Vector3 point = p_transform_a.origin;1478for (int l = 0; l < 3; l++) {1479point += p_transform_a.basis.get_column(l) * he[l];1480}14811482for (int e = 0; e < edge_count; e++) {1483Vector3 p1 = p_transform_b.xform(vertices[edges[e].vertex_a]);1484Vector3 p2 = p_transform_b.xform(vertices[edges[e].vertex_b]);1485Vector3 n = (p2 - p1);14861487if (!separator.test_axis((point - p2).cross(n).cross(n).normalized())) {1488return;1489}1490}1491}1492}1493}1494}14951496separator.generate_contacts();1497}14981499template <bool withMargin>1500static void _collision_box_face(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {1501const GodotBoxShape3D *box_A = static_cast<const GodotBoxShape3D *>(p_a);1502const GodotFaceShape3D *face_B = static_cast<const GodotFaceShape3D *>(p_b);15031504SeparatorAxisTest<GodotBoxShape3D, GodotFaceShape3D, withMargin> separator(box_A, p_transform_a, face_B, p_transform_b, p_collector, p_margin_a, p_margin_b);15051506Vector3 vertex[3] = {1507p_transform_b.xform(face_B->vertex[0]),1508p_transform_b.xform(face_B->vertex[1]),1509p_transform_b.xform(face_B->vertex[2]),1510};15111512Vector3 normal = (vertex[0] - vertex[2]).cross(vertex[0] - vertex[1]).normalized();15131514if (!separator.test_axis(normal)) {1515return;1516}15171518// faces of A1519for (int i = 0; i < 3; i++) {1520Vector3 axis = p_transform_a.basis.get_column(i).normalized();1521if (axis.dot(normal) < 0.0) {1522axis *= -1.0;1523}15241525if (!separator.test_axis(axis)) {1526return;1527}1528}15291530// combined edges15311532for (int i = 0; i < 3; i++) {1533Vector3 e = vertex[i] - vertex[(i + 1) % 3];15341535for (int j = 0; j < 3; j++) {1536Vector3 axis = e.cross(p_transform_a.basis.get_column(j)).normalized();1537if (axis.dot(normal) < 0.0) {1538axis *= -1.0;1539}15401541if (!separator.test_axis(axis)) {1542return;1543}1544}1545}15461547if (withMargin) {1548// calculate closest points between vertices and box edges1549for (int v = 0; v < 3; v++) {1550Vector3 ab_vec = vertex[v] - p_transform_a.origin;15511552Vector3 cnormal_a = p_transform_a.basis.xform_inv(ab_vec);15531554Vector3 support_a = p_transform_a.xform(Vector3(15551556(cnormal_a.x < 0) ? -box_A->get_half_extents().x : box_A->get_half_extents().x,1557(cnormal_a.y < 0) ? -box_A->get_half_extents().y : box_A->get_half_extents().y,1558(cnormal_a.z < 0) ? -box_A->get_half_extents().z : box_A->get_half_extents().z));15591560Vector3 axis_ab = support_a - vertex[v];1561if (axis_ab.dot(normal) < 0.0) {1562axis_ab *= -1.0;1563}15641565if (!separator.test_axis(axis_ab.normalized())) {1566return;1567}15681569//now try edges, which become cylinders!15701571for (int i = 0; i < 3; i++) {1572//a ->b1573Vector3 axis_a = p_transform_a.basis.get_column(i);15741575Vector3 axis = axis_ab.cross(axis_a).cross(axis_a).normalized();1576if (axis.dot(normal) < 0.0) {1577axis *= -1.0;1578}15791580if (!separator.test_axis(axis)) {1581return;1582}1583}1584}15851586//convex edges and box points, there has to be a way to speed up this (get closest point?)1587for (int i = 0; i < 2; i++) {1588for (int j = 0; j < 2; j++) {1589for (int k = 0; k < 2; k++) {1590Vector3 he = box_A->get_half_extents();1591he.x *= (i * 2 - 1);1592he.y *= (j * 2 - 1);1593he.z *= (k * 2 - 1);1594Vector3 point = p_transform_a.origin;1595for (int l = 0; l < 3; l++) {1596point += p_transform_a.basis.get_column(l) * he[l];1597}15981599for (int e = 0; e < 3; e++) {1600Vector3 p1 = vertex[e];1601Vector3 p2 = vertex[(e + 1) % 3];16021603Vector3 n = (p2 - p1);16041605Vector3 axis = (point - p2).cross(n).cross(n).normalized();1606if (axis.dot(normal) < 0.0) {1607axis *= -1.0;1608}16091610if (!separator.test_axis(axis)) {1611return;1612}1613}1614}1615}1616}1617}16181619if (!face_B->backface_collision) {1620if (separator.best_axis.dot(normal) < _BACKFACE_NORMAL_THRESHOLD) {1621if (face_B->invert_backface_collision) {1622separator.best_axis = separator.best_axis.bounce(normal);1623} else {1624// Just ignore backface collision.1625return;1626}1627}1628}16291630separator.generate_contacts();1631}16321633template <bool withMargin>1634static void _collision_capsule_capsule(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {1635const GodotCapsuleShape3D *capsule_A = static_cast<const GodotCapsuleShape3D *>(p_a);1636const GodotCapsuleShape3D *capsule_B = static_cast<const GodotCapsuleShape3D *>(p_b);16371638real_t scale_A = p_transform_a.basis[0].length();1639real_t scale_B = p_transform_b.basis[0].length();16401641// Get the closest points between the capsule segments1642Vector3 capsule_A_closest;1643Vector3 capsule_B_closest;1644Vector3 capsule_A_axis = p_transform_a.basis.get_column(1) * (capsule_A->get_height() * 0.5 - capsule_A->get_radius());1645Vector3 capsule_B_axis = p_transform_b.basis.get_column(1) * (capsule_B->get_height() * 0.5 - capsule_B->get_radius());1646Geometry3D::get_closest_points_between_segments(1647p_transform_a.origin + capsule_A_axis,1648p_transform_a.origin - capsule_A_axis,1649p_transform_b.origin + capsule_B_axis,1650p_transform_b.origin - capsule_B_axis,1651capsule_A_closest,1652capsule_B_closest);16531654// Perform the analytic collision between the two closest capsule spheres1655analytic_sphere_collision<withMargin>(1656capsule_A_closest,1657capsule_A->get_radius() * scale_A,1658capsule_B_closest,1659capsule_B->get_radius() * scale_B,1660p_collector,1661p_margin_a,1662p_margin_b);1663}16641665template <bool withMargin>1666static void _collision_capsule_cylinder(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {1667const GodotCapsuleShape3D *capsule_A = static_cast<const GodotCapsuleShape3D *>(p_a);1668const GodotCylinderShape3D *cylinder_B = static_cast<const GodotCylinderShape3D *>(p_b);16691670// Find the closest points between the axes of the two objects.16711672Vector3 capsule_A_closest;1673Vector3 cylinder_B_closest;1674Vector3 capsule_A_axis = p_transform_a.basis.get_column(1) * (capsule_A->get_height() * 0.5 - capsule_A->get_radius());1675Vector3 cylinder_B_axis = p_transform_b.basis.get_column(1) * (cylinder_B->get_height() * 0.5);1676Geometry3D::get_closest_points_between_segments(1677p_transform_a.origin + capsule_A_axis,1678p_transform_a.origin - capsule_A_axis,1679p_transform_b.origin + cylinder_B_axis,1680p_transform_b.origin - cylinder_B_axis,1681capsule_A_closest,1682cylinder_B_closest);16831684// Perform the collision test between the cylinder and the nearest sphere on the capsule axis.16851686Transform3D sphere_transform(p_transform_a.basis, capsule_A_closest);1687analytic_sphere_cylinder_collision<withMargin>(capsule_A->get_radius(), cylinder_B->get_radius(), cylinder_B->get_height(), sphere_transform, p_transform_b, p_collector, p_margin_a, p_margin_b);1688}16891690template <bool withMargin>1691static void _collision_capsule_convex_polygon(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {1692const GodotCapsuleShape3D *capsule_A = static_cast<const GodotCapsuleShape3D *>(p_a);1693const GodotConvexPolygonShape3D *convex_polygon_B = static_cast<const GodotConvexPolygonShape3D *>(p_b);16941695SeparatorAxisTest<GodotCapsuleShape3D, GodotConvexPolygonShape3D, withMargin> separator(capsule_A, p_transform_a, convex_polygon_B, p_transform_b, p_collector, p_margin_a, p_margin_b);16961697if (!separator.test_previous_axis()) {1698return;1699}17001701const Geometry3D::MeshData &mesh = convex_polygon_B->get_mesh();17021703const Geometry3D::MeshData::Face *faces = mesh.faces.ptr();1704int face_count = mesh.faces.size();1705const Geometry3D::MeshData::Edge *edges = mesh.edges.ptr();1706int edge_count = mesh.edges.size();1707const Vector3 *vertices = mesh.vertices.ptr();17081709// Precalculating this makes the transforms faster.1710Basis b_xform_normal = p_transform_b.basis.inverse().transposed();17111712// faces of B1713for (int i = 0; i < face_count; i++) {1714Vector3 axis = b_xform_normal.xform(faces[i].plane.normal).normalized();17151716if (!separator.test_axis(axis)) {1717return;1718}1719}17201721// edges of B, capsule cylinder17221723for (int i = 0; i < edge_count; i++) {1724// cylinder1725Vector3 edge_axis = p_transform_b.basis.xform(vertices[edges[i].vertex_a]) - p_transform_b.basis.xform(vertices[edges[i].vertex_b]);1726Vector3 axis = edge_axis.cross(p_transform_a.basis.get_column(1)).normalized();17271728if (!separator.test_axis(axis)) {1729return;1730}1731}17321733// capsule balls, edges of B17341735for (int i = 0; i < 2; i++) {1736// edges of B, capsule cylinder17371738Vector3 capsule_axis = p_transform_a.basis.get_column(1) * (capsule_A->get_height() * 0.5 - capsule_A->get_radius());17391740Vector3 sphere_pos = p_transform_a.origin + ((i == 0) ? capsule_axis : -capsule_axis);17411742for (int j = 0; j < edge_count; j++) {1743Vector3 n1 = sphere_pos - p_transform_b.xform(vertices[edges[j].vertex_a]);1744Vector3 n2 = p_transform_b.basis.xform(vertices[edges[j].vertex_a]) - p_transform_b.basis.xform(vertices[edges[j].vertex_b]);17451746Vector3 axis = n1.cross(n2).cross(n2).normalized();17471748if (!separator.test_axis(axis)) {1749return;1750}1751}1752}17531754separator.generate_contacts();1755}17561757template <bool withMargin>1758static void _collision_capsule_face(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {1759const GodotCapsuleShape3D *capsule_A = static_cast<const GodotCapsuleShape3D *>(p_a);1760const GodotFaceShape3D *face_B = static_cast<const GodotFaceShape3D *>(p_b);17611762SeparatorAxisTest<GodotCapsuleShape3D, GodotFaceShape3D, withMargin> separator(capsule_A, p_transform_a, face_B, p_transform_b, p_collector, p_margin_a, p_margin_b);17631764Vector3 vertex[3] = {1765p_transform_b.xform(face_B->vertex[0]),1766p_transform_b.xform(face_B->vertex[1]),1767p_transform_b.xform(face_B->vertex[2]),1768};17691770Vector3 normal = (vertex[0] - vertex[2]).cross(vertex[0] - vertex[1]).normalized();17711772if (!separator.test_axis(normal)) {1773return;1774}17751776// edges of B, capsule cylinder17771778Vector3 capsule_axis = p_transform_a.basis.get_column(1) * (capsule_A->get_height() * 0.5 - capsule_A->get_radius());17791780for (int i = 0; i < 3; i++) {1781// edge-cylinder1782Vector3 edge_axis = vertex[i] - vertex[(i + 1) % 3];17831784Vector3 axis = edge_axis.cross(capsule_axis).normalized();1785if (axis.dot(normal) < 0.0) {1786axis *= -1.0;1787}17881789if (!separator.test_axis(axis)) {1790return;1791}17921793Vector3 dir_axis = (p_transform_a.origin - vertex[i]).cross(capsule_axis).cross(capsule_axis).normalized();1794if (dir_axis.dot(normal) < 0.0) {1795dir_axis *= -1.0;1796}17971798if (!separator.test_axis(dir_axis)) {1799return;1800}18011802for (int j = 0; j < 2; j++) {1803// point-spheres1804Vector3 sphere_pos = p_transform_a.origin + ((j == 0) ? capsule_axis : -capsule_axis);18051806Vector3 n1 = sphere_pos - vertex[i];1807if (n1.dot(normal) < 0.0) {1808n1 *= -1.0;1809}18101811if (!separator.test_axis(n1.normalized())) {1812return;1813}18141815Vector3 n2 = edge_axis;18161817axis = n1.cross(n2).cross(n2);1818if (axis.dot(normal) < 0.0) {1819axis *= -1.0;1820}18211822if (!separator.test_axis(axis.normalized())) {1823return;1824}1825}1826}18271828if (!face_B->backface_collision) {1829if (separator.best_axis.dot(normal) < _BACKFACE_NORMAL_THRESHOLD) {1830if (face_B->invert_backface_collision) {1831separator.best_axis = separator.best_axis.bounce(normal);1832} else {1833// Just ignore backface collision.1834return;1835}1836}1837}18381839separator.generate_contacts();1840}18411842template <bool withMargin>1843static void _collision_cylinder_cylinder(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {1844const GodotCylinderShape3D *cylinder_A = static_cast<const GodotCylinderShape3D *>(p_a);1845const GodotCylinderShape3D *cylinder_B = static_cast<const GodotCylinderShape3D *>(p_b);18461847SeparatorAxisTest<GodotCylinderShape3D, GodotCylinderShape3D, withMargin> separator(cylinder_A, p_transform_a, cylinder_B, p_transform_b, p_collector, p_margin_a, p_margin_b);18481849Vector3 cylinder_A_axis = p_transform_a.basis.get_column(1);1850Vector3 cylinder_B_axis = p_transform_b.basis.get_column(1);18511852if (!separator.test_previous_axis()) {1853return;1854}18551856// Cylinder A end caps.1857if (!separator.test_axis(cylinder_A_axis.normalized())) {1858return;1859}18601861// Cylinder B end caps.1862if (!separator.test_axis(cylinder_B_axis.normalized())) {1863return;1864}18651866Vector3 cylinder_diff = p_transform_b.origin - p_transform_a.origin;18671868// Cylinder A lateral surface.1869if (!separator.test_axis(cylinder_A_axis.cross(cylinder_diff).cross(cylinder_A_axis).normalized())) {1870return;1871}18721873// Cylinder B lateral surface.1874if (!separator.test_axis(cylinder_B_axis.cross(cylinder_diff).cross(cylinder_B_axis).normalized())) {1875return;1876}18771878real_t proj = cylinder_A_axis.cross(cylinder_B_axis).cross(cylinder_B_axis).dot(cylinder_A_axis);1879if (Math::is_zero_approx(proj)) {1880// Parallel cylinders, handle with specific axes only.1881// Note: GJKEPA with no margin can lead to degenerate cases in this situation.1882separator.generate_contacts();1883return;1884}18851886GodotCollisionSolver3D::CallbackResult callback = SeparatorAxisTest<GodotCylinderShape3D, GodotCylinderShape3D, withMargin>::test_contact_points;18871888// Fallback to generic algorithm to find the best separating axis.1889if (!fallback_collision_solver(p_a, p_transform_a, p_b, p_transform_b, callback, &separator, false, p_margin_a, p_margin_b)) {1890return;1891}18921893separator.generate_contacts();1894}18951896template <bool withMargin>1897static void _collision_cylinder_convex_polygon(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {1898const GodotCylinderShape3D *cylinder_A = static_cast<const GodotCylinderShape3D *>(p_a);1899const GodotConvexPolygonShape3D *convex_polygon_B = static_cast<const GodotConvexPolygonShape3D *>(p_b);19001901SeparatorAxisTest<GodotCylinderShape3D, GodotConvexPolygonShape3D, withMargin> separator(cylinder_A, p_transform_a, convex_polygon_B, p_transform_b, p_collector, p_margin_a, p_margin_b);19021903GodotCollisionSolver3D::CallbackResult callback = SeparatorAxisTest<GodotCylinderShape3D, GodotConvexPolygonShape3D, withMargin>::test_contact_points;19041905// Fallback to generic algorithm to find the best separating axis.1906if (!fallback_collision_solver(p_a, p_transform_a, p_b, p_transform_b, callback, &separator, false, p_margin_a, p_margin_b)) {1907return;1908}19091910separator.generate_contacts();1911}19121913template <bool withMargin>1914static void _collision_cylinder_face(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {1915const GodotCylinderShape3D *cylinder_A = static_cast<const GodotCylinderShape3D *>(p_a);1916const GodotFaceShape3D *face_B = static_cast<const GodotFaceShape3D *>(p_b);19171918SeparatorAxisTest<GodotCylinderShape3D, GodotFaceShape3D, withMargin> separator(cylinder_A, p_transform_a, face_B, p_transform_b, p_collector, p_margin_a, p_margin_b);19191920if (!separator.test_previous_axis()) {1921return;1922}19231924Vector3 vertex[3] = {1925p_transform_b.xform(face_B->vertex[0]),1926p_transform_b.xform(face_B->vertex[1]),1927p_transform_b.xform(face_B->vertex[2]),1928};19291930Vector3 normal = (vertex[0] - vertex[2]).cross(vertex[0] - vertex[1]).normalized();19311932// Face B normal.1933if (!separator.test_axis(normal)) {1934return;1935}19361937Vector3 cyl_axis = p_transform_a.basis.get_column(1).normalized();1938if (cyl_axis.dot(normal) < 0.0) {1939cyl_axis *= -1.0;1940}19411942// Cylinder end caps.1943if (!separator.test_axis(cyl_axis)) {1944return;1945}19461947// Edges of B, cylinder lateral surface.1948for (int i = 0; i < 3; i++) {1949Vector3 edge_axis = vertex[i] - vertex[(i + 1) % 3];1950Vector3 axis = edge_axis.cross(cyl_axis);1951if (Math::is_zero_approx(axis.length_squared())) {1952continue;1953}19541955if (axis.dot(normal) < 0.0) {1956axis *= -1.0;1957}19581959if (!separator.test_axis(axis.normalized())) {1960return;1961}1962}19631964// Points of B, cylinder lateral surface.1965for (int i = 0; i < 3; i++) {1966const Vector3 point = vertex[i] - p_transform_a.origin;1967Vector3 axis = Plane(cyl_axis).project(point).normalized();1968if (axis.dot(normal) < 0.0) {1969axis *= -1.0;1970}19711972if (!separator.test_axis(axis)) {1973return;1974}1975}19761977// Edges of B, cylinder end caps rim.1978Vector3 cap_axis = cyl_axis * (cylinder_A->get_height() * 0.5);19791980for (int i = 0; i < 2; i++) {1981Vector3 cap_pos = p_transform_a.origin + ((i == 0) ? cap_axis : -cap_axis);19821983for (int j = 0; j < 3; j++) {1984const Vector3 &edge_start = vertex[j];1985const Vector3 &edge_end = vertex[(j + 1) % 3];1986Vector3 edge_dir = edge_end - edge_start;1987edge_dir.normalize();19881989real_t edge_dot = edge_dir.dot(cyl_axis);1990if (Math::is_zero_approx(edge_dot)) {1991// Edge is perpendicular to cylinder axis.1992continue;1993}19941995// Calculate intersection between edge and circle plane.1996Vector3 edge_diff = cap_pos - edge_start;1997real_t diff_dot = edge_diff.dot(cyl_axis);1998Vector3 intersection = edge_start + edge_dir * diff_dot / edge_dot;19992000// Calculate tangent that touches intersection.2001Vector3 tangent = (cap_pos - intersection).cross(cyl_axis);20022003// Axis is orthogonal both to tangent and edge direction.2004Vector3 axis = tangent.cross(edge_dir);2005if (axis.dot(normal) < 0.0) {2006axis *= -1.0;2007}20082009if (!separator.test_axis(axis.normalized())) {2010return;2011}2012}2013}20142015if (!face_B->backface_collision) {2016if (separator.best_axis.dot(normal) < _BACKFACE_NORMAL_THRESHOLD) {2017if (face_B->invert_backface_collision) {2018separator.best_axis = separator.best_axis.bounce(normal);2019} else {2020// Just ignore backface collision.2021return;2022}2023}2024}20252026separator.generate_contacts();2027}20282029static _FORCE_INLINE_ bool is_minkowski_face(const Vector3 &A, const Vector3 &B, const Vector3 &B_x_A, const Vector3 &C, const Vector3 &D, const Vector3 &D_x_C) {2030// Test if arcs AB and CD intersect on the unit sphere2031real_t CBA = C.dot(B_x_A);2032real_t DBA = D.dot(B_x_A);2033real_t ADC = A.dot(D_x_C);2034real_t BDC = B.dot(D_x_C);20352036return (CBA * DBA < 0.0f) && (ADC * BDC < 0.0f) && (CBA * BDC > 0.0f);2037}20382039template <bool withMargin>2040static void _collision_convex_polygon_convex_polygon(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {2041const GodotConvexPolygonShape3D *convex_polygon_A = static_cast<const GodotConvexPolygonShape3D *>(p_a);2042const GodotConvexPolygonShape3D *convex_polygon_B = static_cast<const GodotConvexPolygonShape3D *>(p_b);20432044SeparatorAxisTest<GodotConvexPolygonShape3D, GodotConvexPolygonShape3D, withMargin> separator(convex_polygon_A, p_transform_a, convex_polygon_B, p_transform_b, p_collector, p_margin_a, p_margin_b);20452046if (!separator.test_previous_axis()) {2047return;2048}20492050const Geometry3D::MeshData &mesh_A = convex_polygon_A->get_mesh();20512052const Geometry3D::MeshData::Face *faces_A = mesh_A.faces.ptr();2053int face_count_A = mesh_A.faces.size();2054const Geometry3D::MeshData::Edge *edges_A = mesh_A.edges.ptr();2055int edge_count_A = mesh_A.edges.size();2056const Vector3 *vertices_A = mesh_A.vertices.ptr();2057int vertex_count_A = mesh_A.vertices.size();20582059const Geometry3D::MeshData &mesh_B = convex_polygon_B->get_mesh();20602061const Geometry3D::MeshData::Face *faces_B = mesh_B.faces.ptr();2062int face_count_B = mesh_B.faces.size();2063const Geometry3D::MeshData::Edge *edges_B = mesh_B.edges.ptr();2064int edge_count_B = mesh_B.edges.size();2065const Vector3 *vertices_B = mesh_B.vertices.ptr();2066int vertex_count_B = mesh_B.vertices.size();20672068// Precalculating this makes the transforms faster.2069Basis a_xform_normal = p_transform_a.basis.inverse().transposed();20702071// faces of A2072for (int i = 0; i < face_count_A; i++) {2073Vector3 axis = a_xform_normal.xform(faces_A[i].plane.normal).normalized();20742075if (!separator.test_axis(axis)) {2076return;2077}2078}20792080// Precalculating this makes the transforms faster.2081Basis b_xform_normal = p_transform_b.basis.inverse().transposed();20822083// faces of B2084for (int i = 0; i < face_count_B; i++) {2085Vector3 axis = b_xform_normal.xform(faces_B[i].plane.normal).normalized();20862087if (!separator.test_axis(axis)) {2088return;2089}2090}20912092// A<->B edges20932094for (int i = 0; i < edge_count_A; i++) {2095Vector3 p1 = p_transform_a.xform(vertices_A[edges_A[i].vertex_a]);2096Vector3 q1 = p_transform_a.xform(vertices_A[edges_A[i].vertex_b]);2097Vector3 e1 = q1 - p1;2098Vector3 u1 = p_transform_a.basis.xform(faces_A[edges_A[i].face_a].plane.normal).normalized();2099Vector3 v1 = p_transform_a.basis.xform(faces_A[edges_A[i].face_b].plane.normal).normalized();21002101for (int j = 0; j < edge_count_B; j++) {2102Vector3 p2 = p_transform_b.xform(vertices_B[edges_B[j].vertex_a]);2103Vector3 q2 = p_transform_b.xform(vertices_B[edges_B[j].vertex_b]);2104Vector3 e2 = q2 - p2;2105Vector3 u2 = p_transform_b.basis.xform(faces_B[edges_B[j].face_a].plane.normal).normalized();2106Vector3 v2 = p_transform_b.basis.xform(faces_B[edges_B[j].face_b].plane.normal).normalized();21072108if (is_minkowski_face(u1, v1, -e1, -u2, -v2, -e2)) {2109Vector3 axis = e1.cross(e2).normalized();21102111if (!separator.test_axis(axis)) {2112return;2113}2114}2115}2116}21172118if (withMargin) {2119//vertex-vertex2120for (int i = 0; i < vertex_count_A; i++) {2121Vector3 va = p_transform_a.xform(vertices_A[i]);21222123for (int j = 0; j < vertex_count_B; j++) {2124if (!separator.test_axis((va - p_transform_b.xform(vertices_B[j])).normalized())) {2125return;2126}2127}2128}2129//edge-vertex (shell)21302131for (int i = 0; i < edge_count_A; i++) {2132Vector3 e1 = p_transform_a.basis.xform(vertices_A[edges_A[i].vertex_a]);2133Vector3 e2 = p_transform_a.basis.xform(vertices_A[edges_A[i].vertex_b]);2134Vector3 n = (e2 - e1);21352136for (int j = 0; j < vertex_count_B; j++) {2137Vector3 e3 = p_transform_b.xform(vertices_B[j]);21382139if (!separator.test_axis((e1 - e3).cross(n).cross(n).normalized())) {2140return;2141}2142}2143}21442145for (int i = 0; i < edge_count_B; i++) {2146Vector3 e1 = p_transform_b.basis.xform(vertices_B[edges_B[i].vertex_a]);2147Vector3 e2 = p_transform_b.basis.xform(vertices_B[edges_B[i].vertex_b]);2148Vector3 n = (e2 - e1);21492150for (int j = 0; j < vertex_count_A; j++) {2151Vector3 e3 = p_transform_a.xform(vertices_A[j]);21522153if (!separator.test_axis((e1 - e3).cross(n).cross(n).normalized())) {2154return;2155}2156}2157}2158}21592160separator.generate_contacts();2161}21622163template <bool withMargin>2164static void _collision_convex_polygon_face(const GodotShape3D *p_a, const Transform3D &p_transform_a, const GodotShape3D *p_b, const Transform3D &p_transform_b, _CollectorCallback *p_collector, real_t p_margin_a, real_t p_margin_b) {2165const GodotConvexPolygonShape3D *convex_polygon_A = static_cast<const GodotConvexPolygonShape3D *>(p_a);2166const GodotFaceShape3D *face_B = static_cast<const GodotFaceShape3D *>(p_b);21672168SeparatorAxisTest<GodotConvexPolygonShape3D, GodotFaceShape3D, withMargin> separator(convex_polygon_A, p_transform_a, face_B, p_transform_b, p_collector, p_margin_a, p_margin_b);21692170const Geometry3D::MeshData &mesh = convex_polygon_A->get_mesh();21712172const Geometry3D::MeshData::Face *faces = mesh.faces.ptr();2173int face_count = mesh.faces.size();2174const Geometry3D::MeshData::Edge *edges = mesh.edges.ptr();2175int edge_count = mesh.edges.size();2176const Vector3 *vertices = mesh.vertices.ptr();2177int vertex_count = mesh.vertices.size();21782179Vector3 vertex[3] = {2180p_transform_b.xform(face_B->vertex[0]),2181p_transform_b.xform(face_B->vertex[1]),2182p_transform_b.xform(face_B->vertex[2]),2183};21842185Vector3 normal = (vertex[0] - vertex[2]).cross(vertex[0] - vertex[1]).normalized();21862187if (!separator.test_axis(normal)) {2188return;2189}21902191// faces of A2192for (int i = 0; i < face_count; i++) {2193//Vector3 axis = p_transform_a.xform( faces[i].plane ).normal;2194Vector3 axis = p_transform_a.basis.xform(faces[i].plane.normal).normalized();2195if (axis.dot(normal) < 0.0) {2196axis *= -1.0;2197}21982199if (!separator.test_axis(axis)) {2200return;2201}2202}22032204// A<->B edges2205for (int i = 0; i < edge_count; i++) {2206Vector3 e1 = p_transform_a.xform(vertices[edges[i].vertex_a]) - p_transform_a.xform(vertices[edges[i].vertex_b]);22072208for (int j = 0; j < 3; j++) {2209Vector3 e2 = vertex[j] - vertex[(j + 1) % 3];22102211Vector3 axis = e1.cross(e2).normalized();2212if (axis.dot(normal) < 0.0) {2213axis *= -1.0;2214}22152216if (!separator.test_axis(axis)) {2217return;2218}2219}2220}22212222if (withMargin) {2223//vertex-vertex2224for (int i = 0; i < vertex_count; i++) {2225Vector3 va = p_transform_a.xform(vertices[i]);22262227for (int j = 0; j < 3; j++) {2228Vector3 axis = (va - vertex[j]).normalized();2229if (axis.dot(normal) < 0.0) {2230axis *= -1.0;2231}22322233if (!separator.test_axis(axis)) {2234return;2235}2236}2237}2238//edge-vertex (shell)22392240for (int i = 0; i < edge_count; i++) {2241Vector3 e1 = p_transform_a.basis.xform(vertices[edges[i].vertex_a]);2242Vector3 e2 = p_transform_a.basis.xform(vertices[edges[i].vertex_b]);2243Vector3 n = (e2 - e1);22442245for (int j = 0; j < 3; j++) {2246Vector3 e3 = vertex[j];22472248Vector3 axis = (e1 - e3).cross(n).cross(n).normalized();2249if (axis.dot(normal) < 0.0) {2250axis *= -1.0;2251}22522253if (!separator.test_axis(axis)) {2254return;2255}2256}2257}22582259for (int i = 0; i < 3; i++) {2260Vector3 e1 = vertex[i];2261Vector3 e2 = vertex[(i + 1) % 3];2262Vector3 n = (e2 - e1);22632264for (int j = 0; j < vertex_count; j++) {2265Vector3 e3 = p_transform_a.xform(vertices[j]);22662267Vector3 axis = (e1 - e3).cross(n).cross(n).normalized();2268if (axis.dot(normal) < 0.0) {2269axis *= -1.0;2270}22712272if (!separator.test_axis(axis)) {2273return;2274}2275}2276}2277}22782279if (!face_B->backface_collision) {2280if (separator.best_axis.dot(normal) < _BACKFACE_NORMAL_THRESHOLD) {2281if (face_B->invert_backface_collision) {2282separator.best_axis = separator.best_axis.bounce(normal);2283} else {2284// Just ignore backface collision.2285return;2286}2287}2288}22892290separator.generate_contacts();2291}22922293bool sat_calculate_penetration(const GodotShape3D *p_shape_A, const Transform3D &p_transform_A, const GodotShape3D *p_shape_B, const Transform3D &p_transform_B, GodotCollisionSolver3D::CallbackResult p_result_callback, void *p_userdata, bool p_swap, Vector3 *r_prev_axis, real_t p_margin_a, real_t p_margin_b) {2294PhysicsServer3D::ShapeType type_A = p_shape_A->get_type();22952296ERR_FAIL_COND_V(type_A == PhysicsServer3D::SHAPE_WORLD_BOUNDARY, false);2297ERR_FAIL_COND_V(type_A == PhysicsServer3D::SHAPE_SEPARATION_RAY, false);2298ERR_FAIL_COND_V(p_shape_A->is_concave(), false);22992300PhysicsServer3D::ShapeType type_B = p_shape_B->get_type();23012302ERR_FAIL_COND_V(type_B == PhysicsServer3D::SHAPE_WORLD_BOUNDARY, false);2303ERR_FAIL_COND_V(type_B == PhysicsServer3D::SHAPE_SEPARATION_RAY, false);2304ERR_FAIL_COND_V(p_shape_B->is_concave(), false);23052306static const CollisionFunc collision_table[6][6] = {2307{ _collision_sphere_sphere<false>,2308_collision_sphere_box<false>,2309_collision_sphere_capsule<false>,2310_collision_sphere_cylinder<false>,2311_collision_sphere_convex_polygon<false>,2312_collision_sphere_face<false> },2313{ nullptr,2314_collision_box_box<false>,2315_collision_box_capsule<false>,2316_collision_box_cylinder<false>,2317_collision_box_convex_polygon<false>,2318_collision_box_face<false> },2319{ nullptr,2320nullptr,2321_collision_capsule_capsule<false>,2322_collision_capsule_cylinder<false>,2323_collision_capsule_convex_polygon<false>,2324_collision_capsule_face<false> },2325{ nullptr,2326nullptr,2327nullptr,2328_collision_cylinder_cylinder<false>,2329_collision_cylinder_convex_polygon<false>,2330_collision_cylinder_face<false> },2331{ nullptr,2332nullptr,2333nullptr,2334nullptr,2335_collision_convex_polygon_convex_polygon<false>,2336_collision_convex_polygon_face<false> },2337{ nullptr,2338nullptr,2339nullptr,2340nullptr,2341nullptr,2342nullptr },2343};23442345static const CollisionFunc collision_table_margin[6][6] = {2346{ _collision_sphere_sphere<true>,2347_collision_sphere_box<true>,2348_collision_sphere_capsule<true>,2349_collision_sphere_cylinder<true>,2350_collision_sphere_convex_polygon<true>,2351_collision_sphere_face<true> },2352{ nullptr,2353_collision_box_box<true>,2354_collision_box_capsule<true>,2355_collision_box_cylinder<true>,2356_collision_box_convex_polygon<true>,2357_collision_box_face<true> },2358{ nullptr,2359nullptr,2360_collision_capsule_capsule<true>,2361_collision_capsule_cylinder<true>,2362_collision_capsule_convex_polygon<true>,2363_collision_capsule_face<true> },2364{ nullptr,2365nullptr,2366nullptr,2367_collision_cylinder_cylinder<true>,2368_collision_cylinder_convex_polygon<true>,2369_collision_cylinder_face<true> },2370{ nullptr,2371nullptr,2372nullptr,2373nullptr,2374_collision_convex_polygon_convex_polygon<true>,2375_collision_convex_polygon_face<true> },2376{ nullptr,2377nullptr,2378nullptr,2379nullptr,2380nullptr,2381nullptr },2382};23832384_CollectorCallback callback;2385callback.callback = p_result_callback;2386callback.swap = p_swap;2387callback.userdata = p_userdata;2388callback.collided = false;2389callback.prev_axis = r_prev_axis;23902391const GodotShape3D *A = p_shape_A;2392const GodotShape3D *B = p_shape_B;2393const Transform3D *transform_A = &p_transform_A;2394const Transform3D *transform_B = &p_transform_B;2395real_t margin_A = p_margin_a;2396real_t margin_B = p_margin_b;23972398if (type_A > type_B) {2399SWAP(A, B);2400SWAP(transform_A, transform_B);2401SWAP(type_A, type_B);2402SWAP(margin_A, margin_B);2403callback.swap = !callback.swap;2404}24052406CollisionFunc collision_func;2407if (margin_A != 0.0 || margin_B != 0.0) {2408collision_func = collision_table_margin[type_A - 2][type_B - 2];24092410} else {2411collision_func = collision_table[type_A - 2][type_B - 2];2412}2413ERR_FAIL_NULL_V(collision_func, false);24142415collision_func(A, *transform_A, B, *transform_B, &callback, margin_A, margin_B);24162417return callback.collided;2418}241924202421