diff options
64 files changed, 765 insertions, 232 deletions
diff --git a/AUTHORS.md b/AUTHORS.md index 4ae6db35d7..c5981306c4 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -55,6 +55,7 @@ name is available. Dana Olson (adolson) Daniel J. Ramirez (djrm) Daniel Rakos (aqnuep) + David Sichma (DavidSichma) Dharkael (lupoDharkael) Dmitry Koteroff (Krakean) DualMatrix @@ -120,6 +121,7 @@ name is available. Patrick (firefly2442) Paul Batty (Paulb23) Paul Joannon (paulloz) + Paul Trojahn (ptrojahn) Pawel Kowal (pkowal1982) Pedro J. Estébanez (RandomShaper) Pieter-Jan Briers (PJB3005) @@ -14,10 +14,12 @@ generous deed immortalized in the next stable release of Godot Engine. Enjin Coin <https://enjincoin.io> Heroic Labs <https://heroiclabs.com> + Interblock <http://interblockgaming.com> ## Gold sponsors Gamblify <https://www.gamblify.com> + Image Campus <https://www.imagecampus.edu.ar> Moonwards <https://www.moonwards.com> ## Mini sponsors @@ -27,8 +29,7 @@ generous deed immortalized in the next stable release of Godot Engine. Anandarup Mallik Andrew Dunai Brandon Lamb - Christian Chipont - Christian Uldall Pedersen + Christopher Montesano Christoph Woinke Darkhan Baimyrza Denis Malyavin @@ -42,6 +43,7 @@ generous deed immortalized in the next stable release of Godot Engine. Javary Co. Jeffery Chiu Jeppe Zapp + John Benard (Linuxydable) Justin Arnold Justo Delgado Baudí Kyle Szklenski @@ -52,7 +54,6 @@ generous deed immortalized in the next stable release of Godot Engine. Mike King Nathan Warden Neal Gompa (Conan Kudo) - Patrick Aarstad ScottMakesGames Slobodan Milnovic Stephan Lanfermann @@ -64,7 +65,6 @@ generous deed immortalized in the next stable release of Godot Engine. Andrei cheese65536 - Daniel Hartmann Dave David Gehrig David Snopek @@ -82,18 +82,17 @@ generous deed immortalized in the next stable release of Godot Engine. Taylor Ritenour Zaven Muradyan + _ Alexander Trey Saunders Asher Glick Austen McRae Bernhard Werner beVR Brian van der Stel - Cameron MacNair Carlo Cabanilla Daniel James David Giardi Default Name - Edward E Florian Breisch Gamejunkey Gero @@ -103,6 +102,7 @@ generous deed immortalized in the next stable release of Godot Engine. Jon Woodward Jose Fernando Alexandre Karl Werf + Klavdij Voncina Lex Steers Luke Maciej Pendolski @@ -115,11 +115,11 @@ generous deed immortalized in the next stable release of Godot Engine. Scott Wadden Sergey Shawn Yu + The Architect thechris Tom Langwaldt tukon Unseen Domains - wmww Alex Khayrullin Branwyn Tylwyth @@ -129,19 +129,18 @@ generous deed immortalized in the next stable release of Godot Engine. Conrad Curry Craig Smith Darrian Little - Dean Harmon Ivan Trombley + Jakub Dering Joan Fons Joshua Flores Krzysztof Jankowski Lord Bloodhound - Lucas Ferreira Franca Nathan Lundquist - Nicklas Breum Pascal Grüter Petr Malac Rami Reneator + Rob Robert Willes Ronnie Ashlock Thomas Bjarnelöf @@ -156,6 +155,7 @@ generous deed immortalized in the next stable release of Godot Engine. André Frélicot andres eduardo lopez Andrew Bowen + aoshiwik Ben Botwin Carlos de Sousa Marques Chase Taranto @@ -166,7 +166,6 @@ generous deed immortalized in the next stable release of Godot Engine. D Daniel Eichler David White - Eric Eric Monson Erik Hatfield Eugenio Hugo Salgüero Jáñez @@ -189,8 +188,6 @@ generous deed immortalized in the next stable release of Godot Engine. Juraj Móza Kasper Jeppesen kinfox - Klaus The - Klavdij Voncina Maarten Elings Marcelo Dornbusch Lopes Markus Fehr @@ -199,7 +196,6 @@ generous deed immortalized in the next stable release of Godot Engine. Marvin Matt Eunson Max Bulai - Max R.R. Collada MuffinManKen Nick Nikitin Oliver Dick @@ -218,7 +214,9 @@ generous deed immortalized in the next stable release of Godot Engine. Scott Pilet spilldata Stoned Xander + TheLevelOfDetail . Thomas Krampl + Thomas Kurz Tobias Bocanegra Urho Zie Weaver @@ -236,27 +234,25 @@ generous deed immortalized in the next stable release of Godot Engine. Adam Smeltzer Adisibio Adrian Demetrescu - Aggelos Arnaoutis Agustinus Arya Aidan O'Flannagain + Aki Mimoto Albin Jonasson Svärdsby Alder Stefano Alessandro Senese - Alexander Koppe Alexandre Alex Davies-Moore Allen Schade Andreas Evers Andreas Krampitz - Andreas Schüle André Simões Andrés Rodríguez Andrew Thomas Andrzej Skalski + Angelos Arnaoutis Anthony Bongiovanni Anthony Staunton Anton Kurkin - Antony K. Jones AP Condomines Arda Erol Arseniy M @@ -266,7 +262,6 @@ generous deed immortalized in the next stable release of Godot Engine. Avencherus B A Balázs Batári - Beliar Benedikt Ben G Ben Phelan @@ -278,20 +273,21 @@ generous deed immortalized in the next stable release of Godot Engine. Bryan Stevenson Carl van der Geest Carwyn Edwards + Casey M. Chris Brown Chris Chapin Christian Baune + Christian Chipont Christian Winter Christoffer Sundbom Christopher Schmitt Clay Heaton - Cobaltum Collin Shooltz Daniel Johnson + Daniel Kimblad DanielMaximiano Daniel Pontillo Daniel Reed - David Cravens David May David Rapisarda David Woodard @@ -308,14 +304,16 @@ generous deed immortalized in the next stable release of Godot Engine. Ellen Marie Dash Elmeri '- Duy Kevin Nguyen Emanuel Kotzayan + Ennemoser Ernst Eric Ellingson Eric Martini Eric Williams + Erkki Seppälä EugeneTel Evan Rose Felix Kollmann - fengjiongmax Flaredown + Florian Richer FuDiggity G3Dev sàrl Gadzhi Kharkharov @@ -324,6 +322,7 @@ generous deed immortalized in the next stable release of Godot Engine. George Marques gmmath Greg Olson + GREGORY C FEIN Greg P Greyson Richey Guldoman @@ -332,7 +331,6 @@ generous deed immortalized in the next stable release of Godot Engine. Hiroshi Naruo Hunter Jones Hylpher - ialex32x Iiari IndustrialRobot Isaac Morton @@ -341,6 +339,7 @@ generous deed immortalized in the next stable release of Godot Engine. Jaime Ruiz-Borau Vizárraga Jako Danar James A F Manley + Jannik Gröger Jax Jed Jeff Hungerford @@ -353,7 +352,6 @@ generous deed immortalized in the next stable release of Godot Engine. Joel Fivat Joel Setterberg Johannes Wuensch - John Selig Jomei Jackson Jonas Rudlang Jonas Yamazaki @@ -365,9 +363,7 @@ generous deed immortalized in the next stable release of Godot Engine. Jose Andrés Mejias Rojas Jose C. Rubio Joseph Catrambone - Josh 'Cheeseness' Bush Juanfran - Juan Negrier Judd Julian Murgia JungleRobba @@ -378,7 +374,6 @@ generous deed immortalized in the next stable release of Godot Engine. KC Chan Keedong Park Keith Bradner - Kevin Kolcheck Kevin McPhillips kickmaniac Kiyohiro Kawamura (kyorohiro) @@ -400,12 +395,12 @@ generous deed immortalized in the next stable release of Godot Engine. Malik Ahmed Malik Nejer Marcus Richter - Markus Lohaus Markus Michael Egger Martin Candela Martin Holas Martin Liška Matthew Little + Matti Pohjanvirta Maxime Blade Maxwell medecau @@ -417,12 +412,11 @@ generous deed immortalized in the next stable release of Godot Engine. Michael Haney Michael Labbe Mikael Olsson - Mikayla Hutchinson + Mikayla Mike Birkhead Mitchell J. Wagner MoM Nathan Fish - Nathan W Natrim nee Neil Blakey-Milner @@ -440,20 +434,19 @@ generous deed immortalized in the next stable release of Godot Engine. Olivier Omar Delarosa Oscar Norlander - Pafka Pan Ip - Patrick Forringer Patrick Nafarrete Paul Gieske Paul Mason Paweł Kowal + Penguin + Petrus Prinsloo Philip Cohoe Pierre-Igor Berthet Pitsanu Tongprasin Point08 Poryg Rafa Laguna - Rafal Wyszomirski rainerLinux Raphael Leroux Remi Rampin @@ -462,6 +455,7 @@ generous deed immortalized in the next stable release of Godot Engine. Ricardo Alcantara Richard Diss Richard Ivánek + Richard Patching Robert Farr (Larington) Robert Hernandez Rodrigo Loli @@ -471,7 +465,7 @@ generous deed immortalized in the next stable release of Godot Engine. Ronan Jouchet Ryan Groom Ryan Hentz - Saad Khoudmi + Sam.C Sam Edson Samuele Zolfanelli Sanka.X @@ -487,13 +481,11 @@ generous deed immortalized in the next stable release of Godot Engine. SK smbe19 Sootstone - Stonepyre Svenne Krap - The Architect + tezuvholovdr thomas Thomas Bechtold Thomas Kelly - Thomas Kurz tiansheng li Tim Drumheller Timothy B. MacDonald @@ -504,7 +496,6 @@ generous deed immortalized in the next stable release of Godot Engine. Torsten Crass Travis O'Brien Trent Skinner - Triptych Troy Bonneau Tryggve Sollid Turgut Temucin diff --git a/core/color.cpp b/core/color.cpp index a54a3115cc..a6ad50b745 100644 --- a/core/color.cpp +++ b/core/color.cpp @@ -214,6 +214,11 @@ void Color::set_hsv(float p_h, float p_s, float p_v, float p_alpha) { } } +bool Color::is_equal_approx(const Color &p_color) const { + + return Math::is_equal_approx(r, p_color.r) && Math::is_equal_approx(g, p_color.g) && Math::is_equal_approx(b, p_color.b) && Math::is_equal_approx(a, p_color.a); +} + void Color::invert() { r = 1.0 - r; diff --git a/core/color.h b/core/color.h index 8fb78d1ced..b34a82ef19 100644 --- a/core/color.h +++ b/core/color.h @@ -86,6 +86,8 @@ struct Color { void operator/=(const Color &p_color); void operator/=(const real_t &rvalue); + bool is_equal_approx(const Color &p_color) const; + void invert(); void contrast(); Color inverted() const; diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index ae2b56e7b7..810e290922 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -164,23 +164,23 @@ void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) { } Segment s(p_id, p_with_id); - if (s.from == p_id) { - s.from_point = a; - s.to_point = b; - } else { - s.from_point = b; - s.to_point = a; + if (bidirectional) s.direction = Segment::BIDIRECTIONAL; + + Set<Segment>::Element *element = segments.find(s); + if (element != NULL) { + s.direction |= element->get().direction; + if (s.direction == Segment::BIDIRECTIONAL) { + // Both are neighbours of each other now + a->unlinked_neighbours.remove(b->id); + b->unlinked_neighbours.remove(a->id); + } + segments.erase(element); } segments.insert(s); } -void AStar::disconnect_points(int p_id, int p_with_id) { - - Segment s(p_id, p_with_id); - ERR_FAIL_COND(!segments.has(s)); - - segments.erase(s); +void AStar::disconnect_points(int p_id, int p_with_id, bool bidirectional) { Point *a; bool a_exists = points.lookup(p_id, a); @@ -190,10 +190,33 @@ void AStar::disconnect_points(int p_id, int p_with_id) { bool b_exists = points.lookup(p_with_id, b); CRASH_COND(!b_exists); - a->neighbours.remove(b->id); - a->unlinked_neighbours.remove(b->id); - b->neighbours.remove(a->id); - b->unlinked_neighbours.remove(a->id); + Segment s(p_id, p_with_id); + int remove_direction = bidirectional ? (int)Segment::BIDIRECTIONAL : s.direction; + + Set<Segment>::Element *element = segments.find(s); + if (element != NULL) { + // s is the new segment + // Erase the directions to be removed + s.direction = (element->get().direction & ~remove_direction); + + a->neighbours.remove(b->id); + if (bidirectional) { + b->neighbours.remove(a->id); + if (element->get().direction != Segment::BIDIRECTIONAL) { + a->unlinked_neighbours.remove(b->id); + b->unlinked_neighbours.remove(a->id); + } + } else { + if (s.direction == Segment::NONE) + b->unlinked_neighbours.remove(a->id); + else + a->unlinked_neighbours.set(b->id, b); + } + + segments.erase(element); + if (s.direction != Segment::NONE) + segments.insert(s); + } } bool AStar::has_point(int p_id) const { @@ -227,10 +250,13 @@ PoolVector<int> AStar::get_point_connections(int p_id) { return point_list; } -bool AStar::are_points_connected(int p_id, int p_with_id) const { +bool AStar::are_points_connected(int p_id, int p_with_id, bool bidirectional) const { Segment s(p_id, p_with_id); - return segments.has(s); + const Set<Segment>::Element *element = segments.find(s); + + return element != NULL && + (bidirectional || (element->get().direction & s.direction) == s.direction); } void AStar::clear() { @@ -284,13 +310,17 @@ Vector3 AStar::get_closest_position_in_segment(const Vector3 &p_point) const { for (const Set<Segment>::Element *E = segments.front(); E; E = E->next()) { - if (!(E->get().from_point->enabled && E->get().to_point->enabled)) { + Point *from_point = nullptr, *to_point = nullptr; + points.lookup(E->get().u, from_point); + points.lookup(E->get().v, to_point); + + if (!(from_point->enabled && to_point->enabled)) { continue; } Vector3 segment[2] = { - E->get().from_point->pos, - E->get().to_point->pos, + from_point->pos, + to_point->pos, }; Vector3 p = Geometry::get_closest_point_to_segment(p_point, segment); @@ -532,8 +562,8 @@ void AStar::_bind_methods() { ClassDB::bind_method(D_METHOD("is_point_disabled", "id"), &AStar::is_point_disabled); ClassDB::bind_method(D_METHOD("connect_points", "id", "to_id", "bidirectional"), &AStar::connect_points, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("disconnect_points", "id", "to_id"), &AStar::disconnect_points); - ClassDB::bind_method(D_METHOD("are_points_connected", "id", "to_id"), &AStar::are_points_connected); + ClassDB::bind_method(D_METHOD("disconnect_points", "id", "to_id", "bidirectional"), &AStar::disconnect_points, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("are_points_connected", "id", "to_id", "bidirectional"), &AStar::are_points_connected, DEFVAL(true)); ClassDB::bind_method(D_METHOD("get_point_count"), &AStar::get_point_count); ClassDB::bind_method(D_METHOD("get_point_capacity"), &AStar::get_point_capacity); diff --git a/core/math/a_star.h b/core/math/a_star.h index 0a5d3e992c..8ff62e646b 100644 --- a/core/math/a_star.h +++ b/core/math/a_star.h @@ -81,24 +81,35 @@ class AStar : public Reference { struct Segment { union { struct { - int32_t from; - int32_t to; + int32_t u; + int32_t v; }; uint64_t key; }; - Point *from_point; - Point *to_point; + enum { + NONE = 0, + FORWARD = 1, + BACKWARD = 2, + BIDIRECTIONAL = FORWARD | BACKWARD + }; + unsigned char direction; bool operator<(const Segment &p_s) const { return key < p_s.key; } - Segment() { key = 0; } + Segment() { + key = 0; + direction = NONE; + } Segment(int p_from, int p_to) { - if (p_from > p_to) { - SWAP(p_from, p_to); + if (p_from < p_to) { + u = p_from; + v = p_to; + direction = FORWARD; + } else { + u = p_to; + v = p_from; + direction = BACKWARD; } - - from = p_from; - to = p_to; } }; @@ -133,8 +144,8 @@ public: bool is_point_disabled(int p_id) const; void connect_points(int p_id, int p_with_id, bool bidirectional = true); - void disconnect_points(int p_id, int p_with_id); - bool are_points_connected(int p_id, int p_with_id) const; + void disconnect_points(int p_id, int p_with_id, bool bidirectional = true); + bool are_points_connected(int p_id, int p_with_id, bool bidirectional = true) const; int get_point_count() const; int get_point_capacity() const; diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp index a4eb1fe2a5..27da061274 100644 --- a/core/math/aabb.cpp +++ b/core/math/aabb.cpp @@ -69,6 +69,11 @@ void AABB::merge_with(const AABB &p_aabb) { size = max - min; } +bool AABB::is_equal_approx(const AABB &p_aabb) const { + + return position.is_equal_approx(p_aabb.position) && size.is_equal_approx(p_aabb.size); +} + AABB AABB::intersection(const AABB &p_aabb) const { Vector3 src_min = position; diff --git a/core/math/aabb.h b/core/math/aabb.h index 52e5ed3626..c3ce33c6f4 100644 --- a/core/math/aabb.h +++ b/core/math/aabb.h @@ -64,6 +64,7 @@ public: bool operator==(const AABB &p_rval) const; bool operator!=(const AABB &p_rval) const; + bool is_equal_approx(const AABB &p_aabb) const; _FORCE_INLINE_ bool intersects(const AABB &p_aabb) const; /// Both AABBs overlap _FORCE_INLINE_ bool intersects_inclusive(const AABB &p_aabb) const; /// Both AABBs (or their faces) overlap _FORCE_INLINE_ bool encloses(const AABB &p_aabb) const; /// p_aabb is completely inside this diff --git a/core/math/basis.cpp b/core/math/basis.cpp index 0a491010e2..d77501c0f6 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -106,17 +106,17 @@ Basis Basis::orthonormalized() const { } bool Basis::is_orthogonal() const { - Basis id; + Basis identity; Basis m = (*this) * transposed(); - return is_equal_approx(id, m); + return m.is_equal_approx(identity); } bool Basis::is_diagonal() const { return ( - Math::is_equal_approx(elements[0][1], 0) && Math::is_equal_approx(elements[0][2], 0) && - Math::is_equal_approx(elements[1][0], 0) && Math::is_equal_approx(elements[1][2], 0) && - Math::is_equal_approx(elements[2][0], 0) && Math::is_equal_approx(elements[2][1], 0)); + Math::is_zero_approx(elements[0][1]) && Math::is_zero_approx(elements[0][2]) && + Math::is_zero_approx(elements[1][0]) && Math::is_zero_approx(elements[1][2]) && + Math::is_zero_approx(elements[2][0]) && Math::is_zero_approx(elements[2][1])); } bool Basis::is_rotation() const { @@ -557,16 +557,9 @@ void Basis::set_euler_yxz(const Vector3 &p_euler) { *this = ymat * xmat * zmat; } -bool Basis::is_equal_approx(const Basis &a, const Basis &b, real_t p_epsilon) const { +bool Basis::is_equal_approx(const Basis &p_basis) const { - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - if (!Math::is_equal_approx(a.elements[i][j], b.elements[i][j], p_epsilon)) - return false; - } - } - - return true; + return elements[0].is_equal_approx(p_basis.elements[0]) && elements[1].is_equal_approx(p_basis.elements[1]) && elements[2].is_equal_approx(p_basis.elements[2]); } bool Basis::is_equal_approx_ratio(const Basis &a, const Basis &b, real_t p_epsilon) const { diff --git a/core/math/basis.h b/core/math/basis.h index 4be4ea4cd3..9b2e38b3d3 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -127,7 +127,9 @@ public: return elements[0][2] * v[0] + elements[1][2] * v[1] + elements[2][2] * v[2]; } - bool is_equal_approx(const Basis &a, const Basis &b, real_t p_epsilon = CMP_EPSILON) const; + bool is_equal_approx(const Basis &p_basis) const; + // TODO: Break compatibility in 4.0 by getting rid of this so that it's only an instance method. See also TODO in variant_call.cpp + bool is_equal_approx(const Basis &a, const Basis &b) const { return a.is_equal_approx(b); } bool is_equal_approx_ratio(const Basis &a, const Basis &b, real_t p_epsilon = UNIT_EPSILON) const; bool operator==(const Basis &p_matrix) const; diff --git a/core/math/bsp_tree.cpp b/core/math/bsp_tree.cpp index cfa698282e..ece293d036 100644 --- a/core/math/bsp_tree.cpp +++ b/core/math/bsp_tree.cpp @@ -364,7 +364,7 @@ static int _bsp_create_node(const Face3 *p_faces, const Vector<int> &p_indices, const Face3 &f = p_faces[indices[i]]; /* - if (f.get_plane().is_almost_like(divisor_plane)) + if (f.get_plane().is_equal_approx(divisor_plane)) continue; */ @@ -412,7 +412,7 @@ static int _bsp_create_node(const Face3 *p_faces, const Vector<int> &p_indices, for (int i = 0; i < p_planes.size(); i++) { - if (p_planes[i].is_almost_like(divisor_plane)) { + if (p_planes[i].is_equal_approx(divisor_plane)) { divisor_plane_idx = i; break; } diff --git a/core/math/delaunay.h b/core/math/delaunay.h index 3f8013a3e6..89a34de082 100644 --- a/core/math/delaunay.h +++ b/core/math/delaunay.h @@ -80,11 +80,11 @@ public: } static bool edge_compare(const Vector<Vector2> &p_vertices, const Edge &p_a, const Edge &p_b) { - if (p_vertices[p_a.edge[0]] == p_vertices[p_b.edge[0]] && p_vertices[p_a.edge[1]] == p_vertices[p_b.edge[1]]) { + if (p_vertices[p_a.edge[0]].is_equal_approx(p_vertices[p_b.edge[0]]) && p_vertices[p_a.edge[1]].is_equal_approx(p_vertices[p_b.edge[1]])) { return true; } - if (p_vertices[p_a.edge[0]] == p_vertices[p_b.edge[1]] && p_vertices[p_a.edge[1]] == p_vertices[p_b.edge[0]]) { + if (p_vertices[p_a.edge[0]].is_equal_approx(p_vertices[p_b.edge[1]]) && p_vertices[p_a.edge[1]].is_equal_approx(p_vertices[p_b.edge[0]])) { return true; } diff --git a/core/math/plane.cpp b/core/math/plane.cpp index b6bcac4b27..d55957cd0a 100644 --- a/core/math/plane.cpp +++ b/core/math/plane.cpp @@ -32,9 +32,6 @@ #include "core/math/math_funcs.h" -#define _PLANE_EQ_DOT_EPSILON 0.999 -#define _PLANE_EQ_D_EPSILON 0.0001 - void Plane::set_normal(const Vector3 &p_normal) { normal = p_normal; @@ -156,9 +153,9 @@ bool Plane::intersects_segment(const Vector3 &p_begin, const Vector3 &p_end, Vec /* misc */ -bool Plane::is_almost_like(const Plane &p_plane) const { +bool Plane::is_equal_approx(const Plane &p_plane) const { - return (normal.dot(p_plane.normal) > _PLANE_EQ_DOT_EPSILON && Math::absd(d - p_plane.d) < _PLANE_EQ_D_EPSILON); + return normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d); } Plane::operator String() const { diff --git a/core/math/plane.h b/core/math/plane.h index ec817edd2c..9abf24fbba 100644 --- a/core/math/plane.h +++ b/core/math/plane.h @@ -68,7 +68,7 @@ public: /* misc */ Plane operator-() const { return Plane(-normal, -d); } - bool is_almost_like(const Plane &p_plane) const; + bool is_equal_approx(const Plane &p_plane) const; _FORCE_INLINE_ bool operator==(const Plane &p_plane) const; _FORCE_INLINE_ bool operator!=(const Plane &p_plane) const; @@ -125,12 +125,12 @@ Plane::Plane(const Vector3 &p_point1, const Vector3 &p_point2, const Vector3 &p_ bool Plane::operator==(const Plane &p_plane) const { - return normal == p_plane.normal && Math::is_equal_approx(d, p_plane.d); + return normal == p_plane.normal && d == p_plane.d; } bool Plane::operator!=(const Plane &p_plane) const { - return normal != p_plane.normal || !Math::is_equal_approx(d, p_plane.d); + return normal != p_plane.normal || d != p_plane.d; } #endif // PLANE_H diff --git a/core/math/quat.cpp b/core/math/quat.cpp index 1a67be7384..a4f91844b9 100644 --- a/core/math/quat.cpp +++ b/core/math/quat.cpp @@ -121,6 +121,11 @@ Quat Quat::operator*(const Quat &q) const { return r; } +bool Quat::is_equal_approx(const Quat &p_quat) const { + + return Math::is_equal_approx(x, p_quat.x) && Math::is_equal_approx(y, p_quat.y) && Math::is_equal_approx(z, p_quat.z) && Math::is_equal_approx(w, p_quat.w); +} + real_t Quat::length() const { return Math::sqrt(length_squared()); diff --git a/core/math/quat.h b/core/math/quat.h index 3d6602e466..27885f4152 100644 --- a/core/math/quat.h +++ b/core/math/quat.h @@ -43,6 +43,7 @@ public: real_t x, y, z, w; _FORCE_INLINE_ real_t length_squared() const; + bool is_equal_approx(const Quat &p_quat) const; real_t length() const; void normalize(); Quat normalized() const; diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index fc2eb1454d..f71f00afd6 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -401,7 +401,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me ERR_CONTINUE(O == E); ERR_CONTINUE(O == NULL); - if (O->get().plane.is_almost_like(f.plane)) { + if (O->get().plane.is_equal_approx(f.plane)) { //merge and delete edge and contiguous face, while repointing edges (uuugh!) int ois = O->get().indices.size(); int merged = 0; diff --git a/core/math/rect2.cpp b/core/math/rect2.cpp index fea128afbd..e31776672e 100644 --- a/core/math/rect2.cpp +++ b/core/math/rect2.cpp @@ -30,6 +30,11 @@ #include "core/math/transform_2d.h" // Includes rect2.h but Rect2 needs Transform2D +bool Rect2::is_equal_approx(const Rect2 &p_rect) const { + + return position.is_equal_approx(p_rect.position) && size.is_equal_approx(p_rect.size); +} + bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const { real_t min = 0, max = 1; diff --git a/core/math/rect2.h b/core/math/rect2.h index f58756ee40..71221ffb1b 100644 --- a/core/math/rect2.h +++ b/core/math/rect2.h @@ -153,6 +153,7 @@ struct Rect2 { return true; } + bool is_equal_approx(const Rect2 &p_rect) const; bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; } bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; } diff --git a/core/math/transform.cpp b/core/math/transform.cpp index 4056975da8..5dcc6ab9f0 100644 --- a/core/math/transform.cpp +++ b/core/math/transform.cpp @@ -182,6 +182,11 @@ Transform Transform::orthonormalized() const { return _copy; } +bool Transform::is_equal_approx(const Transform &p_transform) const { + + return basis.is_equal_approx(p_transform.basis) && origin.is_equal_approx(p_transform.origin); +} + bool Transform::operator==(const Transform &p_transform) const { return (basis == p_transform.basis && origin == p_transform.origin); diff --git a/core/math/transform.h b/core/math/transform.h index 90e2b07583..da65a183cf 100644 --- a/core/math/transform.h +++ b/core/math/transform.h @@ -70,6 +70,7 @@ public: void orthonormalize(); Transform orthonormalized() const; + bool is_equal_approx(const Transform &p_transform) const; bool operator==(const Transform &p_transform) const; bool operator!=(const Transform &p_transform) const; diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index 1d0387bd45..a1c0814637 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -147,6 +147,7 @@ void Transform2D::orthonormalize() { elements[0] = x; elements[1] = y; } + Transform2D Transform2D::orthonormalized() const { Transform2D on = *this; @@ -154,6 +155,11 @@ Transform2D Transform2D::orthonormalized() const { return on; } +bool Transform2D::is_equal_approx(const Transform2D &p_transform) const { + + return elements[0].is_equal_approx(p_transform.elements[0]) && elements[1].is_equal_approx(p_transform.elements[1]) && elements[2].is_equal_approx(p_transform.elements[2]); +} + bool Transform2D::operator==(const Transform2D &p_transform) const { for (int i = 0; i < 3; i++) { diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index e8b44ab197..0ec39a1765 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -96,6 +96,7 @@ struct Transform2D { void orthonormalize(); Transform2D orthonormalized() const; + bool is_equal_approx(const Transform2D &p_transform) const; bool operator==(const Transform2D &p_transform) const; bool operator!=(const Transform2D &p_transform) const; diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp index 972bccc0ac..fbedeb8eb2 100644 --- a/core/math/vector2.cpp +++ b/core/math/vector2.cpp @@ -203,6 +203,10 @@ Vector2 Vector2::reflect(const Vector2 &p_normal) const { return 2.0 * p_normal * this->dot(p_normal) - *this; } +bool Vector2::is_equal_approx(const Vector2 &p_v) const { + return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y); +} + /* Vector2i */ Vector2i Vector2i::operator+(const Vector2i &p_v) const { diff --git a/core/math/vector2.h b/core/math/vector2.h index 1a73831891..7fcaadab00 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -92,6 +92,8 @@ struct Vector2 { Vector2 bounce(const Vector2 &p_normal) const; Vector2 reflect(const Vector2 &p_normal) const; + bool is_equal_approx(const Vector2 &p_v) const; + Vector2 operator+(const Vector2 &p_v) const; void operator+=(const Vector2 &p_v); Vector2 operator-(const Vector2 &p_v) const; @@ -221,11 +223,11 @@ _FORCE_INLINE_ Vector2 Vector2::operator-() const { _FORCE_INLINE_ bool Vector2::operator==(const Vector2 &p_vec2) const { - return Math::is_equal_approx(x, p_vec2.x) && Math::is_equal_approx(y, p_vec2.y); + return x == p_vec2.x && y == p_vec2.y; } _FORCE_INLINE_ bool Vector2::operator!=(const Vector2 &p_vec2) const { - return !Math::is_equal_approx(x, p_vec2.x) || !Math::is_equal_approx(y, p_vec2.y); + return x != p_vec2.x || y != p_vec2.y; } Vector2 Vector2::linear_interpolate(const Vector2 &p_b, real_t p_t) const { diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index ebc1599820..e3211c8fb1 100644 --- a/core/math/vector3.cpp +++ b/core/math/vector3.cpp @@ -149,6 +149,11 @@ Basis Vector3::to_diagonal_matrix() const { 0, 0, z); } +bool Vector3::is_equal_approx(const Vector3 &p_v) const { + + return Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y) && Math::is_equal_approx(z, p_v.z); +} + Vector3::operator String() const { return (rtos(x) + ", " + rtos(y) + ", " + rtos(z)); diff --git a/core/math/vector3.h b/core/math/vector3.h index de1743d88f..43fa09ffac 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -119,6 +119,8 @@ struct Vector3 { _FORCE_INLINE_ Vector3 bounce(const Vector3 &p_normal) const; _FORCE_INLINE_ Vector3 reflect(const Vector3 &p_normal) const; + bool is_equal_approx(const Vector3 &p_v) const; + /* Operators */ _FORCE_INLINE_ Vector3 &operator+=(const Vector3 &p_v); @@ -330,11 +332,12 @@ Vector3 Vector3::operator-() const { bool Vector3::operator==(const Vector3 &p_v) const { - return (Math::is_equal_approx(x, p_v.x) && Math::is_equal_approx(y, p_v.y) && Math::is_equal_approx(z, p_v.z)); + return x == p_v.x && y == p_v.y && z == p_v.z; } bool Vector3::operator!=(const Vector3 &p_v) const { - return (!Math::is_equal_approx(x, p_v.x) || !Math::is_equal_approx(y, p_v.y) || !Math::is_equal_approx(z, p_v.z)); + + return x != p_v.x || y != p_v.y || z != p_v.z; } bool Vector3::operator<(const Vector3 &p_v) const { diff --git a/core/variant_call.cpp b/core/variant_call.cpp index f7a3438d32..6e593a308d 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -351,12 +351,13 @@ struct _VariantCall { r_ret = retval; } - VCALL_LOCALMEM0R(Vector2, normalized); + VCALL_LOCALMEM1R(Vector2, distance_to); + VCALL_LOCALMEM1R(Vector2, distance_squared_to); VCALL_LOCALMEM0R(Vector2, length); VCALL_LOCALMEM0R(Vector2, length_squared); + VCALL_LOCALMEM0R(Vector2, normalized); VCALL_LOCALMEM0R(Vector2, is_normalized); - VCALL_LOCALMEM1R(Vector2, distance_to); - VCALL_LOCALMEM1R(Vector2, distance_squared_to); + VCALL_LOCALMEM1R(Vector2, is_equal_approx); VCALL_LOCALMEM1R(Vector2, posmod); VCALL_LOCALMEM1R(Vector2, posmodv); VCALL_LOCALMEM1R(Vector2, project); @@ -385,24 +386,28 @@ struct _VariantCall { VCALL_LOCALMEM0R(Vector2, sign); VCALL_LOCALMEM0R(Rect2, get_area); + VCALL_LOCALMEM0R(Rect2, has_no_area); + VCALL_LOCALMEM1R(Rect2, has_point); + VCALL_LOCALMEM1R(Rect2, is_equal_approx); VCALL_LOCALMEM1R(Rect2, intersects); VCALL_LOCALMEM1R(Rect2, encloses); - VCALL_LOCALMEM0R(Rect2, has_no_area); VCALL_LOCALMEM1R(Rect2, clip); VCALL_LOCALMEM1R(Rect2, merge); - VCALL_LOCALMEM1R(Rect2, has_point); + VCALL_LOCALMEM1R(Rect2, expand); VCALL_LOCALMEM1R(Rect2, grow); VCALL_LOCALMEM2R(Rect2, grow_margin); VCALL_LOCALMEM4R(Rect2, grow_individual); - VCALL_LOCALMEM1R(Rect2, expand); VCALL_LOCALMEM0R(Rect2, abs); VCALL_LOCALMEM0R(Vector3, min_axis); VCALL_LOCALMEM0R(Vector3, max_axis); + VCALL_LOCALMEM1R(Vector3, distance_to); + VCALL_LOCALMEM1R(Vector3, distance_squared_to); VCALL_LOCALMEM0R(Vector3, length); VCALL_LOCALMEM0R(Vector3, length_squared); - VCALL_LOCALMEM0R(Vector3, is_normalized); VCALL_LOCALMEM0R(Vector3, normalized); + VCALL_LOCALMEM0R(Vector3, is_normalized); + VCALL_LOCALMEM1R(Vector3, is_equal_approx); VCALL_LOCALMEM0R(Vector3, inverse); VCALL_LOCALMEM1R(Vector3, snapped); VCALL_LOCALMEM2R(Vector3, rotated); @@ -418,8 +423,6 @@ struct _VariantCall { VCALL_LOCALMEM0R(Vector3, floor); VCALL_LOCALMEM0R(Vector3, ceil); VCALL_LOCALMEM0R(Vector3, round); - VCALL_LOCALMEM1R(Vector3, distance_to); - VCALL_LOCALMEM1R(Vector3, distance_squared_to); VCALL_LOCALMEM1R(Vector3, posmod); VCALL_LOCALMEM1R(Vector3, posmodv); VCALL_LOCALMEM1R(Vector3, project); @@ -433,6 +436,7 @@ struct _VariantCall { VCALL_LOCALMEM0R(Plane, normalized); VCALL_LOCALMEM0R(Plane, center); VCALL_LOCALMEM0R(Plane, get_any_point); + VCALL_LOCALMEM1R(Plane, is_equal_approx); VCALL_LOCALMEM1R(Plane, is_point_over); VCALL_LOCALMEM1R(Plane, distance_to); VCALL_LOCALMEM2R(Plane, has_point); @@ -467,6 +471,7 @@ struct _VariantCall { VCALL_LOCALMEM0R(Quat, length_squared); VCALL_LOCALMEM0R(Quat, normalized); VCALL_LOCALMEM0R(Quat, is_normalized); + VCALL_LOCALMEM1R(Quat, is_equal_approx); VCALL_LOCALMEM0R(Quat, inverse); VCALL_LOCALMEM1R(Quat, dot); VCALL_LOCALMEM1R(Quat, xform); @@ -492,6 +497,7 @@ struct _VariantCall { VCALL_LOCALMEM1R(Color, darkened); VCALL_LOCALMEM1R(Color, to_html); VCALL_LOCALMEM4R(Color, from_hsv); + VCALL_LOCALMEM1R(Color, is_equal_approx); VCALL_LOCALMEM0R(RID, get_id); @@ -739,13 +745,16 @@ struct _VariantCall { VCALL_PTR0R(AABB, get_area); VCALL_PTR0R(AABB, has_no_area); VCALL_PTR0R(AABB, has_no_surface); + VCALL_PTR1R(AABB, has_point); + VCALL_PTR1R(AABB, is_equal_approx); VCALL_PTR1R(AABB, intersects); VCALL_PTR1R(AABB, encloses); - VCALL_PTR1R(AABB, merge); - VCALL_PTR1R(AABB, intersection); VCALL_PTR1R(AABB, intersects_plane); VCALL_PTR2R(AABB, intersects_segment); - VCALL_PTR1R(AABB, has_point); + VCALL_PTR1R(AABB, intersection); + VCALL_PTR1R(AABB, merge); + VCALL_PTR1R(AABB, expand); + VCALL_PTR1R(AABB, grow); VCALL_PTR1R(AABB, get_support); VCALL_PTR0R(AABB, get_longest_axis); VCALL_PTR0R(AABB, get_longest_axis_index); @@ -753,8 +762,6 @@ struct _VariantCall { VCALL_PTR0R(AABB, get_shortest_axis); VCALL_PTR0R(AABB, get_shortest_axis_index); VCALL_PTR0R(AABB, get_shortest_axis_size); - VCALL_PTR1R(AABB, expand); - VCALL_PTR1R(AABB, grow); VCALL_PTR1R(AABB, get_endpoint); VCALL_PTR0R(Transform2D, inverse); @@ -767,6 +774,7 @@ struct _VariantCall { VCALL_PTR1R(Transform2D, scaled); VCALL_PTR1R(Transform2D, translated); VCALL_PTR2R(Transform2D, interpolate_with); + VCALL_PTR1R(Transform2D, is_equal_approx); static void _call_Transform2D_xform(Variant &r_ret, Variant &p_self, const Variant **p_args) { @@ -823,7 +831,7 @@ struct _VariantCall { VCALL_PTR0R(Basis, get_orthogonal_index); VCALL_PTR0R(Basis, orthonormalized); VCALL_PTR2R(Basis, slerp); - VCALL_PTR2R(Basis, is_equal_approx); + VCALL_PTR2R(Basis, is_equal_approx); // TODO: Break compatibility in 4.0 to change this to an instance method (a.is_equal_approx(b) as VCALL_PTR1R) for consistency. VCALL_PTR0R(Basis, get_rotation_quat); VCALL_PTR0R(Transform, inverse); @@ -834,6 +842,7 @@ struct _VariantCall { VCALL_PTR0R(Transform, orthonormalized); VCALL_PTR2R(Transform, looking_at); VCALL_PTR2R(Transform, interpolate_with); + VCALL_PTR1R(Transform, is_equal_approx); static void _call_Transform_xform(Variant &r_ret, Variant &p_self, const Variant **p_args) { @@ -1614,19 +1623,20 @@ void register_variant_methods() { ADDFUNC0R(STRING, POOL_BYTE_ARRAY, String, to_ascii, varray()); ADDFUNC0R(STRING, POOL_BYTE_ARRAY, String, to_utf8, varray()); - ADDFUNC0R(VECTOR2, VECTOR2, Vector2, normalized, varray()); - ADDFUNC0R(VECTOR2, REAL, Vector2, length, varray()); ADDFUNC0R(VECTOR2, REAL, Vector2, angle, varray()); - ADDFUNC0R(VECTOR2, REAL, Vector2, length_squared, varray()); - ADDFUNC0R(VECTOR2, BOOL, Vector2, is_normalized, varray()); + ADDFUNC1R(VECTOR2, REAL, Vector2, angle_to, VECTOR2, "to", varray()); + ADDFUNC1R(VECTOR2, REAL, Vector2, angle_to_point, VECTOR2, "to", varray()); ADDFUNC1R(VECTOR2, VECTOR2, Vector2, direction_to, VECTOR2, "b", varray()); ADDFUNC1R(VECTOR2, REAL, Vector2, distance_to, VECTOR2, "to", varray()); ADDFUNC1R(VECTOR2, REAL, Vector2, distance_squared_to, VECTOR2, "to", varray()); + ADDFUNC0R(VECTOR2, REAL, Vector2, length, varray()); + ADDFUNC0R(VECTOR2, REAL, Vector2, length_squared, varray()); + ADDFUNC0R(VECTOR2, VECTOR2, Vector2, normalized, varray()); + ADDFUNC0R(VECTOR2, BOOL, Vector2, is_normalized, varray()); + ADDFUNC1R(VECTOR2, BOOL, Vector2, is_equal_approx, VECTOR2, "v", varray()); ADDFUNC1R(VECTOR2, VECTOR2, Vector2, posmod, REAL, "mod", varray()); ADDFUNC1R(VECTOR2, VECTOR2, Vector2, posmodv, VECTOR2, "modv", varray()); ADDFUNC1R(VECTOR2, VECTOR2, Vector2, project, VECTOR2, "b", varray()); - ADDFUNC1R(VECTOR2, REAL, Vector2, angle_to, VECTOR2, "to", varray()); - ADDFUNC1R(VECTOR2, REAL, Vector2, angle_to_point, VECTOR2, "to", varray()); ADDFUNC2R(VECTOR2, VECTOR2, Vector2, linear_interpolate, VECTOR2, "b", REAL, "t", varray()); ADDFUNC2R(VECTOR2, VECTOR2, Vector2, slerp, VECTOR2, "b", REAL, "t", varray()); ADDFUNC4R(VECTOR2, VECTOR2, Vector2, cubic_interpolate, VECTOR2, "b", VECTOR2, "pre_a", VECTOR2, "post_b", REAL, "t", varray()); @@ -1648,31 +1658,36 @@ void register_variant_methods() { ADDFUNC0R(VECTOR2, VECTOR2, Vector2, sign, varray()); ADDFUNC0R(RECT2, REAL, Rect2, get_area, varray()); + ADDFUNC0R(RECT2, BOOL, Rect2, has_no_area, varray()); + ADDFUNC1R(RECT2, BOOL, Rect2, has_point, VECTOR2, "point", varray()); + ADDFUNC1R(RECT2, BOOL, Rect2, is_equal_approx, RECT2, "rect", varray()); ADDFUNC1R(RECT2, BOOL, Rect2, intersects, RECT2, "b", varray()); ADDFUNC1R(RECT2, BOOL, Rect2, encloses, RECT2, "b", varray()); - ADDFUNC0R(RECT2, BOOL, Rect2, has_no_area, varray()); ADDFUNC1R(RECT2, RECT2, Rect2, clip, RECT2, "b", varray()); ADDFUNC1R(RECT2, RECT2, Rect2, merge, RECT2, "b", varray()); - ADDFUNC1R(RECT2, BOOL, Rect2, has_point, VECTOR2, "point", varray()); + ADDFUNC1R(RECT2, RECT2, Rect2, expand, VECTOR2, "to", varray()); ADDFUNC1R(RECT2, RECT2, Rect2, grow, REAL, "by", varray()); ADDFUNC2R(RECT2, RECT2, Rect2, grow_margin, INT, "margin", REAL, "by", varray()); ADDFUNC4R(RECT2, RECT2, Rect2, grow_individual, REAL, "left", REAL, "top", REAL, "right", REAL, " bottom", varray()); - ADDFUNC1R(RECT2, RECT2, Rect2, expand, VECTOR2, "to", varray()); ADDFUNC0R(RECT2, RECT2, Rect2, abs, varray()); ADDFUNC0R(VECTOR3, INT, Vector3, min_axis, varray()); ADDFUNC0R(VECTOR3, INT, Vector3, max_axis, varray()); + ADDFUNC1R(VECTOR3, REAL, Vector3, angle_to, VECTOR3, "to", varray()); + ADDFUNC1R(VECTOR3, VECTOR3, Vector3, direction_to, VECTOR3, "b", varray()); + ADDFUNC1R(VECTOR3, REAL, Vector3, distance_to, VECTOR3, "b", varray()); + ADDFUNC1R(VECTOR3, REAL, Vector3, distance_squared_to, VECTOR3, "b", varray()); ADDFUNC0R(VECTOR3, REAL, Vector3, length, varray()); ADDFUNC0R(VECTOR3, REAL, Vector3, length_squared, varray()); - ADDFUNC0R(VECTOR3, BOOL, Vector3, is_normalized, varray()); ADDFUNC0R(VECTOR3, VECTOR3, Vector3, normalized, varray()); + ADDFUNC0R(VECTOR3, BOOL, Vector3, is_normalized, varray()); + ADDFUNC1R(VECTOR3, BOOL, Vector3, is_equal_approx, VECTOR3, "v", varray()); ADDFUNC0R(VECTOR3, VECTOR3, Vector3, inverse, varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, snapped, VECTOR3, "by", varray()); ADDFUNC2R(VECTOR3, VECTOR3, Vector3, rotated, VECTOR3, "axis", REAL, "phi", varray()); ADDFUNC2R(VECTOR3, VECTOR3, Vector3, linear_interpolate, VECTOR3, "b", REAL, "t", varray()); ADDFUNC2R(VECTOR3, VECTOR3, Vector3, slerp, VECTOR3, "b", REAL, "t", varray()); ADDFUNC4R(VECTOR3, VECTOR3, Vector3, cubic_interpolate, VECTOR3, "b", VECTOR3, "pre_a", VECTOR3, "post_b", REAL, "t", varray()); - ADDFUNC1R(VECTOR3, VECTOR3, Vector3, direction_to, VECTOR3, "b", varray()); ADDFUNC2R(VECTOR3, VECTOR3, Vector3, move_toward, VECTOR3, "to", REAL, "delta", varray()); ADDFUNC1R(VECTOR3, REAL, Vector3, dot, VECTOR3, "b", varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, cross, VECTOR3, "b", varray()); @@ -1682,12 +1697,9 @@ void register_variant_methods() { ADDFUNC0R(VECTOR3, VECTOR3, Vector3, floor, varray()); ADDFUNC0R(VECTOR3, VECTOR3, Vector3, ceil, varray()); ADDFUNC0R(VECTOR3, VECTOR3, Vector3, round, varray()); - ADDFUNC1R(VECTOR3, REAL, Vector3, distance_to, VECTOR3, "b", varray()); - ADDFUNC1R(VECTOR3, REAL, Vector3, distance_squared_to, VECTOR3, "b", varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, posmod, REAL, "mod", varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, posmodv, VECTOR3, "modv", varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, project, VECTOR3, "b", varray()); - ADDFUNC1R(VECTOR3, REAL, Vector3, angle_to, VECTOR3, "to", varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, slide, VECTOR3, "n", varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, bounce, VECTOR3, "n", varray()); ADDFUNC1R(VECTOR3, VECTOR3, Vector3, reflect, VECTOR3, "n", varray()); @@ -1696,6 +1708,7 @@ void register_variant_methods() { ADDFUNC0R(PLANE, PLANE, Plane, normalized, varray()); ADDFUNC0R(PLANE, VECTOR3, Plane, center, varray()); ADDFUNC0R(PLANE, VECTOR3, Plane, get_any_point, varray()); + ADDFUNC1R(PLANE, BOOL, Plane, is_equal_approx, PLANE, "plane", varray()); ADDFUNC1R(PLANE, BOOL, Plane, is_point_over, VECTOR3, "point", varray()); ADDFUNC1R(PLANE, REAL, Plane, distance_to, VECTOR3, "point", varray()); ADDFUNC2R(PLANE, BOOL, Plane, has_point, VECTOR3, "point", REAL, "epsilon", varray(CMP_EPSILON)); @@ -1708,6 +1721,7 @@ void register_variant_methods() { ADDFUNC0R(QUAT, REAL, Quat, length_squared, varray()); ADDFUNC0R(QUAT, QUAT, Quat, normalized, varray()); ADDFUNC0R(QUAT, BOOL, Quat, is_normalized, varray()); + ADDFUNC1R(QUAT, BOOL, Quat, is_equal_approx, QUAT, "quat", varray()); ADDFUNC0R(QUAT, QUAT, Quat, inverse, varray()); ADDFUNC1R(QUAT, REAL, Quat, dot, QUAT, "b", varray()); ADDFUNC1R(QUAT, VECTOR3, Quat, xform, VECTOR3, "v", varray()); @@ -1733,6 +1747,7 @@ void register_variant_methods() { ADDFUNC1R(COLOR, COLOR, Color, darkened, REAL, "amount", varray()); ADDFUNC1R(COLOR, STRING, Color, to_html, BOOL, "with_alpha", varray(true)); ADDFUNC4R(COLOR, COLOR, Color, from_hsv, REAL, "h", REAL, "s", REAL, "v", REAL, "a", varray(1.0)); + ADDFUNC1R(COLOR, BOOL, Color, is_equal_approx, COLOR, "color", varray()); ADDFUNC0R(_RID, INT, RID, get_id, varray()); @@ -1878,13 +1893,16 @@ void register_variant_methods() { ADDFUNC0R(AABB, REAL, AABB, get_area, varray()); ADDFUNC0R(AABB, BOOL, AABB, has_no_area, varray()); ADDFUNC0R(AABB, BOOL, AABB, has_no_surface, varray()); + ADDFUNC1R(AABB, BOOL, AABB, has_point, VECTOR3, "point", varray()); + ADDFUNC1R(AABB, BOOL, AABB, is_equal_approx, AABB, "aabb", varray()); ADDFUNC1R(AABB, BOOL, AABB, intersects, AABB, "with", varray()); ADDFUNC1R(AABB, BOOL, AABB, encloses, AABB, "with", varray()); - ADDFUNC1R(AABB, AABB, AABB, merge, AABB, "with", varray()); - ADDFUNC1R(AABB, AABB, AABB, intersection, AABB, "with", varray()); ADDFUNC1R(AABB, BOOL, AABB, intersects_plane, PLANE, "plane", varray()); ADDFUNC2R(AABB, BOOL, AABB, intersects_segment, VECTOR3, "from", VECTOR3, "to", varray()); - ADDFUNC1R(AABB, BOOL, AABB, has_point, VECTOR3, "point", varray()); + ADDFUNC1R(AABB, AABB, AABB, intersection, AABB, "with", varray()); + ADDFUNC1R(AABB, AABB, AABB, merge, AABB, "with", varray()); + ADDFUNC1R(AABB, AABB, AABB, expand, VECTOR3, "to_point", varray()); + ADDFUNC1R(AABB, AABB, AABB, grow, REAL, "by", varray()); ADDFUNC1R(AABB, VECTOR3, AABB, get_support, VECTOR3, "dir", varray()); ADDFUNC0R(AABB, VECTOR3, AABB, get_longest_axis, varray()); ADDFUNC0R(AABB, INT, AABB, get_longest_axis_index, varray()); @@ -1892,8 +1910,6 @@ void register_variant_methods() { ADDFUNC0R(AABB, VECTOR3, AABB, get_shortest_axis, varray()); ADDFUNC0R(AABB, INT, AABB, get_shortest_axis_index, varray()); ADDFUNC0R(AABB, REAL, AABB, get_shortest_axis_size, varray()); - ADDFUNC1R(AABB, AABB, AABB, expand, VECTOR3, "to_point", varray()); - ADDFUNC1R(AABB, AABB, AABB, grow, REAL, "by", varray()); ADDFUNC1R(AABB, VECTOR3, AABB, get_endpoint, INT, "idx", varray()); ADDFUNC0R(TRANSFORM2D, TRANSFORM2D, Transform2D, inverse, varray()); @@ -1910,6 +1926,7 @@ void register_variant_methods() { ADDFUNC1R(TRANSFORM2D, VECTOR2, Transform2D, basis_xform, VECTOR2, "v", varray()); ADDFUNC1R(TRANSFORM2D, VECTOR2, Transform2D, basis_xform_inv, VECTOR2, "v", varray()); ADDFUNC2R(TRANSFORM2D, TRANSFORM2D, Transform2D, interpolate_with, TRANSFORM2D, "transform", REAL, "weight", varray()); + ADDFUNC1R(TRANSFORM2D, BOOL, Transform2D, is_equal_approx, TRANSFORM2D, "transform", varray()); ADDFUNC0R(BASIS, BASIS, Basis, inverse, varray()); ADDFUNC0R(BASIS, BASIS, Basis, transposed, varray()); @@ -1926,7 +1943,7 @@ void register_variant_methods() { ADDFUNC1R(BASIS, VECTOR3, Basis, xform_inv, VECTOR3, "v", varray()); ADDFUNC0R(BASIS, INT, Basis, get_orthogonal_index, varray()); ADDFUNC2R(BASIS, BASIS, Basis, slerp, BASIS, "b", REAL, "t", varray()); - ADDFUNC2R(BASIS, BOOL, Basis, is_equal_approx, BASIS, "b", REAL, "epsilon", varray(CMP_EPSILON)); + ADDFUNC2R(BASIS, BOOL, Basis, is_equal_approx, BASIS, "b", REAL, "epsilon", varray(CMP_EPSILON)); // TODO: Replace in 4.0, see other TODO. ADDFUNC0R(BASIS, QUAT, Basis, get_rotation_quat, varray()); ADDFUNC0R(TRANSFORM, TRANSFORM, Transform, inverse, varray()); @@ -1937,6 +1954,7 @@ void register_variant_methods() { ADDFUNC1R(TRANSFORM, TRANSFORM, Transform, translated, VECTOR3, "ofs", varray()); ADDFUNC2R(TRANSFORM, TRANSFORM, Transform, looking_at, VECTOR3, "target", VECTOR3, "up", varray()); ADDFUNC2R(TRANSFORM, TRANSFORM, Transform, interpolate_with, TRANSFORM, "transform", REAL, "weight", varray()); + ADDFUNC1R(TRANSFORM, BOOL, Transform, is_equal_approx, TRANSFORM, "transform", varray()); ADDFUNC1R(TRANSFORM, NIL, Transform, xform, NIL, "v", varray()); ADDFUNC1R(TRANSFORM, NIL, Transform, xform_inv, NIL, "v", varray()); diff --git a/doc/classes/AStar.xml b/doc/classes/AStar.xml index e835af01e8..6304bd34f6 100644 --- a/doc/classes/AStar.xml +++ b/doc/classes/AStar.xml @@ -1,11 +1,23 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="AStar" inherits="Reference" category="Core" version="3.2"> <brief_description> - AStar class representation that uses 3d-vectors as edges. + An implementation of A* to find shortest paths among connected points in space. </brief_description> <description> - A* (A star) is a computer algorithm that is widely used in pathfinding and graph traversal, the process of plotting an efficiently directed path between multiple points. It enjoys widespread use due to its performance and accuracy. Godot's A* implementation make use of vectors as points. - You must add points manually with [method add_point] and create segments manually with [method connect_points]. So you can test if there is a path between two points with the [method are_points_connected] function, get the list of existing ids in the found path with [method get_id_path], or the points list with [method get_point_path]. + A* (A star) is a computer algorithm that is widely used in pathfinding and graph traversal, the process of plotting short paths among vertices (points), passing through a given set of edges (segments). It enjoys widespread use due to its performance and accuracy. Godot's A* implementation uses points in three-dimensional space and Euclidean distances by default. + You must add points manually with [method add_point] and create segments manually with [method connect_points]. Then you can test if there is a path between two points with the [method are_points_connected] function, get a path containing indices by [method get_id_path], or one containing actual coordinates with [method get_point_path]. + It is also possible to use non-Euclidean distances. To do so, create a class that extends [code]AStar[/code] and override methods [method _compute_cost] and [method _estimate_cost]. Both take two indices and return a length, as is shown in the following example. + [codeblock] + class MyAStar: + extends AStar + + func _compute_cost(u, v): + return abs(u - v) + + func _estimate_cost(u, v): + return min(0, abs(u - v) - 1) + [/codeblock] + [method _estimate_cost] should return a lower bound of the distance, i.e. [code]_estimate_cost(u, v) <= _compute_cost(u, v)[/code]. This serves as a hint to the algorithm because the custom [code]_compute_cost[/code] might be computation-heavy. If this is not the case, make [method _estimate_cost] return the same value as [method _compute_cost] to provide the algorithm with the most accurate information. </description> <tutorials> </tutorials> @@ -19,6 +31,7 @@ </argument> <description> Called when computing the cost between two connected points. + Note that this function is hidden in the default [code]AStar[/code] class. </description> </method> <method name="_estimate_cost" qualifiers="virtual"> @@ -30,6 +43,7 @@ </argument> <description> Called when estimating the cost between a point and the path's ending point. + Note that this function is hidden in the default [code]AStar[/code] class. </description> </method> <method name="add_point"> @@ -57,8 +71,10 @@ </argument> <argument index="1" name="to_id" type="int"> </argument> + <argument index="2" name="bidirectional" type="bool" default="true"> + </argument> <description> - Returns whether there is a connection/segment between the given points. + Returns whether the two given points are directly connected by a segment. If [code]bidirectional[/code] is [code]false[/code], returns whether movement from [code]id[/code] to [code]to_id[/code] is possible through this segment. </description> </method> <method name="clear"> @@ -94,8 +110,10 @@ </argument> <argument index="1" name="to_id" type="int"> </argument> + <argument index="2" name="bidirectional" type="bool" default="true"> + </argument> <description> - Deletes the segment between the given points. + Deletes the segment between the given points. If [code]bidirectional[/code] is [code]false[/code], only movement from [code]id[/code] to [code]to_id[/code] is prevented, and a unidirectional segment possibly remains. </description> </method> <method name="get_available_point_id" qualifiers="const"> diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index f5a8683a39..75d5a72fb1 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -652,7 +652,18 @@ <argument index="0" name="control" type="Control"> </argument> <description> - Shows the given control at the mouse pointer. A good time to call this method is in [method get_drag_data]. + Shows the given control at the mouse pointer. A good time to call this method is in [method get_drag_data]. The control must not be in the scene tree. + [codeblock] + export (Color, RGBA) var color = Color(1, 0, 0, 1) + + func get_drag_data(position): + # Use a control that is not in the tree + var cpb = ColorPickerButton.new() + cpb.color = color + cpb.rect_size = Vector2(50, 50) + set_drag_preview(cpb) + return color + [/codeblock] </description> </method> <method name="set_end"> diff --git a/doc/classes/RayCast.xml b/doc/classes/RayCast.xml index 19f62a57bd..5e17d6e7d7 100644 --- a/doc/classes/RayCast.xml +++ b/doc/classes/RayCast.xml @@ -11,6 +11,7 @@ RayCast calculates intersection every physics frame (see [Node]), and the result is cached so it can be used later until the next frame. If multiple queries are required between physics frames (or during the same frame), use [method force_raycast_update] after adjusting the raycast. </description> <tutorials> + <link>https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link> </tutorials> <methods> <method name="add_exception"> diff --git a/doc/classes/RayCast2D.xml b/doc/classes/RayCast2D.xml index 81d66ef496..c5ba5da24e 100644 --- a/doc/classes/RayCast2D.xml +++ b/doc/classes/RayCast2D.xml @@ -11,6 +11,7 @@ RayCast2D calculates intersection every physics frame (see [Node]), and the result is cached so it can be used later until the next frame. If multiple queries are required between physics frames (or during the same frame) use [method force_raycast_update] after adjusting the raycast. </description> <tutorials> + <link>https://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html</link> </tutorials> <methods> <method name="add_exception"> diff --git a/doc/classes/String.xml b/doc/classes/String.xml index cf152e716e..e0a4a24299 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -459,7 +459,7 @@ <argument index="1" name="what" type="String"> </argument> <description> - Inserts a substring at a given position. + Returns a copy of the string with the substring [code]what[/code] inserted at the given position. </description> </method> <method name="is_abs_path"> diff --git a/doc/classes/Variant.xml b/doc/classes/Variant.xml index 522e131b45..28c3bc8e85 100644 --- a/doc/classes/Variant.xml +++ b/doc/classes/Variant.xml @@ -4,7 +4,37 @@ The most important data type in Godot. </brief_description> <description> + In computer programming, a Variant class is a class that is designed to store a variety of other types. Dynamic programming languages like PHP, Lua, JavaScript and GDScript like to use them to store variables' data on the backend. With these Variants, properties are able to change value types freely. + [codeblock] + var foo = 2 # foo is dynamically an integer + foo = "Now foo is a string!" + foo = Reference.new() # foo is an Object + var bar: int = 2 # bar is a statically typed integer. + # bar = "Uh oh! I can't make static variables become a different type!" + [/codeblock] + Godot tracks all scripting API variables within Variants. Without even realizing it, you use Variants all the time. When a particular language enforces its own rules for keeping data typed, then that language is applying its own custom logic over the base Variant scripting API. + - GDScript automatically wrap values in them. It keeps all data in plain Variants by default and then optionally enforces custom static typing rules on variable types. + - VisualScript tracks properties inside Variants as well, but it also uses static typing. The GUI interface enforces that properties have a particular type that doesn't change over time. + - C# is statically typed, but uses the Mono [code]object[/code] type in place of Godot's Variant class when it needs to represent a dynamic value. [code]object[/code] is the Mono runtime's equivalent of the same concept. + - The statically-typed language NativeScript C++ does not define a built-in Variant-like class. Godot's GDNative bindings provide their own godot::Variant class for users; Any point at which the C++ code starts interacting with the Godot runtime is a place where you might have to start wrapping data inside Variant objects. + The global [member @GDScript.typeof] function returns the enumerated value of the Variant type stored in the current variable. These correspond to [code]TYPE_*[/code] constants in the [@GlobalScope] docs. + [codeblock] + var foo = 2 + match typeof(foo): + TYPE_NIL: + print("foo is null!") + TYPE_INTEGER: + print("foo is an integer!") + TYPE_OBJECT: + # Note that Objects are their own special category. + # To get the name of the underlying Object type, you need the `get_class()` method. + print("foo is a(n) %s" % foo.get_class()) # inject the class name into a formatted string. + # Note also that there is not yet any way to get a script's `class_name` string easily. + # To fetch that value, you need to dig deeply into a hidden ProjectSettings setting: an Array of Dictionaries called "_global_script_classes". + # Open your project.godot file to see it up close. + [/codeblock] A Variant takes up only 20 bytes and can store almost any engine datatype inside of it. Variants are rarely used to hold information for long periods of time. Instead, they are used mainly for communication, editing, serialization and moving data around. + Godot has specifically invested in making its Variant class as flexible as possible; so much so that it is used for a multitude of operations to facilitate communication between all of Godot's systems. A Variant: - Can store almost any datatype. - Can perform operations between many variants. GDScript uses Variant as its atomic/native datatype. @@ -16,10 +46,11 @@ - Can be serialized to text and use it for printing values and editable settings. - Can work as an exported property, so the editor can edit it universally. - Can be used for dictionaries, arrays, parsers, etc. - [b]Containers ([Array] and [Dictionary]):[/b] Both are implemented using variants. A [Dictionary] can match any datatype used as key to any other datatype. An [Array] just holds an array of Variants. Of course, a Variant can also hold a [Dictionary] and an [Array] inside, making it even more flexible. + [b]Containers (Array and Dictionary):[/b] Both are implemented using variants. A [Dictionary] can match any datatype used as key to any other datatype. An [Array] just holds an array of Variants. Of course, a Variant can also hold a [Dictionary] and an [Array] inside, making it even more flexible. Modifications to a container will modify all references to it. A [Mutex] should be created to lock it if multi-threaded access is desired. </description> <tutorials> + <link>https://docs.godotengine.org/en/latest/development/cpp/variant_class.html</link> </tutorials> <methods> </methods> diff --git a/drivers/gles2/rasterizer_canvas_gles2.cpp b/drivers/gles2/rasterizer_canvas_gles2.cpp index 16e5e92abd..e34705f7b7 100644 --- a/drivers/gles2/rasterizer_canvas_gles2.cpp +++ b/drivers/gles2/rasterizer_canvas_gles2.cpp @@ -494,10 +494,8 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur if (line->width <= 1) { Vector2 verts[2] = { - // Offset the line slightly to make sure we always draw the pixel at the from coordinate. - // Without this, corners of rectangles might be missing a pixel. (See diamond exit rule and #32657) - Vector2(Math::floor(line->from.x) + 0.5, Math::floor(line->from.y) + 0.5), - Vector2(Math::floor(line->to.x) + 0.5, Math::floor(line->to.y) + 0.5) + Vector2(line->from.x, line->from.y), + Vector2(line->to.x, line->to.y) }; #ifdef GLES_OVER_GL diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index e09ba755ea..edffe852a2 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -548,10 +548,8 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur if (line->width <= 1) { Vector2 verts[2] = { - // Offset the line slightly to make sure we always draw the pixel at the from coordinate. - // Without this, corners of rectangles might be missing a pixel. (See diamond exit rule and #32657) - Vector2(Math::floor(line->from.x) + 0.5, Math::floor(line->from.y) + 0.5), - Vector2(Math::floor(line->to.x) + 0.5, Math::floor(line->to.y) + 0.5) + Vector2(line->from.x, line->from.y), + Vector2(line->to.x, line->to.y) }; #ifdef GLES_OVER_GL diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index 5c0adba622..4855d3f69d 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -1146,6 +1146,16 @@ List<Node *> &EditorSelection::get_selected_node_list() { return selected_node_list; } +List<Node *> EditorSelection::get_full_selected_node_list() { + + List<Node *> node_list; + for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) { + node_list.push_back(E->key()); + } + + return node_list; +} + void EditorSelection::clear() { while (!selection.empty()) { diff --git a/editor/editor_data.h b/editor/editor_data.h index f9a6587d8d..aa3e84d5a4 100644 --- a/editor/editor_data.h +++ b/editor/editor_data.h @@ -275,6 +275,7 @@ public: void clear(); List<Node *> &get_selected_node_list(); + List<Node *> get_full_selected_node_list(); Map<Node *, Object *> &get_selection() { return selection; } EditorSelection(); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index d3d91e6e0d..e14beabfa2 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -1884,6 +1884,23 @@ void EditorPropertyColor::_bind_methods() { void EditorPropertyColor::update_property() { picker->set_pick_color(get_edited_object()->get(get_edited_property())); + const Color color = picker->get_pick_color(); + + // Add a tooltip to display each channel's values without having to click the ColorPickerButton + if (picker->is_editing_alpha()) { + picker->set_tooltip(vformat( + "R: %s\nG: %s\nB: %s\nA: %s", + rtos(color.r).pad_decimals(2), + rtos(color.g).pad_decimals(2), + rtos(color.b).pad_decimals(2), + rtos(color.a).pad_decimals(2))); + } else { + picker->set_tooltip(vformat( + "R: %s\nG: %s\nB: %s", + rtos(color.r).pad_decimals(2), + rtos(color.g).pad_decimals(2), + rtos(color.b).pad_decimals(2))); + } } void EditorPropertyColor::setup(bool p_show_alpha) { diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index fa171ddb0c..5409ef65ea 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -71,11 +71,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory subdirectory_item->select(0); } - if ((path.begins_with(lpath) && path != lpath)) { - subdirectory_item->set_collapsed(false); - } else { - subdirectory_item->set_collapsed(uncollapsed_paths.find(lpath) < 0); - } + subdirectory_item->set_collapsed(uncollapsed_paths.find(lpath) < 0); if (searched_string.length() > 0 && dname.to_lower().find(searched_string) >= 0) { parent_should_expand = true; } diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index ca5858b768..9ac775e456 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -1530,28 +1530,33 @@ void ProjectSettingsEditor::_update_translations() { Array l_filter = l_filter_all[1]; int s = names.size(); - if (!translation_locales_list_created) { + bool is_short_list_when_show_all_selected = filter_mode == SHOW_ALL_LOCALES && translation_filter_treeitems.size() < s; + bool is_full_list_when_show_only_selected = filter_mode == SHOW_ONLY_SELECTED_LOCALES && translation_filter_treeitems.size() == s; + bool should_recreate_locales_list = is_short_list_when_show_all_selected || is_full_list_when_show_only_selected; + + if (!translation_locales_list_created || should_recreate_locales_list) { translation_locales_list_created = true; translation_filter->clear(); root = translation_filter->create_item(NULL); translation_filter->set_hide_root(true); - translation_filter_treeitems.resize(s); - + translation_filter_treeitems.clear(); for (int i = 0; i < s; i++) { String n = names[i]; String l = langs[i]; + bool is_checked = l_filter.has(l); + if (filter_mode == SHOW_ONLY_SELECTED_LOCALES && !is_checked) continue; + TreeItem *t = translation_filter->create_item(root); t->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); t->set_text(0, n); t->set_editable(0, true); t->set_tooltip(0, l); - t->set_checked(0, l_filter.has(l)); - translation_filter_treeitems.write[i] = t; + t->set_checked(0, is_checked); + translation_filter_treeitems.push_back(t); } } else { - for (int i = 0; i < s; i++) { - + for (int i = 0; i < translation_filter_treeitems.size(); i++) { TreeItem *t = translation_filter_treeitems[i]; t->set_checked(0, l_filter.has(t->get_tooltip(0))); } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index beead9e7f1..71e93750f0 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -2403,6 +2403,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { } List<Node *> selection = editor_selection->get_selected_node_list(); + List<Node *> full_selection = editor_selection->get_full_selected_node_list(); // Above method only returns nodes with common parent. if (selection.size() == 0) return; @@ -2437,21 +2438,40 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { } if (profile_allow_script_editing) { + bool add_separator = false; - if (selection.size() == 1) { + if (full_selection.size() == 1) { + add_separator = true; menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); if (existing_script.is_valid()) { menu->add_icon_shortcut(get_icon("ScriptExtend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_EXTEND_SCRIPT); } } - if (selection.size() > 1 || (existing_script.is_valid() && exisiting_script_removable)) { + if (existing_script.is_valid() && exisiting_script_removable) { + add_separator = true; menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT); + } else if (full_selection.size() > 1) { + bool script_exists = false; + for (List<Node *>::Element *E = full_selection.front(); E; E = E->next()) { + if (!E->get()->get_script().is_null()) { + script_exists = true; + break; + } + } + + if (script_exists) { + add_separator = true; + menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT); + } + } + + if (add_separator) { + menu->add_separator(); } - menu->add_separator(); } if (profile_allow_editing) { - if (selection.size() == 1) { + if (full_selection.size() == 1) { menu->add_icon_shortcut(get_icon("Rename", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/rename"), TOOL_RENAME); } menu->add_icon_shortcut(get_icon("Reload", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/change_node_type"), TOOL_REPLACE); @@ -2463,7 +2483,9 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->add_icon_shortcut(get_icon("Duplicate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/duplicate"), TOOL_DUPLICATE); menu->add_icon_shortcut(get_icon("Reparent", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/reparent"), TOOL_REPARENT); menu->add_icon_shortcut(get_icon("ReparentToNewNode", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/reparent_to_new_node"), TOOL_REPARENT_TO_NEW_NODE); - menu->add_icon_shortcut(get_icon("NewRoot", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/make_root"), TOOL_MAKE_ROOT); + if (selection.size() == 1) { + menu->add_icon_shortcut(get_icon("NewRoot", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/make_root"), TOOL_MAKE_ROOT); + } } } if (selection.size() == 1) { @@ -2472,9 +2494,11 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { menu->add_separator(); menu->add_icon_shortcut(get_icon("Blend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/merge_from_scene"), TOOL_MERGE_FROM_SCENE); menu->add_icon_shortcut(get_icon("CreateNewSceneFrom", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/save_branch_as_scene"), TOOL_NEW_SCENE_FROM); + } + if (full_selection.size() == 1) { menu->add_separator(); + menu->add_icon_shortcut(get_icon("CopyNodePath", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/copy_node_path"), TOOL_COPY_NODE_PATH); } - menu->add_icon_shortcut(get_icon("CopyNodePath", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/copy_node_path"), TOOL_COPY_NODE_PATH); bool is_external = (selection[0]->get_filename() != ""); if (is_external) { diff --git a/main/splash_editor.png b/main/splash_editor.png Binary files differindex 2967d8dd3e..8c605f0eed 100644 --- a/main/splash_editor.png +++ b/main/splash_editor.png diff --git a/main/tests/test_astar.cpp b/main/tests/test_astar.cpp index d34ff0d95e..4b60a3e94a 100644 --- a/main/tests/test_astar.cpp +++ b/main/tests/test_astar.cpp @@ -31,8 +31,10 @@ #include "test_astar.h" #include "core/math/a_star.h" +#include "core/math/math_funcs.h" #include "core/os/os.h" +#include <math.h> #include <stdio.h> namespace TestAStar { @@ -87,11 +89,268 @@ bool test_abcx() { return ok; } +bool test_add_remove() { + AStar a; + bool ok = true; + + // Manual tests + a.add_point(1, Vector3(0, 0, 0)); + a.add_point(2, Vector3(0, 1, 0)); + a.add_point(3, Vector3(1, 1, 0)); + a.add_point(4, Vector3(2, 0, 0)); + a.connect_points(1, 2, true); + a.connect_points(1, 3, true); + a.connect_points(1, 4, false); + + ok = ok && (a.are_points_connected(2, 1) == true); + ok = ok && (a.are_points_connected(4, 1) == true); + ok = ok && (a.are_points_connected(2, 1, false) == true); + ok = ok && (a.are_points_connected(4, 1, false) == false); + + a.disconnect_points(1, 2, true); + ok = ok && (a.get_point_connections(1).size() == 2); // 3, 4 + ok = ok && (a.get_point_connections(2).size() == 0); + + a.disconnect_points(4, 1, false); + ok = ok && (a.get_point_connections(1).size() == 2); // 3, 4 + ok = ok && (a.get_point_connections(4).size() == 0); + + a.disconnect_points(4, 1, true); + ok = ok && (a.get_point_connections(1).size() == 1); // 3 + ok = ok && (a.get_point_connections(4).size() == 0); + + a.connect_points(2, 3, false); + ok = ok && (a.get_point_connections(2).size() == 1); // 3 + ok = ok && (a.get_point_connections(3).size() == 1); // 1 + + a.connect_points(2, 3, true); + ok = ok && (a.get_point_connections(2).size() == 1); // 3 + ok = ok && (a.get_point_connections(3).size() == 2); // 1, 2 + + a.disconnect_points(2, 3, false); + ok = ok && (a.get_point_connections(2).size() == 0); + ok = ok && (a.get_point_connections(3).size() == 2); // 1, 2 + + a.connect_points(4, 3, true); + ok = ok && (a.get_point_connections(3).size() == 3); // 1, 2, 4 + ok = ok && (a.get_point_connections(4).size() == 1); // 3 + + a.disconnect_points(3, 4, false); + ok = ok && (a.get_point_connections(3).size() == 2); // 1, 2 + ok = ok && (a.get_point_connections(4).size() == 1); // 3 + + a.remove_point(3); + ok = ok && (a.get_point_connections(1).size() == 0); + ok = ok && (a.get_point_connections(2).size() == 0); + ok = ok && (a.get_point_connections(4).size() == 0); + + a.add_point(0, Vector3(0, -1, 0)); + a.add_point(3, Vector3(2, 1, 0)); + // 0: (0, -1) + // 1: (0, 0) + // 2: (0, 1) + // 3: (2, 1) + // 4: (2, 0) + + // Tests for get_closest_position_in_segment + a.connect_points(2, 3); + ok = ok && (a.get_closest_position_in_segment(Vector3(0.5, 0.5, 0)) == Vector3(0.5, 1, 0)); + + a.connect_points(3, 4); + a.connect_points(0, 3); + a.connect_points(1, 4); + a.disconnect_points(1, 4, false); + a.disconnect_points(4, 3, false); + a.disconnect_points(3, 4, false); + // Remaining edges: <2, 3>, <0, 3>, <1, 4> (directed) + ok = ok && (a.get_closest_position_in_segment(Vector3(2, 0.5, 0)) == Vector3(1.75, 0.75, 0)); + ok = ok && (a.get_closest_position_in_segment(Vector3(-1, 0.2, 0)) == Vector3(0, 0, 0)); + ok = ok && (a.get_closest_position_in_segment(Vector3(3, 2, 0)) == Vector3(2, 1, 0)); + + Math::seed(0); + + // Random tests for connectivity checks + for (int i = 0; i < 20000; i++) { + int u = Math::rand() % 5; + int v = Math::rand() % 4; + if (u == v) v = 4; + if (Math::rand() % 2 == 1) { + // Add a (possibly existing) directed edge and confirm connectivity + a.connect_points(u, v, false); + ok = ok && (a.are_points_connected(u, v, false) == true); + } else { + // Remove a (possibly nonexistent) directed edge and confirm disconnectivity + a.disconnect_points(u, v, false); + ok = ok && (a.are_points_connected(u, v, false) == false); + } + } + + // Random tests for point removal + for (int i = 0; i < 20000; i++) { + a.clear(); + for (int j = 0; j < 5; j++) + a.add_point(j, Vector3(0, 0, 0)); + + // Add or remove random edges + for (int j = 0; j < 10; j++) { + int u = Math::rand() % 5; + int v = Math::rand() % 4; + if (u == v) v = 4; + if (Math::rand() % 2 == 1) + a.connect_points(u, v, false); + else + a.disconnect_points(u, v, false); + } + + // Remove point 0 + a.remove_point(0); + // White box: this will check all edges remaining in the segments set + for (int j = 1; j < 5; j++) { + ok = ok && (a.are_points_connected(0, j, true) == false); + } + } + + // It's been great work, cheers \(^ ^)/ + return ok; +} + +bool test_solutions() { + // Random stress tests with Floyd-Warshall + + const int N = 30; + Math::seed(0); + + for (int test = 0; test < 1000; test++) { + AStar a; + Vector3 p[N]; + bool adj[N][N] = { { false } }; + + // Assign initial coordinates + for (int u = 0; u < N; u++) { + p[u].x = Math::rand() % 100; + p[u].y = Math::rand() % 100; + p[u].z = Math::rand() % 100; + a.add_point(u, p[u]); + } + + // Generate a random sequence of operations + for (int i = 0; i < 1000; i++) { + // Pick two different vertices + int u, v; + u = Math::rand() % N; + v = Math::rand() % (N - 1); + if (u == v) v = N - 1; + + // Pick a random operation + int op = Math::rand(); + switch (op % 9) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + // Add edge (u, v); possibly bidirectional + a.connect_points(u, v, op % 2); + adj[u][v] = true; + if (op % 2) adj[v][u] = true; + break; + case 6: + case 7: + // Remove edge (u, v); possibly bidirectional + a.disconnect_points(u, v, op % 2); + adj[u][v] = false; + if (op % 2) adj[v][u] = false; + break; + case 8: + // Remove point u and add it back; clears adjacent edges and changes coordinates + a.remove_point(u); + p[u].x = Math::rand() % 100; + p[u].y = Math::rand() % 100; + p[u].z = Math::rand() % 100; + a.add_point(u, p[u]); + for (v = 0; v < N; v++) + adj[u][v] = adj[v][u] = false; + break; + } + } + + // Floyd-Warshall + float d[N][N]; + for (int u = 0; u < N; u++) + for (int v = 0; v < N; v++) + d[u][v] = (u == v || adj[u][v]) ? p[u].distance_to(p[v]) : INFINITY; + + for (int w = 0; w < N; w++) + for (int u = 0; u < N; u++) + for (int v = 0; v < N; v++) + if (d[u][v] > d[u][w] + d[w][v]) + d[u][v] = d[u][w] + d[w][v]; + + // Display statistics + int count = 0; + for (int u = 0; u < N; u++) + for (int v = 0; v < N; v++) + if (adj[u][v]) count++; + printf("Test #%4d: %3d edges, ", test + 1, count); + count = 0; + for (int u = 0; u < N; u++) + for (int v = 0; v < N; v++) + if (!Math::is_inf(d[u][v])) count++; + printf("%3d/%d pairs of reachable points\n", count - N, N * (N - 1)); + + // Check A*'s output + bool match = true; + for (int u = 0; u < N; u++) + for (int v = 0; v < N; v++) + if (u != v) { + PoolVector<int> route = a.get_id_path(u, v); + if (!Math::is_inf(d[u][v])) { + // Reachable + if (route.size() == 0) { + printf("From %d to %d: A* did not find a path\n", u, v); + match = false; + goto exit; + } + float astar_dist = 0; + for (int i = 1; i < route.size(); i++) { + if (!adj[route[i - 1]][route[i]]) { + printf("From %d to %d: edge (%d, %d) does not exist\n", + u, v, route[i - 1], route[i]); + match = false; + goto exit; + } + astar_dist += p[route[i - 1]].distance_to(p[route[i]]); + } + if (!Math::is_equal_approx(astar_dist, d[u][v])) { + printf("From %d to %d: Floyd-Warshall gives %.6f, A* gives %.6f\n", + u, v, d[u][v], astar_dist); + match = false; + goto exit; + } + } else { + // Unreachable + if (route.size() > 0) { + printf("From %d to %d: A* somehow found a nonexistent path\n", u, v); + match = false; + goto exit; + } + } + } + + exit: + if (!match) return false; + } + return true; +} + typedef bool (*TestFunc)(void); TestFunc test_funcs[] = { test_abc, test_abcx, + test_add_remove, + test_solutions, NULL }; diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp index 5a76f32977..925fff0cc8 100644 --- a/modules/csg/csg.cpp +++ b/modules/csg/csg.cpp @@ -242,7 +242,7 @@ void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_ //check if edge and poly share a vertex, of so, assign it to segment_idx for (int i = 0; i < points.size(); i++) { for (int j = 0; j < 2; j++) { - if (segment[j] == points[i].point) { + if (segment[j].is_equal_approx(points[i].point)) { segment_idx[j] = i; inserted_points.push_back(i); break; @@ -310,7 +310,7 @@ void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_ Vector2 edgeseg[2] = { points[edges[i].points[0]].point, points[edges[i].points[1]].point }; Vector2 closest = Geometry::get_closest_point_to_segment_2d(segment[j], edgeseg); - if (closest == segment[j]) { + if (closest.is_equal_approx(segment[j])) { //point rest of this edge res = closest; found = true; @@ -439,7 +439,7 @@ void CSGBrushOperation::BuildPoly::clip(const CSGBrush *p_brush, int p_face, Mes //transform A points to 2D - if (segment[0] == segment[1]) + if (segment[0].is_equal_approx(segment[1])) return; //too small _clip_segment(p_brush, p_face, segment, mesh_merge, p_for_B); @@ -461,10 +461,10 @@ void CSGBrushOperation::_collision_callback(const CSGBrush *A, int p_face_a, Map { //check if either is a degenerate - if (va[0] == va[1] || va[0] == va[2] || va[1] == va[2]) + if (va[0].is_equal_approx(va[1]) || va[0].is_equal_approx(va[2]) || va[1].is_equal_approx(va[2])) return; - if (vb[0] == vb[1] || vb[0] == vb[2] || vb[1] == vb[2]) + if (vb[0].is_equal_approx(vb[1]) || vb[0].is_equal_approx(vb[2]) || vb[1].is_equal_approx(vb[2])) return; } diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index f96c4f28c8..840971dcf8 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -839,6 +839,7 @@ printraw("B") # Prints AB [/codeblock] + [b]Note:[/b] Due to limitations with Godot's built-in console, this only prints to the terminal. If you need to print in the editor, use another method, such as [method print]. </description> </method> <method name="prints" qualifiers="vararg"> diff --git a/modules/mono/glue/Managed/Files/AABB.cs b/modules/mono/glue/Managed/Files/AABB.cs index 98a73382f4..6a4f785551 100644 --- a/modules/mono/glue/Managed/Files/AABB.cs +++ b/modules/mono/glue/Managed/Files/AABB.cs @@ -458,6 +458,11 @@ namespace Godot return _position == other._position && _size == other._size; } + public bool IsEqualApprox(AABB other) + { + return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other._size); + } + public override int GetHashCode() { return _position.GetHashCode() ^ _size.GetHashCode(); diff --git a/modules/mono/glue/Managed/Files/Basis.cs b/modules/mono/glue/Managed/Files/Basis.cs index 0eb76e9c63..c5e62b77c8 100644 --- a/modules/mono/glue/Managed/Files/Basis.cs +++ b/modules/mono/glue/Managed/Files/Basis.cs @@ -654,6 +654,11 @@ namespace Godot return Row0.Equals(other.Row0) && Row1.Equals(other.Row1) && Row2.Equals(other.Row2); } + public bool IsEqualApprox(Basis other) + { + return Row0.IsEqualApprox(other.Row0) && Row1.IsEqualApprox(other.Row1) && Row2.IsEqualApprox(other.Row2); + } + public override int GetHashCode() { return Row0.GetHashCode() ^ Row1.GetHashCode() ^ Row2.GetHashCode(); diff --git a/modules/mono/glue/Managed/Files/Color.cs b/modules/mono/glue/Managed/Files/Color.cs index 3a52a1a13b..df817e47e9 100644 --- a/modules/mono/glue/Managed/Files/Color.cs +++ b/modules/mono/glue/Managed/Files/Color.cs @@ -661,6 +661,11 @@ namespace Godot public bool Equals(Color other) { + return r == other.r && g == other.g && b == other.b && a == other.a; + } + + public bool IsEqualApprox(Color other) + { return Mathf.IsEqualApprox(r, other.r) && Mathf.IsEqualApprox(g, other.g) && Mathf.IsEqualApprox(b, other.b) && Mathf.IsEqualApprox(a, other.a); } diff --git a/modules/mono/glue/Managed/Files/Mathf.cs b/modules/mono/glue/Managed/Files/Mathf.cs index ce34cd6a99..54821fe790 100644 --- a/modules/mono/glue/Managed/Files/Mathf.cs +++ b/modules/mono/glue/Managed/Files/Mathf.cs @@ -19,12 +19,12 @@ namespace Godot private const real_t Deg2RadConst = (real_t) 0.0174532925199432957692369077M; // 0.0174532924f and 0.0174532925199433 private const real_t Rad2DegConst = (real_t) 57.295779513082320876798154814M; // 57.29578f and 57.2957795130823 - public static real_t Abs(real_t s) + public static int Abs(int s) { return Math.Abs(s); } - public static int Abs(int s) + public static real_t Abs(real_t s) { return Math.Abs(s); } @@ -79,29 +79,6 @@ namespace Godot return (real_t)Math.Cosh(s); } - public static int StepDecimals(real_t step) - { - double[] sd = new double[] { - 0.9999, - 0.09999, - 0.009999, - 0.0009999, - 0.00009999, - 0.000009999, - 0.0000009999, - 0.00000009999, - 0.000000009999, - }; - double abs = Mathf.Abs(step); - double decs = abs - (int)abs; // Strip away integer part - for (int i = 0; i < sd.Length; i++) { - if (decs >= sd[i]) { - return i; - } - } - return 0; - } - public static real_t Deg2Rad(real_t deg) { return deg * Deg2RadConst; @@ -159,12 +136,14 @@ namespace Godot public static bool IsEqualApprox(real_t a, real_t b) { // Check for exact equality first, required to handle "infinity" values. - if (a == b) { + if (a == b) + { return true; } // Then check for approximate equality. real_t tolerance = Epsilon * Abs(a); - if (tolerance < Epsilon) { + if (tolerance < Epsilon) + { tolerance = Epsilon; } return Abs(a - b) < tolerance; @@ -190,7 +169,8 @@ namespace Godot return from + (to - from) * weight; } - public static real_t LerpAngle(real_t from, real_t to, real_t weight) { + public static real_t LerpAngle(real_t from, real_t to, real_t weight) + { real_t difference = (to - from) % Mathf.Tau; real_t distance = ((2 * difference) % Mathf.Tau) - difference; return from + distance * weight; @@ -246,9 +226,9 @@ namespace Godot /// <summary> /// Performs a canonical Modulus operation, where the output is on the range [0, b). /// </summary> - public static real_t PosMod(real_t a, real_t b) + public static int PosMod(int a, int b) { - real_t c = a % b; + int c = a % b; if ((c < 0 && b > 0) || (c > 0 && b < 0)) { c += b; @@ -259,9 +239,9 @@ namespace Godot /// <summary> /// Performs a canonical Modulus operation, where the output is on the range [0, b). /// </summary> - public static int PosMod(int a, int b) + public static real_t PosMod(real_t a, real_t b) { - int c = a % b; + real_t c = a % b; if ((c < 0 && b > 0) || (c > 0 && b < 0)) { c += b; @@ -319,6 +299,31 @@ namespace Godot return (real_t)Math.Sqrt(s); } + public static int StepDecimals(real_t step) + { + double[] sd = new double[] { + 0.9999, + 0.09999, + 0.009999, + 0.0009999, + 0.00009999, + 0.000009999, + 0.0000009999, + 0.00000009999, + 0.000000009999, + }; + double abs = Mathf.Abs(step); + double decs = abs - (int)abs; // Strip away integer part + for (int i = 0; i < sd.Length; i++) + { + if (decs >= sd[i]) + { + return i; + } + } + return 0; + } + public static real_t Stepify(real_t s, real_t step) { if (step != 0f) diff --git a/modules/mono/glue/Managed/Files/MathfEx.cs b/modules/mono/glue/Managed/Files/MathfEx.cs index 6cffc7f01d..1b7fd4906f 100644 --- a/modules/mono/glue/Managed/Files/MathfEx.cs +++ b/modules/mono/glue/Managed/Files/MathfEx.cs @@ -49,7 +49,8 @@ namespace Godot public static bool IsEqualApprox(real_t a, real_t b, real_t tolerance) { // Check for exact equality first, required to handle "infinity" values. - if (a == b) { + if (a == b) + { return true; } // Then check for approximate equality. diff --git a/modules/mono/glue/Managed/Files/Plane.cs b/modules/mono/glue/Managed/Files/Plane.cs index 26e717e089..885845e3a4 100644 --- a/modules/mono/glue/Managed/Files/Plane.cs +++ b/modules/mono/glue/Managed/Files/Plane.cs @@ -204,7 +204,12 @@ namespace Godot public bool Equals(Plane other) { - return _normal == other._normal && Mathf.IsEqualApprox(D, other.D); + return _normal == other._normal && D == other.D; + } + + public bool IsEqualApprox(Plane other) + { + return _normal.IsEqualApprox(other._normal) && Mathf.IsEqualApprox(D, other.D); } public override int GetHashCode() diff --git a/modules/mono/glue/Managed/Files/Quat.cs b/modules/mono/glue/Managed/Files/Quat.cs index 845c7c730e..8f60867ac3 100644 --- a/modules/mono/glue/Managed/Files/Quat.cs +++ b/modules/mono/glue/Managed/Files/Quat.cs @@ -363,6 +363,11 @@ namespace Godot public bool Equals(Quat other) { + return x == other.x && y == other.y && z == other.z && w == other.w; + } + + public bool IsEqualApprox(Quat other) + { return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w); } diff --git a/modules/mono/glue/Managed/Files/Rect2.cs b/modules/mono/glue/Managed/Files/Rect2.cs index 99542d0c0a..91e614dc7b 100644 --- a/modules/mono/glue/Managed/Files/Rect2.cs +++ b/modules/mono/glue/Managed/Files/Rect2.cs @@ -231,6 +231,11 @@ namespace Godot return _position.Equals(other._position) && _size.Equals(other._size); } + public bool IsEqualApprox(Rect2 other) + { + return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other.Size); + } + public override int GetHashCode() { return _position.GetHashCode() ^ _size.GetHashCode(); diff --git a/modules/mono/glue/Managed/Files/Transform.cs b/modules/mono/glue/Managed/Files/Transform.cs index cc4d26158d..0b84050f07 100644 --- a/modules/mono/glue/Managed/Files/Transform.cs +++ b/modules/mono/glue/Managed/Files/Transform.cs @@ -185,6 +185,11 @@ namespace Godot return basis.Equals(other.basis) && origin.Equals(other.origin); } + public bool IsEqualApprox(Transform other) + { + return basis.IsEqualApprox(other.basis) && origin.IsEqualApprox(other.origin); + } + public override int GetHashCode() { return basis.GetHashCode() ^ origin.GetHashCode(); diff --git a/modules/mono/glue/Managed/Files/Transform2D.cs b/modules/mono/glue/Managed/Files/Transform2D.cs index 814332dc07..77ea3e5830 100644 --- a/modules/mono/glue/Managed/Files/Transform2D.cs +++ b/modules/mono/glue/Managed/Files/Transform2D.cs @@ -357,6 +357,11 @@ namespace Godot return x.Equals(other.x) && y.Equals(other.y) && origin.Equals(other.origin); } + public bool IsEqualApprox(Transform2D other) + { + return x.IsEqualApprox(other.x) && y.IsEqualApprox(other.y) && origin.IsEqualApprox(other.origin); + } + public override int GetHashCode() { return x.GetHashCode() ^ y.GetHashCode() ^ origin.GetHashCode(); diff --git a/modules/mono/glue/Managed/Files/Vector2.cs b/modules/mono/glue/Managed/Files/Vector2.cs index 0daa94057e..f92453f546 100644 --- a/modules/mono/glue/Managed/Files/Vector2.cs +++ b/modules/mono/glue/Managed/Files/Vector2.cs @@ -455,6 +455,11 @@ namespace Godot public bool Equals(Vector2 other) { + return x == other.x && y == other.y; + } + + public bool IsEqualApprox(Vector2 other) + { return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y); } diff --git a/modules/mono/glue/Managed/Files/Vector3.cs b/modules/mono/glue/Managed/Files/Vector3.cs index 9076dbd3b0..025b09199f 100644 --- a/modules/mono/glue/Managed/Files/Vector3.cs +++ b/modules/mono/glue/Managed/Files/Vector3.cs @@ -513,6 +513,11 @@ namespace Godot public bool Equals(Vector3 other) { + return x == other.x && y == other.y && z == other.z; + } + + public bool IsEqualApprox(Vector3 other) + { return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z); } diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 0db771f7c0..6aae2fd15b 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -3515,6 +3515,7 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri } Ref<VisualScriptNode> vnode; + Ref<VisualScriptPropertySet> script_prop_set; if (p_category == String("method")) { @@ -3525,8 +3526,8 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri Ref<VisualScriptPropertySet> n; n.instance(); - n->set_property(p_text); vnode = n; + script_prop_set = n; } else if (p_category == String("get")) { Ref<VisualScriptPropertyGet> n; @@ -3578,6 +3579,9 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri undo_redo->add_undo_method(this, "_update_graph", new_id); undo_redo->commit_action(); + if (script_prop_set.is_valid()) + script_prop_set->set_property(p_text); + port_action_new_node = new_id; Ref<VisualScriptNode> vsn = script->get_node(func, port_action_new_node); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index c4cd8e068c..e73cfcf241 100644..100755 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -556,8 +556,14 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) mm->set_button_mask(last_button_state); - mm->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); - mm->set_global_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); + POINT coords; //client coords + coords.x = GET_X_LPARAM(lParam); + coords.y = GET_Y_LPARAM(lParam); + + ScreenToClient(hWnd, &coords); + + mm->set_position(Vector2(coords.x, coords.y)); + mm->set_global_position(Vector2(coords.x, coords.y)); if (mouse_mode == MOUSE_MODE_CAPTURED) { diff --git a/scene/2d/navigation_2d.cpp b/scene/2d/navigation_2d.cpp index 5cf28d6c89..c7d2dabbae 100644 --- a/scene/2d/navigation_2d.cpp +++ b/scene/2d/navigation_2d.cpp @@ -541,7 +541,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect if (CLOCK_TANGENT(apex_point, portal_left, left) >= 0) { //process - if (Math::is_zero_approx(portal_left.distance_squared_to(apex_point)) || CLOCK_TANGENT(apex_point, left, portal_right) > 0) { + if (portal_left.is_equal_approx(apex_point) || CLOCK_TANGENT(apex_point, left, portal_right) > 0) { left_poly = p; portal_left = left; } else { @@ -551,7 +551,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect left_poly = p; portal_left = apex_point; portal_right = apex_point; - if (!path.size() || path[path.size() - 1] != apex_point) + if (!path.size() || !path[path.size() - 1].is_equal_approx(apex_point)) path.push_back(apex_point); skip = true; } @@ -559,7 +559,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect if (!skip && CLOCK_TANGENT(apex_point, portal_right, right) <= 0) { //process - if (Math::is_zero_approx(portal_right.distance_squared_to(apex_point)) || CLOCK_TANGENT(apex_point, right, portal_left) < 0) { + if (portal_right.is_equal_approx(apex_point) || CLOCK_TANGENT(apex_point, right, portal_left) < 0) { right_poly = p; portal_right = right; } else { @@ -569,7 +569,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect right_poly = p; portal_right = apex_point; portal_left = apex_point; - if (!path.size() || path[path.size() - 1] != apex_point) + if (!path.size() || !path[path.size() - 1].is_equal_approx(apex_point)) path.push_back(apex_point); } } @@ -595,7 +595,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect } } - if (!path.size() || !Math::is_zero_approx(path[path.size() - 1].distance_squared_to(begin_point))) { + if (!path.size() || !path[path.size() - 1].is_equal_approx(begin_point)) { path.push_back(begin_point); // Add the begin point } else { path.write[path.size() - 1] = begin_point; // Replace first midpoint by the exact begin point @@ -603,7 +603,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect path.invert(); - if (path.size() <= 1 || !Math::is_zero_approx(path[path.size() - 1].distance_squared_to(end_point))) { + if (path.size() <= 1 || !path[path.size() - 1].is_equal_approx(end_point)) { path.push_back(end_point); // Add the end point } else { path.write[path.size() - 1] = end_point; // Replace last midpoint by the exact end point diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index a02cc4bee6..a107c3bf7a 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -1201,7 +1201,7 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve if (p_stop_on_slope) { if ((lv_n + p_floor_direction).length() < 0.01 && collision.travel.length() < 1) { Transform gt = get_global_transform(); - gt.origin -= collision.travel; + gt.origin -= collision.travel.slide(p_floor_direction); set_global_transform(gt); return Vector3(); } @@ -1265,7 +1265,7 @@ Vector3 KinematicBody::move_and_slide_with_snap(const Vector3 &p_linear_velocity if (p_stop_on_slope) { // move and collide may stray the object a bit because of pre un-stucking, // so only ensure that motion happens on floor direction in this case. - col.travel = p_floor_direction * p_floor_direction.dot(col.travel); + col.travel = col.travel.project(p_floor_direction); } } else { apply = false; //snapped with floor direction, but did not snap to a floor, do not snap. diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 95536bbb23..d060ac273d 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2298,12 +2298,12 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { Input *input = Input::get_singleton(); - if (p_event->is_action_pressed("ui_focus_next") && input->is_action_just_pressed("ui_focus_next")) { + if (!mods && p_event->is_action_pressed("ui_focus_next") && input->is_action_just_pressed("ui_focus_next")) { next = from->find_next_valid_focus(); } - if (p_event->is_action_pressed("ui_focus_prev") && input->is_action_just_pressed("ui_focus_prev")) { + if (!mods && p_event->is_action_pressed("ui_focus_prev") && input->is_action_just_pressed("ui_focus_prev")) { next = from->find_prev_valid_focus(); } diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index d932545da4..f4ac277d00 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -2870,9 +2870,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons const Vector3 &v1 = t1.value.loc; const Vector3 &v2 = t2.value.loc; - if (v0 == v2) { + if (v0.is_equal_approx(v2)) { //0 and 2 are close, let's see if 1 is close - if (v0 != v1) { + if (!v0.is_equal_approx(v1)) { //not close, not optimizable return false; } @@ -2909,9 +2909,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons //localize both to rotation from q0 - if (Math::is_zero_approx((q0 - q2).length())) { + if (q0.is_equal_approx(q2)) { - if (!Math::is_zero_approx((q0 - q1).length())) + if (!q0.is_equal_approx(q1)) return false; } else { @@ -2959,9 +2959,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons const Vector3 &v1 = t1.value.scale; const Vector3 &v2 = t2.value.scale; - if (v0 == v2) { + if (v0.is_equal_approx(v2)) { //0 and 2 are close, let's see if 1 is close - if (v0 != v1) { + if (!v0.is_equal_approx(v1)) { //not close, not optimizable return false; } |