Path: blob/master/modules/godot_physics_3d/godot_shape_3d.cpp
10277 views
/**************************************************************************/1/* godot_shape_3d.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "godot_shape_3d.h"3132#include "core/io/image.h"33#include "core/math/convex_hull.h"34#include "core/math/geometry_3d.h"35#include "core/templates/sort_array.h"3637// GodotHeightMapShape3D is based on Bullet btHeightfieldTerrainShape.3839/*40Bullet Continuous Collision Detection and Physics Library41Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.org4243This software is provided 'as-is', without any express or implied warranty.44In no event will the authors be held liable for any damages arising from the use of this software.45Permission is granted to anyone to use this software for any purpose,46including commercial applications, and to alter it and redistribute it freely,47subject to the following restrictions:48491. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.502. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.513. This notice may not be removed or altered from any source distribution.52*/5354const double edge_support_threshold = 0.99999998;55const double edge_support_threshold_lower = Math::sqrt(1.0 - edge_support_threshold * edge_support_threshold);56// For a unit normal vector n, the horizontality condition57// sqrt(n.x * n.x + n.z * n.z) > edge_support_threshold58// is equivalent to the condition59// abs(n.y) < edge_support_threshold_lower,60// which is cheaper to test.61const double face_support_threshold = 0.9998;6263const double cylinder_edge_support_threshold = 0.999998;64const double cylinder_edge_support_threshold_lower = Math::sqrt(1.0 - cylinder_edge_support_threshold * cylinder_edge_support_threshold);65const double cylinder_face_support_threshold = 0.999;6667void GodotShape3D::configure(const AABB &p_aabb) {68aabb = p_aabb;69configured = true;70for (const KeyValue<GodotShapeOwner3D *, int> &E : owners) {71GodotShapeOwner3D *co = E.key;72co->_shape_changed();73}74}7576Vector3 GodotShape3D::get_support(const Vector3 &p_normal) const {77Vector3 res;78int amnt;79FeatureType type;80get_supports(p_normal, 1, &res, amnt, type);81return res;82}8384void GodotShape3D::add_owner(GodotShapeOwner3D *p_owner) {85HashMap<GodotShapeOwner3D *, int>::Iterator E = owners.find(p_owner);86if (E) {87E->value++;88} else {89owners[p_owner] = 1;90}91}9293void GodotShape3D::remove_owner(GodotShapeOwner3D *p_owner) {94HashMap<GodotShapeOwner3D *, int>::Iterator E = owners.find(p_owner);95ERR_FAIL_COND(!E);96E->value--;97if (E->value == 0) {98owners.remove(E);99}100}101102bool GodotShape3D::is_owner(GodotShapeOwner3D *p_owner) const {103return owners.has(p_owner);104}105106const HashMap<GodotShapeOwner3D *, int> &GodotShape3D::get_owners() const {107return owners;108}109110GodotShape3D::~GodotShape3D() {111ERR_FAIL_COND(owners.size());112}113114Plane GodotWorldBoundaryShape3D::get_plane() const {115return plane;116}117118void GodotWorldBoundaryShape3D::project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const {119// gibberish, a plane is infinity120r_min = -1e7;121r_max = 1e7;122}123124Vector3 GodotWorldBoundaryShape3D::get_support(const Vector3 &p_normal) const {125return p_normal * 1e15;126}127128bool GodotWorldBoundaryShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {129bool inters = plane.intersects_segment(p_begin, p_end, &r_result);130if (inters) {131r_normal = plane.normal;132}133return inters;134}135136bool GodotWorldBoundaryShape3D::intersect_point(const Vector3 &p_point) const {137return plane.distance_to(p_point) < 0;138}139140Vector3 GodotWorldBoundaryShape3D::get_closest_point_to(const Vector3 &p_point) const {141if (plane.is_point_over(p_point)) {142return plane.project(p_point);143} else {144return p_point;145}146}147148Vector3 GodotWorldBoundaryShape3D::get_moment_of_inertia(real_t p_mass) const {149return Vector3(); // not applicable.150}151152void GodotWorldBoundaryShape3D::_setup(const Plane &p_plane) {153plane = p_plane;154configure(AABB(Vector3(-1e15, -1e15, -1e15), Vector3(1e15 * 2, 1e15 * 2, 1e15 * 2)));155}156157void GodotWorldBoundaryShape3D::set_data(const Variant &p_data) {158_setup(p_data);159}160161Variant GodotWorldBoundaryShape3D::get_data() const {162return plane;163}164165GodotWorldBoundaryShape3D::GodotWorldBoundaryShape3D() {166}167168//169170real_t GodotSeparationRayShape3D::get_length() const {171return length;172}173174bool GodotSeparationRayShape3D::get_slide_on_slope() const {175return slide_on_slope;176}177178void GodotSeparationRayShape3D::project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const {179// don't think this will be even used180r_min = 0;181r_max = 1;182}183184Vector3 GodotSeparationRayShape3D::get_support(const Vector3 &p_normal) const {185if (p_normal.z > 0) {186return Vector3(0, 0, length);187} else {188return Vector3(0, 0, 0);189}190}191192void GodotSeparationRayShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const {193if (Math::abs(p_normal.z) < edge_support_threshold_lower) {194r_amount = 2;195r_type = FEATURE_EDGE;196r_supports[0] = Vector3(0, 0, 0);197r_supports[1] = Vector3(0, 0, length);198} else if (p_normal.z > 0) {199r_amount = 1;200r_type = FEATURE_POINT;201*r_supports = Vector3(0, 0, length);202} else {203r_amount = 1;204r_type = FEATURE_POINT;205*r_supports = Vector3(0, 0, 0);206}207}208209bool GodotSeparationRayShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {210return false; //simply not possible211}212213bool GodotSeparationRayShape3D::intersect_point(const Vector3 &p_point) const {214return false; //simply not possible215}216217Vector3 GodotSeparationRayShape3D::get_closest_point_to(const Vector3 &p_point) const {218return Geometry3D::get_closest_point_to_segment(p_point, Vector3(0, 0, 0), Vector3(0, 0, length));219}220221Vector3 GodotSeparationRayShape3D::get_moment_of_inertia(real_t p_mass) const {222return Vector3();223}224225void GodotSeparationRayShape3D::_setup(real_t p_length, bool p_slide_on_slope) {226length = p_length;227slide_on_slope = p_slide_on_slope;228configure(AABB(Vector3(0, 0, 0), Vector3(0.1, 0.1, length)));229}230231void GodotSeparationRayShape3D::set_data(const Variant &p_data) {232Dictionary d = p_data;233_setup(d["length"], d["slide_on_slope"]);234}235236Variant GodotSeparationRayShape3D::get_data() const {237Dictionary d;238d["length"] = length;239d["slide_on_slope"] = slide_on_slope;240return d;241}242243GodotSeparationRayShape3D::GodotSeparationRayShape3D() {}244245/********** SPHERE *************/246247real_t GodotSphereShape3D::get_radius() const {248return radius;249}250251void GodotSphereShape3D::project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const {252real_t d = p_normal.dot(p_transform.origin);253254// figure out scale at point255Vector3 local_normal = p_transform.basis.xform_inv(p_normal);256real_t scale = local_normal.length();257258r_min = d - (radius)*scale;259r_max = d + (radius)*scale;260}261262Vector3 GodotSphereShape3D::get_support(const Vector3 &p_normal) const {263return p_normal * radius;264}265266void GodotSphereShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const {267*r_supports = p_normal * radius;268r_amount = 1;269r_type = FEATURE_POINT;270}271272bool GodotSphereShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {273return Geometry3D::segment_intersects_sphere(p_begin, p_end, Vector3(), radius, &r_result, &r_normal);274}275276bool GodotSphereShape3D::intersect_point(const Vector3 &p_point) const {277return p_point.length() < radius;278}279280Vector3 GodotSphereShape3D::get_closest_point_to(const Vector3 &p_point) const {281Vector3 p = p_point;282real_t l = p.length();283if (l < radius) {284return p_point;285}286return (p / l) * radius;287}288289Vector3 GodotSphereShape3D::get_moment_of_inertia(real_t p_mass) const {290real_t s = 0.4 * p_mass * radius * radius;291return Vector3(s, s, s);292}293294void GodotSphereShape3D::_setup(real_t p_radius) {295radius = p_radius;296configure(AABB(Vector3(-radius, -radius, -radius), Vector3(radius * 2.0, radius * 2.0, radius * 2.0)));297}298299void GodotSphereShape3D::set_data(const Variant &p_data) {300_setup(p_data);301}302303Variant GodotSphereShape3D::get_data() const {304return radius;305}306307GodotSphereShape3D::GodotSphereShape3D() {}308309/********** BOX *************/310311void GodotBoxShape3D::project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const {312// no matter the angle, the box is mirrored anyway313Vector3 local_normal = p_transform.basis.xform_inv(p_normal);314315real_t length = local_normal.abs().dot(half_extents);316real_t distance = p_normal.dot(p_transform.origin);317318r_min = distance - length;319r_max = distance + length;320}321322Vector3 GodotBoxShape3D::get_support(const Vector3 &p_normal) const {323Vector3 point(324(p_normal.x < 0) ? -half_extents.x : half_extents.x,325(p_normal.y < 0) ? -half_extents.y : half_extents.y,326(p_normal.z < 0) ? -half_extents.z : half_extents.z);327328return point;329}330331void GodotBoxShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const {332static const int next[3] = { 1, 2, 0 };333static const int next2[3] = { 2, 0, 1 };334335for (int i = 0; i < 3; i++) {336Vector3 axis;337axis[i] = 1.0;338real_t dot = p_normal.dot(axis);339if (Math::abs(dot) > face_support_threshold) {340//Vector3 axis_b;341342bool neg = dot < 0;343r_amount = 4;344r_type = FEATURE_FACE;345346Vector3 point;347point[i] = half_extents[i];348349int i_n = next[i];350int i_n2 = next2[i];351352static const real_t sign[4][2] = {353{ -1.0, 1.0 },354{ 1.0, 1.0 },355{ 1.0, -1.0 },356{ -1.0, -1.0 },357};358359for (int j = 0; j < 4; j++) {360point[i_n] = sign[j][0] * half_extents[i_n];361point[i_n2] = sign[j][1] * half_extents[i_n2];362r_supports[j] = neg ? -point : point;363}364365if (neg) {366SWAP(r_supports[1], r_supports[2]);367SWAP(r_supports[0], r_supports[3]);368}369370return;371}372373r_amount = 0;374}375376for (int i = 0; i < 3; i++) {377Vector3 axis;378axis[i] = 1.0;379380if (Math::abs(p_normal.dot(axis)) < edge_support_threshold_lower) {381r_amount = 2;382r_type = FEATURE_EDGE;383384int i_n = next[i];385int i_n2 = next2[i];386387Vector3 point = half_extents;388389if (p_normal[i_n] < 0) {390point[i_n] = -point[i_n];391}392if (p_normal[i_n2] < 0) {393point[i_n2] = -point[i_n2];394}395396r_supports[0] = point;397point[i] = -point[i];398r_supports[1] = point;399return;400}401}402/* USE POINT */403404Vector3 point(405(p_normal.x < 0) ? -half_extents.x : half_extents.x,406(p_normal.y < 0) ? -half_extents.y : half_extents.y,407(p_normal.z < 0) ? -half_extents.z : half_extents.z);408409r_amount = 1;410r_type = FEATURE_POINT;411r_supports[0] = point;412}413414bool GodotBoxShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {415AABB aabb_ext(-half_extents, half_extents * 2.0);416417return aabb_ext.intersects_segment(p_begin, p_end, &r_result, &r_normal);418}419420bool GodotBoxShape3D::intersect_point(const Vector3 &p_point) const {421return (Math::abs(p_point.x) < half_extents.x && Math::abs(p_point.y) < half_extents.y && Math::abs(p_point.z) < half_extents.z);422}423424Vector3 GodotBoxShape3D::get_closest_point_to(const Vector3 &p_point) const {425int outside = 0;426Vector3 min_point;427428for (int i = 0; i < 3; i++) {429if (Math::abs(p_point[i]) > half_extents[i]) {430outside++;431if (outside == 1) {432//use plane if only one side matches433Vector3 n;434n[i] = SIGN(p_point[i]);435436Plane p(n, half_extents[i]);437min_point = p.project(p_point);438}439}440}441442if (!outside) {443return p_point; //it's inside, don't do anything else444}445446if (outside == 1) { //if only above one plane, this plane clearly wins447return min_point;448}449450//check segments451real_t min_distance = 1e20;452const Vector3 closest_vertex = half_extents * p_point.sign();453for (int i = 0; i < 3; i++) {454Vector3 segment_b = closest_vertex;455segment_b[i] = -segment_b[i]; //edge456457const Vector3 closest_edge = Geometry3D::get_closest_point_to_segment(p_point, closest_vertex, segment_b);458459const real_t d = p_point.distance_to(closest_edge);460if (d < min_distance) {461min_point = closest_edge;462min_distance = d;463}464}465466return min_point;467}468469Vector3 GodotBoxShape3D::get_moment_of_inertia(real_t p_mass) const {470real_t lx = half_extents.x;471real_t ly = half_extents.y;472real_t lz = half_extents.z;473474return Vector3((p_mass / 3.0) * (ly * ly + lz * lz), (p_mass / 3.0) * (lx * lx + lz * lz), (p_mass / 3.0) * (lx * lx + ly * ly));475}476477void GodotBoxShape3D::_setup(const Vector3 &p_half_extents) {478half_extents = p_half_extents.abs();479480configure(AABB(-half_extents, half_extents * 2));481}482483void GodotBoxShape3D::set_data(const Variant &p_data) {484_setup(p_data);485}486487Variant GodotBoxShape3D::get_data() const {488return half_extents;489}490491GodotBoxShape3D::GodotBoxShape3D() {}492493/********** CAPSULE *************/494495void GodotCapsuleShape3D::project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const {496Vector3 n = p_transform.basis.xform_inv(p_normal).normalized();497real_t h = height * 0.5 - radius;498499n *= radius;500n.y += (n.y > 0) ? h : -h;501502r_max = p_normal.dot(p_transform.xform(n));503r_min = p_normal.dot(p_transform.xform(-n));504}505506Vector3 GodotCapsuleShape3D::get_support(const Vector3 &p_normal) const {507Vector3 n = p_normal;508509real_t h = height * 0.5 - radius;510511n *= radius;512n.y += (n.y > 0) ? h : -h;513return n;514}515516void GodotCapsuleShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const {517Vector3 n = p_normal;518519real_t d = n.y;520real_t h = height * 0.5 - radius; // half-height of the cylinder part521522if (h > 0 && Math::abs(d) < edge_support_threshold_lower) {523// make it flat524n.y = 0.0;525n.normalize();526n *= radius;527528r_amount = 2;529r_type = FEATURE_EDGE;530r_supports[0] = n;531r_supports[0].y += h;532r_supports[1] = n;533r_supports[1].y -= h;534} else {535n *= radius;536n.y += (d > 0) ? h : -h;537r_amount = 1;538r_type = FEATURE_POINT;539*r_supports = n;540}541}542543bool GodotCapsuleShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {544Vector3 norm = (p_end - p_begin).normalized();545real_t min_d = 1e20;546547Vector3 res, n;548bool collision = false;549550Vector3 auxres, auxn;551bool collided;552553// test against cylinder and spheres :-|554555collided = Geometry3D::segment_intersects_cylinder(p_begin, p_end, height - radius * 2.0, radius, &auxres, &auxn, 1);556557if (collided) {558real_t d = norm.dot(auxres);559if (d < min_d) {560min_d = d;561res = auxres;562n = auxn;563collision = true;564}565}566567collided = Geometry3D::segment_intersects_sphere(p_begin, p_end, Vector3(0, height * 0.5 - radius, 0), radius, &auxres, &auxn);568569if (collided) {570real_t d = norm.dot(auxres);571if (d < min_d) {572min_d = d;573res = auxres;574n = auxn;575collision = true;576}577}578579collided = Geometry3D::segment_intersects_sphere(p_begin, p_end, Vector3(0, height * -0.5 + radius, 0), radius, &auxres, &auxn);580581if (collided) {582real_t d = norm.dot(auxres);583584if (d < min_d) {585min_d = d;586res = auxres;587n = auxn;588collision = true;589}590}591592if (collision) {593r_result = res;594r_normal = n;595}596return collision;597}598599bool GodotCapsuleShape3D::intersect_point(const Vector3 &p_point) const {600if (Math::abs(p_point.y) < height * 0.5 - radius) {601return Vector3(p_point.x, 0, p_point.z).length() < radius;602} else {603Vector3 p = p_point;604p.y = Math::abs(p.y) - height * 0.5 + radius;605return p.length() < radius;606}607}608609Vector3 GodotCapsuleShape3D::get_closest_point_to(const Vector3 &p_point) const {610const Vector3 segment_a = Vector3(0, -height * 0.5 + radius, 0);611const Vector3 segment_b = Vector3(0, height * 0.5 - radius, 0);612613const Vector3 p = Geometry3D::get_closest_point_to_segment(p_point, segment_a, segment_b);614615if (p.distance_to(p_point) < radius) {616return p_point;617}618619return p + (p_point - p).normalized() * radius;620}621622Vector3 GodotCapsuleShape3D::get_moment_of_inertia(real_t p_mass) const {623// use bad AABB approximation624Vector3 extents = get_aabb().size * 0.5;625626return Vector3(627(p_mass / 3.0) * (extents.y * extents.y + extents.z * extents.z),628(p_mass / 3.0) * (extents.x * extents.x + extents.z * extents.z),629(p_mass / 3.0) * (extents.x * extents.x + extents.y * extents.y));630}631632void GodotCapsuleShape3D::_setup(real_t p_height, real_t p_radius) {633height = p_height;634radius = p_radius;635configure(AABB(Vector3(-radius, -height * 0.5, -radius), Vector3(radius * 2, height, radius * 2)));636}637638void GodotCapsuleShape3D::set_data(const Variant &p_data) {639Dictionary d = p_data;640ERR_FAIL_COND(!d.has("radius"));641ERR_FAIL_COND(!d.has("height"));642_setup(d["height"], d["radius"]);643}644645Variant GodotCapsuleShape3D::get_data() const {646Dictionary d;647d["radius"] = radius;648d["height"] = height;649return d;650}651652GodotCapsuleShape3D::GodotCapsuleShape3D() {}653654/********** CYLINDER *************/655656void GodotCylinderShape3D::project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const {657Vector3 cylinder_axis = p_transform.basis.get_column(1).normalized();658real_t axis_dot = cylinder_axis.dot(p_normal);659660Vector3 local_normal = p_transform.basis.xform_inv(p_normal);661real_t scale = local_normal.length();662real_t scaled_radius = radius * scale;663real_t scaled_height = height * scale;664665real_t length;666if (Math::abs(axis_dot) > 1.0) {667length = scaled_height * 0.5;668} else {669length = Math::abs(axis_dot * scaled_height * 0.5) + scaled_radius * Math::sqrt(1.0 - axis_dot * axis_dot);670}671672real_t distance = p_normal.dot(p_transform.origin);673674r_min = distance - length;675r_max = distance + length;676}677678Vector3 GodotCylinderShape3D::get_support(const Vector3 &p_normal) const {679Vector3 n = p_normal;680real_t h = (n.y > 0) ? height : -height;681real_t s = Math::sqrt(n.x * n.x + n.z * n.z);682if (Math::is_zero_approx(s)) {683n.x = radius;684n.y = h * 0.5;685n.z = 0.0;686} else {687real_t d = radius / s;688n.x = n.x * d;689n.y = h * 0.5;690n.z = n.z * d;691}692693return n;694}695696void GodotCylinderShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const {697real_t d = p_normal.y;698if (Math::abs(d) > cylinder_face_support_threshold) {699real_t h = (d > 0) ? height : -height;700701Vector3 n = p_normal;702n.x = 0.0;703n.z = 0.0;704n.y = h * 0.5;705706r_amount = 3;707r_type = FEATURE_CIRCLE;708r_supports[0] = n;709r_supports[1] = n;710r_supports[1].x += radius;711r_supports[2] = n;712r_supports[2].z += radius;713} else if (Math::abs(d) < cylinder_edge_support_threshold_lower) {714// make it flat715Vector3 n = p_normal;716n.y = 0.0;717n.normalize();718n *= radius;719720r_amount = 2;721r_type = FEATURE_EDGE;722r_supports[0] = n;723r_supports[0].y += height * 0.5;724r_supports[1] = n;725r_supports[1].y -= height * 0.5;726} else {727r_amount = 1;728r_type = FEATURE_POINT;729r_supports[0] = get_support(p_normal);730}731}732733bool GodotCylinderShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {734return Geometry3D::segment_intersects_cylinder(p_begin, p_end, height, radius, &r_result, &r_normal, 1);735}736737bool GodotCylinderShape3D::intersect_point(const Vector3 &p_point) const {738if (Math::abs(p_point.y) < height * 0.5) {739return Vector3(p_point.x, 0, p_point.z).length() < radius;740}741return false;742}743744Vector3 GodotCylinderShape3D::get_closest_point_to(const Vector3 &p_point) const {745if (Math::abs(p_point.y) > height * 0.5) {746// Project point to top disk.747real_t dir = p_point.y > 0.0 ? 1.0 : -1.0;748Vector3 circle_pos(0.0, dir * height * 0.5, 0.0);749Plane circle_plane(Vector3(0.0, dir, 0.0), circle_pos);750Vector3 proj_point = circle_plane.project(p_point);751752// Clip position.753Vector3 delta_point_1 = proj_point - circle_pos;754real_t dist_point_1 = delta_point_1.length_squared();755if (!Math::is_zero_approx(dist_point_1)) {756dist_point_1 = Math::sqrt(dist_point_1);757proj_point = circle_pos + delta_point_1 * MIN(dist_point_1, radius) / dist_point_1;758}759760return proj_point;761} else {762const Vector3 segment_a = Vector3(0, -height * 0.5, 0);763const Vector3 segment_b = Vector3(0, height * 0.5, 0);764765const Vector3 p = Geometry3D::get_closest_point_to_segment(p_point, segment_a, segment_b);766767if (p.distance_to(p_point) < radius) {768return p_point;769}770771return p + (p_point - p).normalized() * radius;772}773}774775Vector3 GodotCylinderShape3D::get_moment_of_inertia(real_t p_mass) const {776// use bad AABB approximation777Vector3 extents = get_aabb().size * 0.5;778779return Vector3(780(p_mass / 3.0) * (extents.y * extents.y + extents.z * extents.z),781(p_mass / 3.0) * (extents.x * extents.x + extents.z * extents.z),782(p_mass / 3.0) * (extents.x * extents.x + extents.y * extents.y));783}784785void GodotCylinderShape3D::_setup(real_t p_height, real_t p_radius) {786height = p_height;787radius = p_radius;788configure(AABB(Vector3(-radius, -height * 0.5, -radius), Vector3(radius * 2.0, height, radius * 2.0)));789}790791void GodotCylinderShape3D::set_data(const Variant &p_data) {792Dictionary d = p_data;793ERR_FAIL_COND(!d.has("radius"));794ERR_FAIL_COND(!d.has("height"));795_setup(d["height"], d["radius"]);796}797798Variant GodotCylinderShape3D::get_data() const {799Dictionary d;800d["radius"] = radius;801d["height"] = height;802return d;803}804805GodotCylinderShape3D::GodotCylinderShape3D() {}806807/********** CONVEX POLYGON *************/808809void GodotConvexPolygonShape3D::project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const {810uint32_t vertex_count = mesh.vertices.size();811if (vertex_count == 0) {812return;813}814815const Vector3 *vrts = &mesh.vertices[0];816817if (vertex_count > 3 * extreme_vertices.size()) {818// For a large mesh, two calls to get_support() is faster than a full819// scan over all vertices.820821Vector3 n = p_transform.basis.xform_inv(p_normal).normalized();822r_min = p_normal.dot(p_transform.xform(get_support(-n)));823r_max = p_normal.dot(p_transform.xform(get_support(n)));824} else {825for (uint32_t i = 0; i < vertex_count; i++) {826real_t d = p_normal.dot(p_transform.xform(vrts[i]));827828if (i == 0 || d > r_max) {829r_max = d;830}831if (i == 0 || d < r_min) {832r_min = d;833}834}835}836}837838Vector3 GodotConvexPolygonShape3D::get_support(const Vector3 &p_normal) const {839// Skip if there are no vertices in the mesh840if (mesh.vertices.is_empty()) {841return Vector3();842}843844// Get the array of vertices845const Vector3 *const vertices_array = mesh.vertices.ptr();846847// Start with an initial assumption of the first extreme vertex.848int best_vertex = extreme_vertices[0];849real_t max_support = p_normal.dot(vertices_array[best_vertex]);850851// Check the remaining extreme vertices for a better vertex.852for (const int &vert : extreme_vertices) {853real_t s = p_normal.dot(vertices_array[vert]);854if (s > max_support) {855best_vertex = vert;856max_support = s;857}858}859860// If we checked all vertices in the mesh then we're done.861if (extreme_vertices.size() == mesh.vertices.size()) {862return vertices_array[best_vertex];863}864865// Move along the surface until we reach the true support vertex.866int last_vertex = -1;867while (true) {868int next_vertex = -1;869870// Iterate over all the neighbors checking for a better vertex.871for (const int &vert : vertex_neighbors[best_vertex]) {872if (vert != last_vertex) {873real_t s = p_normal.dot(vertices_array[vert]);874if (s > max_support) {875next_vertex = vert;876max_support = s;877break;878}879}880}881882// No better vertex found, we have the best883if (next_vertex == -1) {884return vertices_array[best_vertex];885}886887// Move to the better vertex and try again888last_vertex = best_vertex;889best_vertex = next_vertex;890}891}892893void GodotConvexPolygonShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const {894const Geometry3D::MeshData::Face *faces = mesh.faces.ptr();895int fc = mesh.faces.size();896897const Geometry3D::MeshData::Edge *edges = mesh.edges.ptr();898int ec = mesh.edges.size();899900const Vector3 *vertices = mesh.vertices.ptr();901int vc = mesh.vertices.size();902903r_amount = 0;904ERR_FAIL_COND_MSG(vc == 0, "Convex polygon shape has no vertices.");905906//find vertex first907real_t max = 0;908int vtx = 0;909910for (int i = 0; i < vc; i++) {911real_t d = p_normal.dot(vertices[i]);912913if (i == 0 || d > max) {914max = d;915vtx = i;916}917}918919for (int i = 0; i < fc; i++) {920if (faces[i].plane.normal.dot(p_normal) > face_support_threshold) {921int ic = faces[i].indices.size();922const int *ind = faces[i].indices.ptr();923924bool valid = false;925for (int j = 0; j < ic; j++) {926if (ind[j] == vtx) {927valid = true;928break;929}930}931932if (!valid) {933continue;934}935936int m = MIN(p_max, ic);937for (int j = 0; j < m; j++) {938r_supports[j] = vertices[ind[j]];939}940r_amount = m;941r_type = FEATURE_FACE;942return;943}944}945946for (int i = 0; i < ec; i++) {947real_t dot = (vertices[edges[i].vertex_a] - vertices[edges[i].vertex_b]).normalized().dot(p_normal);948dot = Math::abs(dot);949if (dot < edge_support_threshold_lower && (edges[i].vertex_a == vtx || edges[i].vertex_b == vtx)) {950r_amount = 2;951r_type = FEATURE_EDGE;952r_supports[0] = vertices[edges[i].vertex_a];953r_supports[1] = vertices[edges[i].vertex_b];954return;955}956}957958r_supports[0] = vertices[vtx];959r_amount = 1;960r_type = FEATURE_POINT;961}962963bool GodotConvexPolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {964const Geometry3D::MeshData::Face *faces = mesh.faces.ptr();965int fc = mesh.faces.size();966967const Vector3 *vertices = mesh.vertices.ptr();968969Vector3 n = p_end - p_begin;970real_t min = 1e20;971bool col = false;972973for (int i = 0; i < fc; i++) {974if (faces[i].plane.normal.dot(n) > 0) {975continue; //opposing face976}977978int ic = faces[i].indices.size();979const int *ind = faces[i].indices.ptr();980981for (int j = 1; j < ic - 1; j++) {982Face3 f(vertices[ind[0]], vertices[ind[j]], vertices[ind[j + 1]]);983Vector3 result;984if (f.intersects_segment(p_begin, p_end, &result)) {985real_t d = n.dot(result);986if (d < min) {987min = d;988r_result = result;989r_normal = faces[i].plane.normal;990col = true;991}992993break;994}995}996}997998return col;999}10001001bool GodotConvexPolygonShape3D::intersect_point(const Vector3 &p_point) const {1002const Geometry3D::MeshData::Face *faces = mesh.faces.ptr();1003int fc = mesh.faces.size();10041005for (int i = 0; i < fc; i++) {1006if (faces[i].plane.distance_to(p_point) >= 0) {1007return false;1008}1009}10101011return true;1012}10131014Vector3 GodotConvexPolygonShape3D::get_closest_point_to(const Vector3 &p_point) const {1015const Geometry3D::MeshData::Face *faces = mesh.faces.ptr();1016int fc = mesh.faces.size();1017const Vector3 *vertices = mesh.vertices.ptr();10181019bool all_inside = true;1020for (int i = 0; i < fc; i++) {1021if (!faces[i].plane.is_point_over(p_point)) {1022continue;1023}10241025all_inside = false;1026bool is_inside = true;1027int ic = faces[i].indices.size();1028const int *indices = faces[i].indices.ptr();10291030for (int j = 0; j < ic; j++) {1031Vector3 a = vertices[indices[j]];1032Vector3 b = vertices[indices[(j + 1) % ic]];1033Vector3 n = (a - b).cross(faces[i].plane.normal).normalized();1034if (Plane(n, a).is_point_over(p_point)) {1035is_inside = false;1036break;1037}1038}10391040if (is_inside) {1041return faces[i].plane.project(p_point);1042}1043}10441045if (all_inside) {1046return p_point;1047}10481049real_t min_distance = 1e20;1050Vector3 min_point;10511052//check edges1053const Geometry3D::MeshData::Edge *edges = mesh.edges.ptr();1054int ec = mesh.edges.size();1055for (int i = 0; i < ec; i++) {1056const Vector3 segment_a = vertices[edges[i].vertex_a];1057const Vector3 segment_b = vertices[edges[i].vertex_b];10581059Vector3 closest = Geometry3D::get_closest_point_to_segment(p_point, segment_a, segment_b);1060real_t d = closest.distance_to(p_point);1061if (d < min_distance) {1062min_distance = d;1063min_point = closest;1064}1065}10661067return min_point;1068}10691070Vector3 GodotConvexPolygonShape3D::get_moment_of_inertia(real_t p_mass) const {1071// use bad AABB approximation1072Vector3 extents = get_aabb().size * 0.5;10731074return Vector3(1075(p_mass / 3.0) * (extents.y * extents.y + extents.z * extents.z),1076(p_mass / 3.0) * (extents.x * extents.x + extents.z * extents.z),1077(p_mass / 3.0) * (extents.x * extents.x + extents.y * extents.y));1078}10791080void GodotConvexPolygonShape3D::_setup(const Vector<Vector3> &p_vertices) {1081Error err = ConvexHullComputer::convex_hull(p_vertices, mesh);1082if (err != OK) {1083ERR_PRINT("Failed to build convex hull");1084}1085extreme_vertices.resize(0);1086vertex_neighbors.resize(0);10871088AABB _aabb;10891090for (uint32_t i = 0; i < mesh.vertices.size(); i++) {1091if (i == 0) {1092_aabb.position = mesh.vertices[i];1093} else {1094_aabb.expand_to(mesh.vertices[i]);1095}1096}10971098configure(_aabb);10991100// Pre-compute the extreme vertices in 26 directions. This will be used1101// to speed up get_support() by letting us quickly get a good guess for1102// the support vertex.11031104for (int x = -1; x < 2; x++) {1105for (int y = -1; y < 2; y++) {1106for (int z = -1; z < 2; z++) {1107if (x != 0 || y != 0 || z != 0) {1108Vector3 dir(x, y, z);1109dir.normalize();1110real_t max_support = 0.0;1111int best_vertex = -1;1112for (uint32_t i = 0; i < mesh.vertices.size(); i++) {1113real_t s = dir.dot(mesh.vertices[i]);1114if (best_vertex == -1 || s > max_support) {1115best_vertex = i;1116max_support = s;1117}1118}1119if (!extreme_vertices.has(best_vertex)) {1120extreme_vertices.push_back(best_vertex);1121}1122}1123}1124}1125}11261127// Record all the neighbors of each vertex. This is used in get_support().11281129if (extreme_vertices.size() < mesh.vertices.size()) {1130vertex_neighbors.resize(mesh.vertices.size());1131for (Geometry3D::MeshData::Edge &edge : mesh.edges) {1132vertex_neighbors[edge.vertex_a].push_back(edge.vertex_b);1133vertex_neighbors[edge.vertex_b].push_back(edge.vertex_a);1134}1135}1136}11371138void GodotConvexPolygonShape3D::set_data(const Variant &p_data) {1139_setup(p_data);1140}11411142Variant GodotConvexPolygonShape3D::get_data() const {1143Vector<Vector3> vertices;1144vertices.resize(mesh.vertices.size());1145for (uint32_t i = 0; i < mesh.vertices.size(); i++) {1146vertices.write[i] = mesh.vertices[i];1147}1148return vertices;1149}11501151GodotConvexPolygonShape3D::GodotConvexPolygonShape3D() {1152}11531154/********** FACE POLYGON *************/11551156void GodotFaceShape3D::project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const {1157for (int i = 0; i < 3; i++) {1158Vector3 v = p_transform.xform(vertex[i]);1159real_t d = p_normal.dot(v);11601161if (i == 0 || d > r_max) {1162r_max = d;1163}11641165if (i == 0 || d < r_min) {1166r_min = d;1167}1168}1169}11701171Vector3 GodotFaceShape3D::get_support(const Vector3 &p_normal) const {1172int vert_support_idx = -1;1173real_t support_max = 0;11741175for (int i = 0; i < 3; i++) {1176real_t d = p_normal.dot(vertex[i]);11771178if (i == 0 || d > support_max) {1179support_max = d;1180vert_support_idx = i;1181}1182}11831184return vertex[vert_support_idx];1185}11861187void GodotFaceShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const {1188Vector3 n = p_normal;11891190/** TEST FACE AS SUPPORT **/1191if (Math::abs(normal.dot(n)) > face_support_threshold) {1192r_amount = 3;1193r_type = FEATURE_FACE;1194for (int i = 0; i < 3; i++) {1195r_supports[i] = vertex[i];1196}1197return;1198}11991200/** FIND SUPPORT VERTEX **/12011202int vert_support_idx = -1;1203real_t support_max = 0;12041205for (int i = 0; i < 3; i++) {1206real_t d = n.dot(vertex[i]);12071208if (i == 0 || d > support_max) {1209support_max = d;1210vert_support_idx = i;1211}1212}12131214/** TEST EDGES AS SUPPORT **/12151216for (int i = 0; i < 3; i++) {1217int nx = (i + 1) % 3;1218if (i != vert_support_idx && nx != vert_support_idx) {1219continue;1220}12211222// check if edge is valid as a support1223real_t dot = (vertex[i] - vertex[nx]).normalized().dot(n);1224dot = Math::abs(dot);1225if (dot < edge_support_threshold_lower) {1226r_amount = 2;1227r_type = FEATURE_EDGE;1228r_supports[0] = vertex[i];1229r_supports[1] = vertex[nx];1230return;1231}1232}12331234r_amount = 1;1235r_type = FEATURE_POINT;1236r_supports[0] = vertex[vert_support_idx];1237}12381239bool GodotFaceShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {1240bool c = Geometry3D::segment_intersects_triangle(p_begin, p_end, vertex[0], vertex[1], vertex[2], &r_result);1241if (c) {1242r_normal = Plane(vertex[0], vertex[1], vertex[2]).normal;1243if (r_normal.dot(p_end - p_begin) > 0) {1244if (backface_collision && p_hit_back_faces) {1245r_normal = -r_normal;1246} else {1247c = false;1248}1249}1250}12511252return c;1253}12541255bool GodotFaceShape3D::intersect_point(const Vector3 &p_point) const {1256return false; //face is flat1257}12581259Vector3 GodotFaceShape3D::get_closest_point_to(const Vector3 &p_point) const {1260return Face3(vertex[0], vertex[1], vertex[2]).get_closest_point_to(p_point);1261}12621263Vector3 GodotFaceShape3D::get_moment_of_inertia(real_t p_mass) const {1264return Vector3(); // Sorry, but i don't think anyone cares, FaceShape!1265}12661267GodotFaceShape3D::GodotFaceShape3D() {1268configure(AABB());1269}12701271Vector<Vector3> GodotConcavePolygonShape3D::get_faces() const {1272Vector<Vector3> rfaces;1273rfaces.resize(faces.size() * 3);12741275for (int i = 0; i < faces.size(); i++) {1276Face f = faces.get(i);12771278for (int j = 0; j < 3; j++) {1279rfaces.set(i * 3 + j, vertices.get(f.indices[j]));1280}1281}12821283return rfaces;1284}12851286void GodotConcavePolygonShape3D::project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const {1287int count = vertices.size();1288if (count == 0) {1289r_min = 0;1290r_max = 0;1291return;1292}1293const Vector3 *vptr = vertices.ptr();12941295for (int i = 0; i < count; i++) {1296real_t d = p_normal.dot(p_transform.xform(vptr[i]));12971298if (i == 0 || d > r_max) {1299r_max = d;1300}1301if (i == 0 || d < r_min) {1302r_min = d;1303}1304}1305}13061307Vector3 GodotConcavePolygonShape3D::get_support(const Vector3 &p_normal) const {1308int count = vertices.size();1309if (count == 0) {1310return Vector3();1311}13121313const Vector3 *vptr = vertices.ptr();13141315Vector3 n = p_normal;13161317int vert_support_idx = -1;1318real_t support_max = 0;13191320for (int i = 0; i < count; i++) {1321real_t d = n.dot(vptr[i]);13221323if (i == 0 || d > support_max) {1324support_max = d;1325vert_support_idx = i;1326}1327}13281329return vptr[vert_support_idx];1330}13311332void GodotConcavePolygonShape3D::_cull_segment(int p_idx, _SegmentCullParams *p_params) const {1333const BVH *params_bvh = &p_params->bvh[p_idx];13341335if (!params_bvh->aabb.intersects_segment(p_params->from, p_params->to)) {1336return;1337}13381339if (params_bvh->face_index >= 0) {1340const Face *f = &p_params->faces[params_bvh->face_index];1341GodotFaceShape3D *face = p_params->face;1342face->normal = f->normal;1343face->vertex[0] = p_params->vertices[f->indices[0]];1344face->vertex[1] = p_params->vertices[f->indices[1]];1345face->vertex[2] = p_params->vertices[f->indices[2]];13461347Vector3 res;1348Vector3 normal;1349int face_index = params_bvh->face_index;1350if (face->intersect_segment(p_params->from, p_params->to, res, normal, face_index, true)) {1351real_t d = p_params->dir.dot(res) - p_params->dir.dot(p_params->from);1352if ((d > 0) && (d < p_params->min_d)) {1353p_params->min_d = d;1354p_params->result = res;1355p_params->normal = normal;1356p_params->face_index = face_index;1357p_params->collisions++;1358}1359}1360} else {1361if (params_bvh->left >= 0) {1362_cull_segment(params_bvh->left, p_params);1363}1364if (params_bvh->right >= 0) {1365_cull_segment(params_bvh->right, p_params);1366}1367}1368}13691370bool GodotConcavePolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {1371if (faces.is_empty()) {1372return false;1373}13741375// unlock data1376const Face *fr = faces.ptr();1377const Vector3 *vr = vertices.ptr();1378const BVH *br = bvh.ptr();13791380GodotFaceShape3D face;1381face.backface_collision = backface_collision && p_hit_back_faces;13821383_SegmentCullParams params;1384params.from = p_begin;1385params.to = p_end;1386params.dir = (p_end - p_begin).normalized();13871388params.faces = fr;1389params.vertices = vr;1390params.bvh = br;13911392params.face = &face;13931394// cull1395_cull_segment(0, ¶ms);13961397if (params.collisions > 0) {1398r_result = params.result;1399r_normal = params.normal;1400r_face_index = params.face_index;1401return true;1402} else {1403return false;1404}1405}14061407bool GodotConcavePolygonShape3D::intersect_point(const Vector3 &p_point) const {1408return false; //face is flat1409}14101411Vector3 GodotConcavePolygonShape3D::get_closest_point_to(const Vector3 &p_point) const {1412return Vector3();1413}14141415bool GodotConcavePolygonShape3D::_cull(int p_idx, _CullParams *p_params) const {1416const BVH *params_bvh = &p_params->bvh[p_idx];14171418if (!p_params->aabb.intersects(params_bvh->aabb)) {1419return false;1420}14211422if (params_bvh->face_index >= 0) {1423const Face *f = &p_params->faces[params_bvh->face_index];1424GodotFaceShape3D *face = p_params->face;1425face->normal = f->normal;1426face->vertex[0] = p_params->vertices[f->indices[0]];1427face->vertex[1] = p_params->vertices[f->indices[1]];1428face->vertex[2] = p_params->vertices[f->indices[2]];1429if (p_params->callback(p_params->userdata, face)) {1430return true;1431}1432} else {1433if (params_bvh->left >= 0) {1434if (_cull(params_bvh->left, p_params)) {1435return true;1436}1437}14381439if (params_bvh->right >= 0) {1440if (_cull(params_bvh->right, p_params)) {1441return true;1442}1443}1444}14451446return false;1447}14481449void GodotConcavePolygonShape3D::cull(const AABB &p_local_aabb, QueryCallback p_callback, void *p_userdata, bool p_invert_backface_collision) const {1450// make matrix local to concave1451if (faces.is_empty()) {1452return;1453}14541455AABB local_aabb = p_local_aabb;14561457// unlock data1458const Face *fr = faces.ptr();1459const Vector3 *vr = vertices.ptr();1460const BVH *br = bvh.ptr();14611462GodotFaceShape3D face; // use this to send in the callback1463face.backface_collision = backface_collision;1464face.invert_backface_collision = p_invert_backface_collision;14651466_CullParams params;1467params.aabb = local_aabb;1468params.face = &face;1469params.faces = fr;1470params.vertices = vr;1471params.bvh = br;1472params.callback = p_callback;1473params.userdata = p_userdata;14741475// cull1476_cull(0, ¶ms);1477}14781479Vector3 GodotConcavePolygonShape3D::get_moment_of_inertia(real_t p_mass) const {1480// use bad AABB approximation1481Vector3 extents = get_aabb().size * 0.5;14821483return Vector3(1484(p_mass / 3.0) * (extents.y * extents.y + extents.z * extents.z),1485(p_mass / 3.0) * (extents.x * extents.x + extents.z * extents.z),1486(p_mass / 3.0) * (extents.x * extents.x + extents.y * extents.y));1487}14881489struct _Volume_BVH_Element {1490AABB aabb;1491Vector3 center;1492int face_index = 0;1493};14941495struct _Volume_BVH_CompareX {1496_FORCE_INLINE_ bool operator()(const _Volume_BVH_Element &a, const _Volume_BVH_Element &b) const {1497return a.center.x < b.center.x;1498}1499};15001501struct _Volume_BVH_CompareY {1502_FORCE_INLINE_ bool operator()(const _Volume_BVH_Element &a, const _Volume_BVH_Element &b) const {1503return a.center.y < b.center.y;1504}1505};15061507struct _Volume_BVH_CompareZ {1508_FORCE_INLINE_ bool operator()(const _Volume_BVH_Element &a, const _Volume_BVH_Element &b) const {1509return a.center.z < b.center.z;1510}1511};15121513struct _Volume_BVH {1514AABB aabb;1515_Volume_BVH *left = nullptr;1516_Volume_BVH *right = nullptr;15171518int face_index = 0;1519};15201521_Volume_BVH *_volume_build_bvh(_Volume_BVH_Element *p_elements, int p_size, int &count) {1522_Volume_BVH *bvh = memnew(_Volume_BVH);15231524if (p_size == 1) {1525//leaf1526bvh->aabb = p_elements[0].aabb;1527bvh->left = nullptr;1528bvh->right = nullptr;1529bvh->face_index = p_elements->face_index;1530count++;1531return bvh;1532} else {1533bvh->face_index = -1;1534}15351536AABB aabb;1537for (int i = 0; i < p_size; i++) {1538if (i == 0) {1539aabb = p_elements[i].aabb;1540} else {1541aabb.merge_with(p_elements[i].aabb);1542}1543}1544bvh->aabb = aabb;1545switch (aabb.get_longest_axis_index()) {1546case 0: {1547SortArray<_Volume_BVH_Element, _Volume_BVH_CompareX> sort_x;1548sort_x.sort(p_elements, p_size);15491550} break;1551case 1: {1552SortArray<_Volume_BVH_Element, _Volume_BVH_CompareY> sort_y;1553sort_y.sort(p_elements, p_size);1554} break;1555case 2: {1556SortArray<_Volume_BVH_Element, _Volume_BVH_CompareZ> sort_z;1557sort_z.sort(p_elements, p_size);1558} break;1559}15601561int split = p_size / 2;1562bvh->left = _volume_build_bvh(p_elements, split, count);1563bvh->right = _volume_build_bvh(&p_elements[split], p_size - split, count);15641565//printf("branch at %p - %i: %i\n",bvh,count,bvh->face_index);1566count++;1567return bvh;1568}15691570void GodotConcavePolygonShape3D::_fill_bvh(_Volume_BVH *p_bvh_tree, BVH *p_bvh_array, int &p_idx) {1571int idx = p_idx;15721573p_bvh_array[idx].aabb = p_bvh_tree->aabb;1574p_bvh_array[idx].face_index = p_bvh_tree->face_index;1575//printf("%p - %i: %i(%p) -- %p:%p\n",%p_bvh_array[idx],p_idx,p_bvh_array[i]->face_index,&p_bvh_tree->face_index,p_bvh_tree->left,p_bvh_tree->right);15761577if (p_bvh_tree->left) {1578p_bvh_array[idx].left = ++p_idx;1579_fill_bvh(p_bvh_tree->left, p_bvh_array, p_idx);15801581} else {1582p_bvh_array[p_idx].left = -1;1583}15841585if (p_bvh_tree->right) {1586p_bvh_array[idx].right = ++p_idx;1587_fill_bvh(p_bvh_tree->right, p_bvh_array, p_idx);15881589} else {1590p_bvh_array[p_idx].right = -1;1591}15921593memdelete(p_bvh_tree);1594}15951596void GodotConcavePolygonShape3D::_setup(const Vector<Vector3> &p_faces, bool p_backface_collision) {1597int src_face_count = p_faces.size();1598if (src_face_count == 0) {1599configure(AABB());1600return;1601}1602ERR_FAIL_COND(src_face_count % 3);1603src_face_count /= 3;16041605const Vector3 *facesr = p_faces.ptr();16061607Vector<_Volume_BVH_Element> bvh_array;1608bvh_array.resize(src_face_count);16091610_Volume_BVH_Element *bvh_arrayw = bvh_array.ptrw();16111612faces.resize(src_face_count);1613Face *facesw = faces.ptrw();16141615vertices.resize(src_face_count * 3);16161617Vector3 *verticesw = vertices.ptrw();16181619AABB _aabb;16201621for (int i = 0; i < src_face_count; i++) {1622Face3 face(facesr[i * 3 + 0], facesr[i * 3 + 1], facesr[i * 3 + 2]);16231624bvh_arrayw[i].aabb = face.get_aabb();1625bvh_arrayw[i].center = bvh_arrayw[i].aabb.get_center();1626bvh_arrayw[i].face_index = i;1627facesw[i].indices[0] = i * 3 + 0;1628facesw[i].indices[1] = i * 3 + 1;1629facesw[i].indices[2] = i * 3 + 2;1630facesw[i].normal = face.get_plane().normal;1631verticesw[i * 3 + 0] = face.vertex[0];1632verticesw[i * 3 + 1] = face.vertex[1];1633verticesw[i * 3 + 2] = face.vertex[2];1634if (i == 0) {1635_aabb = bvh_arrayw[i].aabb;1636} else {1637_aabb.merge_with(bvh_arrayw[i].aabb);1638}1639}16401641int count = 0;1642_Volume_BVH *bvh_tree = _volume_build_bvh(bvh_arrayw, src_face_count, count);16431644bvh.resize(count + 1);16451646BVH *bvh_arrayw2 = bvh.ptrw();16471648int idx = 0;1649_fill_bvh(bvh_tree, bvh_arrayw2, idx);16501651backface_collision = p_backface_collision;16521653configure(_aabb); // this type of shape has no margin1654}16551656void GodotConcavePolygonShape3D::set_data(const Variant &p_data) {1657Dictionary d = p_data;1658ERR_FAIL_COND(!d.has("faces"));16591660_setup(d["faces"], d["backface_collision"]);1661}16621663Variant GodotConcavePolygonShape3D::get_data() const {1664Dictionary d;1665d["faces"] = get_faces();1666d["backface_collision"] = backface_collision;16671668return d;1669}16701671GodotConcavePolygonShape3D::GodotConcavePolygonShape3D() {1672}16731674/* HEIGHT MAP SHAPE */16751676Vector<real_t> GodotHeightMapShape3D::get_heights() const {1677return heights;1678}16791680int GodotHeightMapShape3D::get_width() const {1681return width;1682}16831684int GodotHeightMapShape3D::get_depth() const {1685return depth;1686}16871688void GodotHeightMapShape3D::project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const {1689//not very useful, but not very used either1690p_transform.xform(get_aabb()).project_range_in_plane(Plane(p_normal), r_min, r_max);1691}16921693Vector3 GodotHeightMapShape3D::get_support(const Vector3 &p_normal) const {1694//not very useful, but not very used either1695return get_aabb().get_support(p_normal);1696}16971698struct _HeightmapSegmentCullParams {1699Vector3 from;1700Vector3 to;1701Vector3 dir;17021703Vector3 result;1704Vector3 normal;17051706const GodotHeightMapShape3D *heightmap = nullptr;1707GodotFaceShape3D *face = nullptr;1708};17091710struct _HeightmapGridCullState {1711real_t length = 0.0;1712real_t length_flat = 0.0;17131714real_t dist = 0.0;1715real_t prev_dist = 0.0;17161717int x = 0;1718int z = 0;1719};17201721_FORCE_INLINE_ bool _heightmap_face_cull_segment(_HeightmapSegmentCullParams &p_params) {1722Vector3 res;1723Vector3 normal;1724int fi = -1;1725if (p_params.face->intersect_segment(p_params.from, p_params.to, res, normal, fi, true)) {1726p_params.result = res;1727p_params.normal = normal;17281729return true;1730}17311732return false;1733}17341735_FORCE_INLINE_ bool _heightmap_cell_cull_segment(_HeightmapSegmentCullParams &p_params, const _HeightmapGridCullState &p_state) {1736// First triangle.1737p_params.heightmap->_get_point(p_state.x, p_state.z, p_params.face->vertex[0]);1738p_params.heightmap->_get_point(p_state.x + 1, p_state.z, p_params.face->vertex[1]);1739p_params.heightmap->_get_point(p_state.x, p_state.z + 1, p_params.face->vertex[2]);1740p_params.face->normal = Plane(p_params.face->vertex[0], p_params.face->vertex[1], p_params.face->vertex[2]).normal;1741if (_heightmap_face_cull_segment(p_params)) {1742return true;1743}17441745// Second triangle.1746p_params.face->vertex[0] = p_params.face->vertex[1];1747p_params.heightmap->_get_point(p_state.x + 1, p_state.z + 1, p_params.face->vertex[1]);1748p_params.face->normal = Plane(p_params.face->vertex[0], p_params.face->vertex[1], p_params.face->vertex[2]).normal;1749if (_heightmap_face_cull_segment(p_params)) {1750return true;1751}17521753return false;1754}17551756_FORCE_INLINE_ bool _heightmap_chunk_cull_segment(_HeightmapSegmentCullParams &p_params, const _HeightmapGridCullState &p_state) {1757const GodotHeightMapShape3D::Range &chunk = p_params.heightmap->_get_bounds_chunk(p_state.x, p_state.z);17581759Vector3 enter_pos;1760Vector3 exit_pos;17611762if (p_state.length_flat > CMP_EPSILON) {1763real_t flat_to_3d = p_state.length / p_state.length_flat;1764real_t enter_param = p_state.prev_dist * flat_to_3d;1765real_t exit_param = p_state.dist * flat_to_3d;1766enter_pos = p_params.from + p_params.dir * enter_param;1767exit_pos = p_params.from + p_params.dir * exit_param;1768} else {1769// Consider the ray vertical.1770// (though we shouldn't reach this often because there is an early check up-front)1771enter_pos = p_params.from;1772exit_pos = p_params.to;1773}17741775// Transform positions to heightmap space.1776enter_pos *= GodotHeightMapShape3D::BOUNDS_CHUNK_SIZE;1777exit_pos *= GodotHeightMapShape3D::BOUNDS_CHUNK_SIZE;17781779// We did enter the flat projection of the AABB,1780// but we have to check if we intersect it on the vertical axis.1781if ((enter_pos.y > chunk.max) && (exit_pos.y > chunk.max)) {1782return false;1783}1784if ((enter_pos.y < chunk.min) && (exit_pos.y < chunk.min)) {1785return false;1786}17871788return p_params.heightmap->_intersect_grid_segment(_heightmap_cell_cull_segment, enter_pos, exit_pos, p_params.heightmap->width, p_params.heightmap->depth, p_params.heightmap->local_origin, p_params.result, p_params.normal);1789}17901791template <typename ProcessFunction>1792bool GodotHeightMapShape3D::_intersect_grid_segment(ProcessFunction &p_process, const Vector3 &p_begin, const Vector3 &p_end, int p_width, int p_depth, const Vector3 &offset, Vector3 &r_point, Vector3 &r_normal) const {1793Vector3 delta = (p_end - p_begin);1794real_t length = delta.length();17951796if (length < CMP_EPSILON) {1797return false;1798}17991800Vector3 local_begin = p_begin + offset;18011802GodotFaceShape3D face;1803face.backface_collision = false;18041805_HeightmapSegmentCullParams params;1806params.from = p_begin;1807params.to = p_end;1808params.dir = delta / length;1809params.heightmap = this;1810params.face = &face;18111812_HeightmapGridCullState state;18131814// Perform grid query from projected ray.1815Vector2 ray_dir_flat(delta.x, delta.z);1816state.length = length;1817state.length_flat = ray_dir_flat.length();18181819if (state.length_flat < CMP_EPSILON) {1820ray_dir_flat = Vector2();1821} else {1822ray_dir_flat /= state.length_flat;1823}18241825const int x_step = (ray_dir_flat.x > CMP_EPSILON) ? 1 : ((ray_dir_flat.x < -CMP_EPSILON) ? -1 : 0);1826const int z_step = (ray_dir_flat.y > CMP_EPSILON) ? 1 : ((ray_dir_flat.y < -CMP_EPSILON) ? -1 : 0);18271828const real_t infinite = 1e20;1829const real_t delta_x = (x_step != 0) ? 1.f / Math::abs(ray_dir_flat.x) : infinite;1830const real_t delta_z = (z_step != 0) ? 1.f / Math::abs(ray_dir_flat.y) : infinite;18311832real_t cross_x; // At which value of `param` we will cross a x-axis lane?1833real_t cross_z; // At which value of `param` we will cross a z-axis lane?18341835// X initialization.1836if (x_step != 0) {1837if (x_step == 1) {1838cross_x = (Math::ceil(local_begin.x) - local_begin.x) * delta_x;1839} else {1840cross_x = (local_begin.x - Math::floor(local_begin.x)) * delta_x;1841}1842} else {1843cross_x = infinite; // Will never cross on X.1844}18451846// Z initialization.1847if (z_step != 0) {1848if (z_step == 1) {1849cross_z = (Math::ceil(local_begin.z) - local_begin.z) * delta_z;1850} else {1851cross_z = (local_begin.z - Math::floor(local_begin.z)) * delta_z;1852}1853} else {1854cross_z = infinite; // Will never cross on Z.1855}18561857int x = Math::floor(local_begin.x);1858int z = Math::floor(local_begin.z);18591860// Workaround cases where the ray starts at an integer position.1861if (Math::is_zero_approx(cross_x)) {1862cross_x += delta_x;1863// If going backwards, we should ignore the position we would get by the above flooring,1864// because the ray is not heading in that direction.1865if (x_step == -1) {1866x -= 1;1867}1868}18691870if (Math::is_zero_approx(cross_z)) {1871cross_z += delta_z;1872if (z_step == -1) {1873z -= 1;1874}1875}18761877// Start inside the grid.1878int x_start = MAX(MIN(x, p_width - 2), 0);1879int z_start = MAX(MIN(z, p_depth - 2), 0);18801881// Adjust initial cross values.1882cross_x += delta_x * x_step * (x_start - x);1883cross_z += delta_z * z_step * (z_start - z);18841885x = x_start;1886z = z_start;18871888while (true) {1889state.prev_dist = state.dist;1890state.x = x;1891state.z = z;18921893if (cross_x < cross_z) {1894// X lane.1895x += x_step;1896// Assign before advancing the param,1897// to be in sync with the initialization step.1898state.dist = cross_x;1899cross_x += delta_x;1900} else {1901// Z lane.1902z += z_step;1903state.dist = cross_z;1904cross_z += delta_z;1905}19061907if (state.dist > state.length_flat) {1908state.dist = state.length_flat;1909if (p_process(params, state)) {1910r_point = params.result;1911r_normal = params.normal;1912return true;1913}1914break;1915}19161917if (p_process(params, state)) {1918r_point = params.result;1919r_normal = params.normal;1920return true;1921}19221923// Stop when outside the grid.1924if ((x < 0) || (z < 0) || (x >= p_width - 1) || (z >= p_depth - 1)) {1925break;1926}1927}19281929return false;1930}19311932bool GodotHeightMapShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {1933if (heights.is_empty()) {1934return false;1935}19361937Vector3 local_begin = p_begin + local_origin;1938Vector3 local_end = p_end + local_origin;19391940// Quantize the ray begin/end.1941int begin_x = Math::floor(local_begin.x);1942int begin_z = Math::floor(local_begin.z);1943int end_x = Math::floor(local_end.x);1944int end_z = Math::floor(local_end.z);19451946if ((begin_x == end_x) && (begin_z == end_z)) {1947// Simple case for rays that don't traverse the grid horizontally.1948// Just perform a test on the given cell.1949GodotFaceShape3D face;1950face.backface_collision = p_hit_back_faces;19511952_HeightmapSegmentCullParams params;1953params.from = p_begin;1954params.to = p_end;1955params.dir = (p_end - p_begin).normalized();19561957params.heightmap = this;1958params.face = &face;19591960_HeightmapGridCullState state;1961state.x = MAX(MIN(begin_x, width - 2), 0);1962state.z = MAX(MIN(begin_z, depth - 2), 0);1963if (_heightmap_cell_cull_segment(params, state)) {1964r_point = params.result;1965r_normal = params.normal;1966return true;1967}1968} else if (bounds_grid.is_empty()) {1969// Process all cells intersecting the flat projection of the ray.1970return _intersect_grid_segment(_heightmap_cell_cull_segment, p_begin, p_end, width, depth, local_origin, r_point, r_normal);1971} else {1972Vector3 ray_diff = (p_end - p_begin);1973real_t length_flat_sqr = ray_diff.x * ray_diff.x + ray_diff.z * ray_diff.z;1974if (length_flat_sqr < BOUNDS_CHUNK_SIZE * BOUNDS_CHUNK_SIZE) {1975// Don't use chunks, the ray is too short in the plane.1976return _intersect_grid_segment(_heightmap_cell_cull_segment, p_begin, p_end, width, depth, local_origin, r_point, r_normal);1977} else {1978// The ray is long, run raycast on a higher-level grid.1979Vector3 bounds_from = p_begin / BOUNDS_CHUNK_SIZE;1980Vector3 bounds_to = p_end / BOUNDS_CHUNK_SIZE;1981Vector3 bounds_offset = local_origin / BOUNDS_CHUNK_SIZE;1982// Plus 1 here to width and depth of the chunk because _intersect_grid_segment() is used by cell level as well,1983// and in _intersect_grid_segment() the loop will exit 1 early because for cell point triangle lookup, it dose x + 1, z + 1 etc for the vertex.1984int bounds_width = bounds_grid_width + 1;1985int bounds_depth = bounds_grid_depth + 1;1986return _intersect_grid_segment(_heightmap_chunk_cull_segment, bounds_from, bounds_to, bounds_width, bounds_depth, bounds_offset, r_point, r_normal);1987}1988}19891990return false;1991}19921993bool GodotHeightMapShape3D::intersect_point(const Vector3 &p_point) const {1994return false;1995}19961997Vector3 GodotHeightMapShape3D::get_closest_point_to(const Vector3 &p_point) const {1998return Vector3();1999}20002001void GodotHeightMapShape3D::_get_cell(const Vector3 &p_point, int &r_x, int &r_y, int &r_z) const {2002const AABB &shape_aabb = get_aabb();20032004Vector3 pos_local = shape_aabb.position + local_origin;20052006Vector3 clamped_point(p_point);2007clamped_point = p_point.clamp(pos_local, pos_local + shape_aabb.size);20082009r_x = (clamped_point.x < 0.0) ? (clamped_point.x - 0.5) : (clamped_point.x + 0.5);2010r_y = (clamped_point.y < 0.0) ? (clamped_point.y - 0.5) : (clamped_point.y + 0.5);2011r_z = (clamped_point.z < 0.0) ? (clamped_point.z - 0.5) : (clamped_point.z + 0.5);2012}20132014void GodotHeightMapShape3D::cull(const AABB &p_local_aabb, QueryCallback p_callback, void *p_userdata, bool p_invert_backface_collision) const {2015if (heights.is_empty()) {2016return;2017}20182019AABB local_aabb = p_local_aabb;2020local_aabb.position += local_origin;20212022// Quantize the aabb, and adjust the start/end ranges.2023int aabb_min[3];2024int aabb_max[3];2025_get_cell(local_aabb.position, aabb_min[0], aabb_min[1], aabb_min[2]);2026_get_cell(local_aabb.position + local_aabb.size, aabb_max[0], aabb_max[1], aabb_max[2]);20272028// Expand the min/max quantized values.2029// This is to catch the case where the input aabb falls between grid points.2030for (int i = 0; i < 3; ++i) {2031aabb_min[i]--;2032aabb_max[i]++;2033}20342035int start_x = MAX(0, aabb_min[0]);2036int end_x = MIN(width - 1, aabb_max[0]);2037int start_z = MAX(0, aabb_min[2]);2038int end_z = MIN(depth - 1, aabb_max[2]);20392040GodotFaceShape3D face;2041face.backface_collision = !p_invert_backface_collision;2042face.invert_backface_collision = p_invert_backface_collision;20432044for (int z = start_z; z < end_z; z++) {2045for (int x = start_x; x < end_x; x++) {2046// First triangle.2047_get_point(x, z, face.vertex[0]);2048_get_point(x + 1, z, face.vertex[1]);2049_get_point(x, z + 1, face.vertex[2]);2050face.normal = Plane(face.vertex[0], face.vertex[1], face.vertex[2]).normal;2051if (p_callback(p_userdata, &face)) {2052return;2053}20542055// Second triangle.2056face.vertex[0] = face.vertex[1];2057_get_point(x + 1, z + 1, face.vertex[1]);2058face.normal = Plane(face.vertex[0], face.vertex[1], face.vertex[2]).normal;2059if (p_callback(p_userdata, &face)) {2060return;2061}2062}2063}2064}20652066Vector3 GodotHeightMapShape3D::get_moment_of_inertia(real_t p_mass) const {2067// use bad AABB approximation2068Vector3 extents = get_aabb().size * 0.5;20692070return Vector3(2071(p_mass / 3.0) * (extents.y * extents.y + extents.z * extents.z),2072(p_mass / 3.0) * (extents.x * extents.x + extents.z * extents.z),2073(p_mass / 3.0) * (extents.x * extents.x + extents.y * extents.y));2074}20752076void GodotHeightMapShape3D::_build_accelerator() {2077bounds_grid.clear();20782079bounds_grid_width = width / BOUNDS_CHUNK_SIZE;2080bounds_grid_depth = depth / BOUNDS_CHUNK_SIZE;20812082if (width % BOUNDS_CHUNK_SIZE > 0) {2083++bounds_grid_width; // In case terrain size isn't dividable by chunk size.2084}20852086if (depth % BOUNDS_CHUNK_SIZE > 0) {2087++bounds_grid_depth;2088}20892090uint32_t bound_grid_size = (uint32_t)(bounds_grid_width * bounds_grid_depth);20912092if (bound_grid_size < 2) {2093// Grid is empty or just one chunk.2094return;2095}20962097bounds_grid.resize(bound_grid_size);20982099// Compute min and max height for all chunks.2100for (int cz = 0; cz < bounds_grid_depth; ++cz) {2101int z0 = cz * BOUNDS_CHUNK_SIZE;21022103for (int cx = 0; cx < bounds_grid_width; ++cx) {2104int x0 = cx * BOUNDS_CHUNK_SIZE;21052106Range r;21072108r.min = _get_height(x0, z0);2109r.max = r.min;21102111// Compute min and max height for this chunk.2112// We have to include one extra cell to account for neighbors.2113// Here is why:2114// Say we have a flat terrain, and a plateau that fits a chunk perfectly.2115//2116// Left Right2117// 0---0---0---1---1---12118// | | | | | |2119// 0---0---0---1---1---12120// | | | | | |2121// 0---0---0---1---1---12122// x2123//2124// If the AABB for the Left chunk did not share vertices with the Right,2125// then we would fail collision tests at x due to a gap.2126//2127int z_max = MIN(z0 + BOUNDS_CHUNK_SIZE + 1, depth);2128int x_max = MIN(x0 + BOUNDS_CHUNK_SIZE + 1, width);2129for (int z = z0; z < z_max; ++z) {2130for (int x = x0; x < x_max; ++x) {2131real_t height = _get_height(x, z);2132if (height < r.min) {2133r.min = height;2134} else if (height > r.max) {2135r.max = height;2136}2137}2138}21392140bounds_grid[cx + cz * bounds_grid_width] = r;2141}2142}2143}21442145void GodotHeightMapShape3D::_setup(const Vector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) {2146heights = p_heights;2147width = p_width;2148depth = p_depth;21492150// Initialize aabb.2151AABB aabb_new;2152aabb_new.position = Vector3(0.0, p_min_height, 0.0);2153aabb_new.size = Vector3(p_width - 1, p_max_height - p_min_height, p_depth - 1);21542155// Initialize origin as the aabb center.2156local_origin = aabb_new.position + 0.5 * aabb_new.size;2157local_origin.y = 0.0;21582159aabb_new.position -= local_origin;21602161_build_accelerator();21622163configure(aabb_new);2164}21652166void GodotHeightMapShape3D::set_data(const Variant &p_data) {2167ERR_FAIL_COND(p_data.get_type() != Variant::DICTIONARY);21682169Dictionary d = p_data;2170ERR_FAIL_COND(!d.has("width"));2171ERR_FAIL_COND(!d.has("depth"));2172ERR_FAIL_COND(!d.has("heights"));21732174int width_new = d["width"];2175int depth_new = d["depth"];21762177ERR_FAIL_COND(width_new <= 0.0);2178ERR_FAIL_COND(depth_new <= 0.0);21792180Variant heights_variant = d["heights"];2181Vector<real_t> heights_buffer;2182#ifdef REAL_T_IS_DOUBLE2183if (heights_variant.get_type() == Variant::PACKED_FLOAT64_ARRAY) {2184#else2185if (heights_variant.get_type() == Variant::PACKED_FLOAT32_ARRAY) {2186#endif2187// Ready-to-use heights can be passed.2188heights_buffer = heights_variant;2189} else if (heights_variant.get_type() == Variant::OBJECT) {2190// If an image is passed, we have to convert it.2191// This would be expensive to do with a script, so it's nice to have it here.2192Ref<Image> image = heights_variant;2193ERR_FAIL_COND(image.is_null());2194ERR_FAIL_COND(image->get_format() != Image::FORMAT_RF);21952196PackedByteArray im_data = image->get_data();2197heights_buffer.resize(image->get_width() * image->get_height());21982199real_t *w = heights_buffer.ptrw();2200real_t *rp = (real_t *)im_data.ptr();2201for (int i = 0; i < heights_buffer.size(); ++i) {2202w[i] = rp[i];2203}2204} else {2205#ifdef REAL_T_IS_DOUBLE2206ERR_FAIL_MSG("Expected PackedFloat64Array or float Image.");2207#else2208ERR_FAIL_MSG("Expected PackedFloat32Array or float Image.");2209#endif2210}22112212// Compute min and max heights or use precomputed values.2213real_t min_height = 0.0;2214real_t max_height = 0.0;2215if (d.has("min_height") && d.has("max_height")) {2216min_height = d["min_height"];2217max_height = d["max_height"];2218} else {2219int heights_size = heights.size();2220for (int i = 0; i < heights_size; ++i) {2221real_t h = heights[i];2222if (h < min_height) {2223min_height = h;2224} else if (h > max_height) {2225max_height = h;2226}2227}2228}22292230ERR_FAIL_COND(min_height > max_height);22312232ERR_FAIL_COND(heights_buffer.size() != (width_new * depth_new));22332234// If specified, min and max height will be used as precomputed values.2235_setup(heights_buffer, width_new, depth_new, min_height, max_height);2236}22372238Variant GodotHeightMapShape3D::get_data() const {2239Dictionary d;2240d["width"] = width;2241d["depth"] = depth;22422243const AABB &shape_aabb = get_aabb();2244d["min_height"] = shape_aabb.position.y;2245d["max_height"] = shape_aabb.position.y + shape_aabb.size.y;22462247d["heights"] = heights;22482249return d;2250}22512252GodotHeightMapShape3D::GodotHeightMapShape3D() {2253}225422552256