diff options
262 files changed, 4097 insertions, 3005 deletions
diff --git a/.gitignore b/.gitignore index 00d87b6bf6..5b3414fe7e 100644 --- a/.gitignore +++ b/.gitignore @@ -390,3 +390,6 @@ gcov.css # https://clangd.llvm.org/ cache folder .clangd/ .cache/ + +# Generated by unit tests files +tests/data/*.translation @@ -29,6 +29,8 @@ Eric M <itsjusteza@gmail.com> Eric Rybicki <info@ericrybicki.com> <stratos695@googlemail.com> Erik Selecký <35656626+rxlecky@users.noreply.github.com> Erik Selecký <35656626+rxlecky@users.noreply.github.com> <35656626+SeleckyErik@users.noreply.github.com> +Eveline Jarosz <marqin.pl@gmail.com> +Eveline Jarosz <marqin.pl@gmail.com> <marqin.pl+git@gmail.com> Fabian <supagu@gmail.com> Ferenc Arn <tagcup@yahoo.com> Ferenc Arn <tagcup@yahoo.com> <tagcup@users.noreply.github.com> @@ -43,8 +45,6 @@ Guilherme Felipe <guilhermefelipecgs@gmail.com> Hanif Bin Ariffin <hanif.ariffin.4326@gmail.com> HaSa1002 <johawitt@outlook.de> Hein-Pieter van Braam-Stewart <hp@tmm.cx> -Hubert Jarosz <marqin.pl@gmail.com> -Hubert Jarosz <marqin.pl@gmail.com> <marqin.pl+git@gmail.com> Hugo Locurcio <hugo.locurcio@hugo.pro> <hugo.l@openmailbox.org> Hugo Locurcio <hugo.locurcio@hugo.pro> <Calinou@users.noreply.github.com> Hugo Locurcio <hugo.locurcio@hugo.pro> Calinou <calinou@opmbx.org> @@ -107,6 +107,7 @@ Pieter-Jan Briers <pieterjan.briers+git@gmail.com> Pieter-Jan Briers <pieterjan.briers+git@gmail.com> <pieterjan.briers@gmail.com> Poommetee Ketson <poommetee@protonmail.com> Przemysław Gołąb (n-pigeon) <golab.przemyslaw@gmail.com> +Rafał Mikrut <mikrutrafal@protonmail.com> Rafał Mikrut <mikrutrafal@protonmail.com> <mikrutrafal54@gmail.com> Ralf Hölzemer <r.hoelzemer@posteo.de> <rollenrolm@posteo.de> Ralf Hölzemer <r.hoelzemer@posteo.de> <rollenrolm@users.noreply.github.com> @@ -116,6 +117,7 @@ RaphaelHunter <raphael10241024@gmail.com> <Raphael10241024@gmail.com> RaphaelHunter <raphael10241024@gmail.com> <raphael20141024@gmail.com> Rémi Verschelde <rverschelde@gmail.com> <remi@verschelde.fr> Rhody Lugo <rhodylugo@gmail.com> <rhodylugo@me.com> +Ricardo Subtil <ricasubtil@gmail.com> Robin Hübner <profan@prfn.se> <robinhubner@gmail.com> romulox_x <romulox_x@yahoo.com> Ruslan Mustakov <r.mustakov@gmail.com> <ruslan.mustakov@xored.com> diff --git a/AUTHORS.md b/AUTHORS.md index 5147da3700..0a0e3a9c21 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -36,6 +36,7 @@ name is available. Andrii Doroshenko (Xrayez) Andy Moss (MillionOstrich) Angad Kambli (angad-k) + Anilforextra (AnilBK) Anish Bhobe (KidRigger) Anton Yabchinskiy (a12n) Anutrix @@ -76,6 +77,7 @@ name is available. Eric Rybicki (ericrybick) Erik Selecký (rxlecky) est31 + Eveline Jarosz (Marqin) Fabian Mathews (supagu) Fabio Alessandrelli (Faless) fabriceci @@ -99,7 +101,6 @@ name is available. Hiroshi Ogawa (hi-ogawa) homer666 hoontee - Hubert Jarosz (Marqin) Hugo Locurcio (Calinou) Ian Bishop (ianb96) Ibrahn Sahir (ibrahn) @@ -109,9 +110,9 @@ name is available. J08nY Jakub Grzesik (kubecz3k) James Buck (jbuck3) + Jean-Michel Bernard (jmb462) Jérôme Gully (Nutriz) Jia Jun Chai (SkyLucilfer) - jmb462 Joan Fons Sanchez (JFonS) Johannes Witt (HaSa1002) Johan Manuel (29jm) @@ -119,6 +120,7 @@ name is available. Juan Linietsky (reduz) Julian Murgia (StraToN) Julien Nguyen (Blackiris) + Jummit Justo Delgado (mrcdk) Kelly Thomas (KellyThomas) kleonc @@ -137,7 +139,7 @@ name is available. Marc Gilleron (Zylann) Marcin Zawiejski (dragmz) Marcus Brummer (mbrlabs) - Marcus (MCrafterzz) + Marcus Elg (MCrafterzz) Mariano Javier Suligoy (MarianoGnu) Mario Schlack (hurikhan) Martin Capitanio (capnm) diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index da60213038..e84302620e 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -80,6 +80,13 @@ Copyright: 2008-2016, The Android Open Source Project 2002, Google, Inc. License: Apache-2.0 +Files: ./scene/animation/easing_equations.h +Comment: Robert Penner's Easing Functions +Copyright: 2001, Robert Penner + 2007-2021 Juan Linietsky, Ariel Manzur. + 2014-2021 Godot Engine contributors. +License: Expat + Files: ./servers/physics_3d/collision_solver_3d_sat.cpp Comment: Open Dynamics Engine Copyright: 2001-2003, Russell L. Smith, Alen Ladavac, Nguyen Binh @@ -307,11 +314,6 @@ Comment: Fast Filtering of Reflection Probes Copyright: 2016, Activision Publishing, Inc. License: Expat -Files: ./thirdparty/misc/easing_equations.cpp -Comment: Robert Penner's Easing Functions -Copyright: 2001, Robert Penner -License: BSD-3-clause - Files: ./thirdparty/misc/fastlz.c ./thirdparty/misc/fastlz.h Comment: FastLZ @@ -23,7 +23,6 @@ generous deed immortalized in the next stable release of Godot Engine. ## Silver sponsors ASIFA-Hollywood <https://www.asifa-hollywood.org> - LITSLINK <https://litslink.com> ## Bronze sponsors @@ -41,6 +40,7 @@ generous deed immortalized in the next stable release of Godot Engine. AD Ford alex brown + Andrew Bowen Andrew Dunai Angry Skull anti666 @@ -65,7 +65,6 @@ generous deed immortalized in the next stable release of Godot Engine. Jonah Stich Justin Arnold Justo Delgado Baudí - Kamil Brzezinskip Marcel Kräml Marek Belski Matthieu Huvé @@ -93,24 +92,26 @@ generous deed immortalized in the next stable release of Godot Engine. Acheron Adam Brown albinaask - Alvaro A Baena R Andres Hernandez + Arisaka Mayuki Asher Glick Barugon + Benito Carlo Cabanilla Daniel James David Gehrig David Graham David Snopek - Don B Ed Morley Ellen Poe Florian Rämisch Gamejunkey + Jacobus Dens Jakub Grzesik Javier Roman Joan Fons Johnny IV Young + Jonathan Wright Jon Woodward Karl Werf Klavdij Voncina @@ -119,13 +120,14 @@ generous deed immortalized in the next stable release of Godot Engine. Maciej Pendolski Manuele Finocchiaro Markus Wiesner - Mason Bially Matthew Hillier Michael + Mick Monster Vial Officine Pixel S.n.c. + Patrick Brock + Pedro Silva Petrus Prinsloo - Rene Retro Village Rob Messick Roland Fredenhagen @@ -133,8 +135,10 @@ generous deed immortalized in the next stable release of Godot Engine. Sarksus Scott B Sean + segfault-god Sergey Sofox + Stephan Kessler Stephen Molyneaux Taylor Ritenour Tom Langwaldt @@ -142,22 +146,21 @@ generous deed immortalized in the next stable release of Godot Engine. tukon Vagabond Arcade Vitaliy Sapronenko - William Wold Xeno Coliseum Zaven Muradyan Adam Nakonieczny + Adam Nelson + Alexander Erlemann Alexander J Maynard - Alex de la Mare - Alexey Dyadchenko Alex Khayrullin alice gambrell Andrew Cunningham Andrew Farr + Andriy Antanas Paskauskas Antoni Batchelli Arch Henderson III - Arisaka Mayuki Arthur S. Muszynski Ben Botwin Brandon Hawkinson @@ -181,11 +184,8 @@ generous deed immortalized in the next stable release of Godot Engine. Daniel Tebbutt Darrian Little Dennis Belfrage - Dev To be curious - Digital Denizen Dimitri Nüscheler Donn Eddy - Easypete Edgar Sun Eric Eric Brand @@ -200,6 +200,7 @@ generous deed immortalized in the next stable release of Godot Engine. General Chicken Geoffroy Warin GGGames.org + gisora GrayDwarf Guilherme Felipe de C. G. da Silva Harvey Fong @@ -210,22 +211,22 @@ generous deed immortalized in the next stable release of Godot Engine. Hunter Barabas Jake Burga James Couzens + James Zahn Jan Sælid Jared Jared White - Jeppe Zapp + Jennifer Wilcox Jesús Chicharro Joel Fivat Johnathan Kupferer - John Knight Jonathan Turner + Josef Stumpfegger Jose Malheiro Jose Manuel Muñoz Perez Joseph Crane Joshie Sparks Joshua Flores Joshua Lesperance - Juan T Chen Juan Velandia Judd Julián Absatz @@ -237,13 +238,13 @@ generous deed immortalized in the next stable release of Godot Engine. kinfox Kis Levente Lorand Kos - Lain Ballard Laszlo Kiss leetNightshade Leo Fidel R Liban Liam Smyth LoparPanda Luca Vazzano + Luke MadScientistCarl Marcus Dobler Marcus Richter @@ -253,6 +254,7 @@ generous deed immortalized in the next stable release of Godot Engine. Martin Soucek matt Matt Greene + Matthew Hall Matthias Toepp Mecha Kaiju X medecau @@ -264,27 +266,24 @@ generous deed immortalized in the next stable release of Godot Engine. MikadoSC Mike Barbee minz1 - MuffinManKen nate etan Nick Abousselam + Nicola Cocchiaro Nicole Barovic + Nikita Bliznyuk Oliver Dick Oscar Campos - Patrick Ting Paul Hocker Paul Von Zimmerman Pavel Kotlyar - Pedro Silva Pete Goodwin - Peter Richmond Petr Malac PhaineOfCatz - pl + Piotr Wyszyński + Pyxl Raymond Harris - Renato Fontes Rene Tailleur Rhodochrone - Ricardo Alcantara Rob Robert McDermott Robert Willes @@ -295,6 +294,7 @@ generous deed immortalized in the next stable release of Godot Engine. Ronnie Ashlock Ronny Mühle Ryan Scott + Samuel Hummerstone Samuel Judd Samuel Smart Sean Morgan @@ -304,6 +304,7 @@ generous deed immortalized in the next stable release of Godot Engine. Sergey Fonaryov Sergey Minakov Shishir Tandale + Sing Chun Lee SKison Song Junwoo spacechase0 @@ -316,6 +317,7 @@ generous deed immortalized in the next stable release of Godot Engine. Thomas Kurz Tim Howard Tobias Bocanegra + Tobias Raggl Todd Smith Torbulous toto bibi @@ -333,37 +335,36 @@ generous deed immortalized in the next stable release of Godot Engine. Yifan Lai Yuancheng Zhang Zie Weaver - Zoran Kukulj ## Silver donors 1D_Inc Aaron Oldenburg + A. B. Actual_Dio Adam Brunnmeier Adam Carr Adam Long Adam McCurdy Adam N Webber + Adam R Pope Adam Smeltzer Adam Szymański Adisibio Adrien de Pierres - Agar3s - Giovanny Beltrán Ahmet Kalyoncu Aidan O'Flannagain Aki Mimoto Alan Beauchamp + Alberto Salazar Muñoz Alberto Vilches Albin Jonasson Svärdsby Alder Stefano AleMax + Ales Jelovcan Alessandro Senese - Alexander Erlemann Alexander Ravenheart - Alexander Ryndin Alexander Walter (SilvanuZ) - Alexandre Beaudoin Alex Chan Alex Clavelle alex raeside @@ -379,6 +380,7 @@ generous deed immortalized in the next stable release of Godot Engine. Ano Nim Anthony Avina Anton Bouwer + Antti Vesanen Arch Toasty Arda Erol Armin Preiml @@ -394,14 +396,17 @@ generous deed immortalized in the next stable release of Godot Engine. Bartosz Bielecki Benedikt Benoit Jauvin-Girard + Ben Ridley Ben Vercammen Bernd Jänichen Bernhard Werner Bill Thibault + bitbrain Bjarne Voigtländer Black Block blackjacksike Blair Allen + Blunderjack Bobby CC Wong Borkzilla Bram @@ -409,19 +414,19 @@ generous deed immortalized in the next stable release of Godot Engine. Brian Klein Brodie Fairhall Bronson Zgeb - Bùi Việt Thành + Bruno Hurth Burney Waring bwhirt Caleb Gartner Caleb Makela Cameron Meyer Carlos Cejudo + Carlos Rios Carl van der Geest Casey Cassidy James Cédric Givord Chad Steadman - Charles Alston Chris Chapin Chris Langford Christian Clavet @@ -447,13 +452,16 @@ generous deed immortalized in the next stable release of Godot Engine. David Bôle David May David Maziarka + David Rapisarda deadwithbread Devin Carraway Diego Pereira Dima Fedotov + Dimitri Roche Dmitry Fisher Dmytro Korchynskyi Dominik Wetzel + Don B Douglas Plumley Dragontrapper Dr Ewan Murray @@ -473,12 +481,12 @@ generous deed immortalized in the next stable release of Godot Engine. Eric Walkingshaw Eric Williams Erkki Seppälä - Evan Rose Faisal Alkubaisi Fancy Ants Studios fby Fekinox Felix Bohmann + Filip Lundby Forty Doubleu Francisco Garcia Florez Francois Holland @@ -489,6 +497,7 @@ generous deed immortalized in the next stable release of Godot Engine. George Marques Georgi Petkov Graham Overby + Green Fox Greg Lincoln Greg Olson Greyson Richey @@ -503,7 +512,6 @@ generous deed immortalized in the next stable release of Godot Engine. Hunter Jones Ian ORourke Ian Williams - Iiari IndustrialRobot Ivan Nikolaev iveks @@ -515,6 +523,7 @@ generous deed immortalized in the next stable release of Godot Engine. Jako Danar James James A F Manley + James Gary James Guardino James Quincy James Thomas @@ -546,7 +555,6 @@ generous deed immortalized in the next stable release of Godot Engine. Jonatan R Jonathan Bieber Jonathan G - Jonathan Wright Jon Bonazza Jon Sully Jordan West @@ -565,9 +573,9 @@ generous deed immortalized in the next stable release of Godot Engine. Julian Murgia June Little Justin Hamilton + Justin Hurst Justin Oaksford Justin Spedding - Justin W. Flory KaDokta Karol Porzycki Keedong Park @@ -575,7 +583,6 @@ generous deed immortalized in the next stable release of Godot Engine. Keith Bradner Kenji Kawabata Ken Minardo - Kenneth Lee Kent Jofur Ketafuki Kevin van Rooijen @@ -585,6 +592,7 @@ generous deed immortalized in the next stable release of Godot Engine. Kolandrious Konstantin Goncharov kormai + Kquona Krishna Nadoor Kristian Nygaard Jensen KR McGinley @@ -596,7 +604,6 @@ generous deed immortalized in the next stable release of Godot Engine. Kyuppin Lasse le Dous Laurent CHEA - Laurent Tréguier Laxman Pradhan LEMMiNO Leonardo Dimano @@ -614,11 +621,12 @@ generous deed immortalized in the next stable release of Godot Engine. Mark Malone Markus Martin Markus Michael Egger + Markus Ort Martin FIbik Martin Holas Martin Liška Martin Trbola - Marvin + Martin Zabinski Mathieu Matt Edwards Matthew Booe @@ -642,6 +650,7 @@ generous deed immortalized in the next stable release of Godot Engine. Mike Mike Birkhead Mike Copley + Mitchell Mitchell J. Wagner MJacred ModularMind @@ -666,25 +675,21 @@ generous deed immortalized in the next stable release of Godot Engine. Nicolas Goll-Perrier Nicolas Rosset Nicolas SAN AGUSTIN + Nils Nordmark Nima Farid Noel Billig - Nuno Dionísio NZ oceoh Okatima Oleg Reva - Omar Delarosa - Orfist Oriol Muñoz Princep oscar1000108 Oscar Domingo Pascal - Patrick Brock Patrick Nafarrete Paul Gieske Paweł Kowal Paweł Łyczkowski - p_brighenti Peter Höglund Philip Cohoe Philip Ludington (MrPhil) @@ -695,43 +700,44 @@ generous deed immortalized in the next stable release of Godot Engine. pwab RabidTunes Rackat + RackBar Dingum Rad Cat Rafa Laguna Raffaele Aramo Rainer Amler Rami Hanano Rammeow - RAMupgrade Remi Rampin Reneator + René Habermann Riccardo Marini Richard Hayes Richard Ivánek Richard Néveri Riley - Robert Farr (Larington) Rob Ruana Rodrigo Loli Roger Smith + Roglozor Roland Rząsa Roman Tinkov Ronald Ho Hip (CrimsonZA) Ronan - Ross Squires Roy Scayged + Ryan Ryan Groom Sam Caulfield Sam Edson Scott Longley Sean Lynch Sebastian Michailidis - segfault-god SeongWan Kim SeungJong k Shaidak Shane Shane Sicienski Shane Spoor + Silver1063 simdee Simon Jonas Larsen Simon Schoenenberger @@ -741,10 +747,12 @@ generous deed immortalized in the next stable release of Godot Engine. smbe19 smo1704 Solene Waked + Sophie Winter Spencer Everhart Squirrel Stéphane Roussel Steve Cloete + Steven Drovie summerblind Sung soo Choi Svenne Krap @@ -753,6 +761,7 @@ generous deed immortalized in the next stable release of Godot Engine. Tarch Terry the9thdude + The Domis4 Theodore Lindsey TheVoiceInMyHead thomas @@ -790,6 +799,7 @@ generous deed immortalized in the next stable release of Godot Engine. Vi Watch Vladimir Savin Vladislav Smirnov + VoxelVisions.com Vytenis Narušis werner mendizabal Wiley Thompson diff --git a/SConstruct b/SConstruct index b6c98eea77..b539dc59b7 100644 --- a/SConstruct +++ b/SConstruct @@ -91,6 +91,7 @@ env_base.__class__.add_program = methods.add_program env_base.__class__.CommandNoCache = methods.CommandNoCache env_base.__class__.Run = methods.Run env_base.__class__.disable_warnings = methods.disable_warnings +env_base.__class__.force_optimization_on_debug = methods.force_optimization_on_debug env_base.__class__.module_check_dependencies = methods.module_check_dependencies env_base["x86_libtheora_opt_gcc"] = False diff --git a/core/core_bind.cpp b/core/core_bind.cpp index e029b85450..1f028702f6 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -1786,13 +1786,13 @@ void Thread::_start_func(void *ud) { // We must check if we are in case b). int target_param_count = 0; int target_default_arg_count = 0; - Ref<Script> script = t->target_instance->get_script(); + Ref<Script> script = t->target_callable.get_object()->get_script(); if (script.is_valid()) { - MethodInfo mi = script->get_method_info(t->target_method); + MethodInfo mi = script->get_method_info(t->target_callable.get_method()); target_param_count = mi.arguments.size(); target_default_arg_count = mi.default_arguments.size(); } else { - MethodBind *method = ClassDB::get_method(t->target_instance->get_class_name(), t->target_method); + MethodBind *method = ClassDB::get_method(t->target_callable.get_object()->get_class_name(), t->target_callable.get_method()); target_param_count = method->get_argument_count(); target_default_arg_count = method->get_default_argument_count(); } @@ -1801,41 +1801,21 @@ void Thread::_start_func(void *ud) { } } - ::Thread::set_name(t->target_method); + ::Thread::set_name(t->target_callable.get_method()); - t->ret = t->target_instance->call(t->target_method, arg, argc, ce); + t->target_callable.call(arg, argc, t->ret, ce); if (ce.error != Callable::CallError::CALL_OK) { - String reason; - switch (ce.error) { - case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: { - reason = "Invalid Argument #" + itos(ce.argument); - } break; - case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: { - reason = "Too Many Arguments"; - } break; - case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: { - reason = "Too Few Arguments"; - } break; - case Callable::CallError::CALL_ERROR_INVALID_METHOD: { - reason = "Method Not Found"; - } break; - default: { - } - } - - ERR_FAIL_MSG("Could not call function '" + t->target_method.operator String() + "' to start thread " + t->get_id() + ": " + reason + "."); + ERR_FAIL_MSG("Could not call function '" + t->target_callable.get_method().operator String() + "' to start thread " + t->get_id() + ": " + Variant::get_callable_error_text(t->target_callable, arg, argc, ce) + "."); } } -Error Thread::start(Object *p_instance, const StringName &p_method, const Variant &p_userdata, Priority p_priority) { +Error Thread::start(const Callable &p_callable, const Variant &p_userdata, Priority p_priority) { ERR_FAIL_COND_V_MSG(active.is_set(), ERR_ALREADY_IN_USE, "Thread already started."); - ERR_FAIL_COND_V(!p_instance, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(p_method == StringName(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_callable.is_null(), ERR_INVALID_PARAMETER); ERR_FAIL_INDEX_V(p_priority, PRIORITY_MAX, ERR_INVALID_PARAMETER); ret = Variant(); - target_method = p_method; - target_instance = p_instance; + target_callable = p_callable; userdata = p_userdata; active.set(); @@ -1861,15 +1841,14 @@ Variant Thread::wait_to_finish() { thread.wait_to_finish(); Variant r = ret; active.clear(); - target_method = StringName(); - target_instance = nullptr; + target_callable = Callable(); userdata = Variant(); return r; } void Thread::_bind_methods() { - ClassDB::bind_method(D_METHOD("start", "instance", "method", "userdata", "priority"), &Thread::start, DEFVAL(Variant()), DEFVAL(PRIORITY_NORMAL)); + ClassDB::bind_method(D_METHOD("start", "callable", "userdata", "priority"), &Thread::start, DEFVAL(Variant()), DEFVAL(PRIORITY_NORMAL)); ClassDB::bind_method(D_METHOD("get_id"), &Thread::get_id); ClassDB::bind_method(D_METHOD("is_active"), &Thread::is_active); ClassDB::bind_method(D_METHOD("wait_to_finish"), &Thread::wait_to_finish); @@ -2221,7 +2200,7 @@ Object *Engine::get_singleton_object(const StringName &p_name) const { void Engine::register_singleton(const StringName &p_name, Object *p_object) { ERR_FAIL_COND_MSG(has_singleton(p_name), "Singleton already registered: " + String(p_name)); - ERR_FAIL_COND_MSG(p_name.operator String().is_valid_identifier(), "Singleton name is not a valid identifier: " + String(p_name)); + ERR_FAIL_COND_MSG(!String(p_name).is_valid_identifier(), "Singleton name is not a valid identifier: " + p_name); ::Engine::Singleton s; s.class_name = p_name; s.name = p_name; @@ -2297,13 +2276,11 @@ void Engine::_bind_methods() { ClassDB::bind_method(D_METHOD("unregister_singleton", "name"), &Engine::unregister_singleton); ClassDB::bind_method(D_METHOD("get_singleton_list"), &Engine::get_singleton_list); - ClassDB::bind_method(D_METHOD("set_editor_hint", "enabled"), &Engine::set_editor_hint); ClassDB::bind_method(D_METHOD("is_editor_hint"), &Engine::is_editor_hint); ClassDB::bind_method(D_METHOD("set_print_error_messages", "enabled"), &Engine::set_print_error_messages); ClassDB::bind_method(D_METHOD("is_printing_error_messages"), &Engine::is_printing_error_messages); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_hint"), "set_editor_hint", "is_editor_hint"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "print_error_messages"), "set_print_error_messages", "is_printing_error_messages"); ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_ticks_per_second"), "set_physics_ticks_per_second", "get_physics_ticks_per_second"); ADD_PROPERTY(PropertyInfo(Variant::INT, "target_fps"), "set_target_fps", "get_target_fps"); diff --git a/core/core_bind.h b/core/core_bind.h index a5d5a7c8ce..84a284f948 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -539,8 +539,7 @@ protected: Variant ret; Variant userdata; SafeFlag active; - Object *target_instance = nullptr; - StringName target_method; + Callable target_callable; ::Thread thread; static void _bind_methods(); static void _start_func(void *ud); @@ -553,7 +552,7 @@ public: PRIORITY_MAX }; - Error start(Object *p_instance, const StringName &p_method, const Variant &p_userdata = Variant(), Priority p_priority = PRIORITY_NORMAL); + Error start(const Callable &p_callable, const Variant &p_userdata = Variant(), Priority p_priority = PRIORITY_NORMAL); String get_id() const; bool is_active() const; Variant wait_to_finish(); diff --git a/core/extension/gdnative_interface.cpp b/core/extension/gdnative_interface.cpp index b41f74a4bc..ff09b0b86c 100644 --- a/core/extension/gdnative_interface.cpp +++ b/core/extension/gdnative_interface.cpp @@ -854,14 +854,21 @@ static GDNativeMethodBindPtr gdnative_classdb_get_method_bind(const char *p_clas return (GDNativeMethodBindPtr)mb; } -static GDNativeClassConstructor gdnative_classdb_get_constructor(const char *p_classname) { +static GDNativeClassConstructor gdnative_classdb_get_constructor(const char *p_classname, GDNativeExtensionPtr *r_extension) { ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(StringName(p_classname)); if (class_info) { + if (r_extension) { + *r_extension = class_info->native_extension; + } return (GDNativeClassConstructor)class_info->creation_func; } return nullptr; } +static GDNativeObjectPtr gdnative_classdb_construct_object(GDNativeClassConstructor p_constructor, GDNativeExtensionPtr p_extension) { + return (GDNativeObjectPtr)ClassDB::construct_object((Object * (*)()) p_constructor, (ObjectNativeExtension *)p_extension); +} + static void *gdnative_classdb_get_class_tag(const char *p_classname) { ClassDB::ClassInfo *class_info = ClassDB::classes.getptr(p_classname); return class_info ? class_info->class_ptr : nullptr; @@ -1010,6 +1017,7 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) { /* CLASSDB */ gdni.classdb_get_constructor = gdnative_classdb_get_constructor; + gdni.classdb_construct_object = gdnative_classdb_construct_object; gdni.classdb_get_method_bind = gdnative_classdb_get_method_bind; gdni.classdb_get_class_tag = gdnative_classdb_get_class_tag; diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h index df735db9b6..73f78bde54 100644 --- a/core/extension/gdnative_interface.h +++ b/core/extension/gdnative_interface.h @@ -137,6 +137,7 @@ typedef void *GDNativeStringNamePtr; typedef void *GDNativeStringPtr; typedef void *GDNativeObjectPtr; typedef void *GDNativeTypePtr; +typedef void *GDNativeExtensionPtr; typedef void *GDNativeMethodBindPtr; typedef int64_t GDNativeInt; typedef uint8_t GDNativeBool; @@ -431,7 +432,8 @@ typedef struct { /* CLASSDB */ - GDNativeClassConstructor (*classdb_get_constructor)(const char *p_classname); + GDNativeClassConstructor (*classdb_get_constructor)(const char *p_classname, GDNativeExtensionPtr *r_extension); + GDNativeObjectPtr (*classdb_construct_object)(GDNativeClassConstructor p_constructor, GDNativeExtensionPtr p_extension); GDNativeMethodBindPtr (*classdb_get_method_bind)(const char *p_classname, const char *p_methodname, GDNativeInt p_hash); void *(*classdb_get_class_tag)(const char *p_classname); diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index 50b2099236..c2a9d30fff 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -1557,9 +1557,13 @@ bool InputEventShortcut::is_pressed() const { } String InputEventShortcut::as_text() const { + ERR_FAIL_COND_V(shortcut.is_null(), "None"); + return vformat(RTR("Input Event with Shortcut=%s"), shortcut->get_as_text()); } String InputEventShortcut::to_string() { + ERR_FAIL_COND_V(shortcut.is_null(), "None"); + return vformat("InputEventShortcut: shortcut=%s", shortcut->get_as_text()); } diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index 8bec80a99e..1ec4299093 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -467,7 +467,7 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { // ///// UI Text Input Shortcuts ///// inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(KEY_SPACE | KEY_MASK_CMD)); + inputs.push_back(InputEventKey::create_reference(KEY_SPACE | KEY_MASK_CTRL)); default_builtin_cache.insert("ui_text_completion_query", inputs); inputs = List<Ref<InputEvent>>(); diff --git a/core/io/image.cpp b/core/io/image.cpp index 3112dd217f..c70f4b86bd 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -2506,7 +2506,7 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po clipped_src_rect.position.y = ABS(p_dest.y); } - if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) { + if (clipped_src_rect.has_no_area()) { return; } @@ -2561,7 +2561,7 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co clipped_src_rect.position.y = ABS(p_dest.y); } - if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) { + if (clipped_src_rect.has_no_area()) { return; } @@ -2615,7 +2615,7 @@ void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const P clipped_src_rect.position.y = ABS(p_dest.y); } - if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) { + if (clipped_src_rect.has_no_area()) { return; } @@ -2664,7 +2664,7 @@ void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, c clipped_src_rect.position.y = ABS(p_dest.y); } - if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0) { + if (clipped_src_rect.has_no_area()) { return; } diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 8da44fd290..87d2b66e5b 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -138,6 +138,7 @@ Error PacketPeer::_get_packet_error() const { void PacketPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_var", "allow_objects"), &PacketPeer::_bnd_get_var, DEFVAL(false)); ClassDB::bind_method(D_METHOD("put_var", "var", "full_objects"), &PacketPeer::put_var, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_packet"), &PacketPeer::_get_packet); ClassDB::bind_method(D_METHOD("put_packet", "buffer"), &PacketPeer::_put_packet); ClassDB::bind_method(D_METHOD("get_packet_error"), &PacketPeer::_get_packet_error); @@ -151,6 +152,51 @@ void PacketPeer::_bind_methods() { /***************/ +int PacketPeerExtension::get_available_packet_count() const { + int count; + if (GDVIRTUAL_CALL(_get_available_packet_count, count)) { + return count; + } + WARN_PRINT_ONCE("PacketPeerExtension::_get_available_packet_count is unimplemented!"); + return -1; +} + +Error PacketPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + int err; + if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("PacketPeerExtension::_get_packet_native is unimplemented!"); + return FAILED; +} + +Error PacketPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + int err; + if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("PacketPeerExtension::_put_packet_native is unimplemented!"); + return FAILED; +} + +int PacketPeerExtension::get_max_packet_size() const { + int size; + if (GDVIRTUAL_CALL(_get_max_packet_size, size)) { + return size; + } + WARN_PRINT_ONCE("PacketPeerExtension::_get_max_packet_size is unimplemented!"); + return 0; +} + +void PacketPeerExtension::_bind_methods() { + GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size"); + GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size"); + GDVIRTUAL_BIND(_get_available_packet_count); + GDVIRTUAL_BIND(_get_max_packet_size); +} + +/***************/ + void PacketPeerStream::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stream_peer", "peer"), &PacketPeerStream::set_stream_peer); ClassDB::bind_method(D_METHOD("get_stream_peer"), &PacketPeerStream::get_stream_peer); diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h index 9a345af3d0..bc1f4aaabf 100644 --- a/core/io/packet_peer.h +++ b/core/io/packet_peer.h @@ -35,6 +35,10 @@ #include "core/object/class_db.h" #include "core/templates/ring_buffer.h" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" +#include "core/variant/native_ptr.h" + class PacketPeer : public RefCounted { GDCLASS(PacketPeer, RefCounted); @@ -73,6 +77,25 @@ public: ~PacketPeer() {} }; +class PacketPeerExtension : public PacketPeer { + GDCLASS(PacketPeerExtension, PacketPeer); + +protected: + static void _bind_methods(); + +public: + virtual int get_available_packet_count() const override; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; + virtual int get_max_packet_size() const override; + + /* GDExtension */ + GDVIRTUAL0RC(int, _get_available_packet_count); + GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); + GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int); + GDVIRTUAL0RC(int, _get_max_packet_size); +}; + class PacketPeerStream : public PacketPeer { GDCLASS(PacketPeerStream, PacketPeer); diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp index 97d683f415..290a71043c 100644 --- a/core/io/resource_uid.cpp +++ b/core/io/resource_uid.cpp @@ -126,8 +126,7 @@ String ResourceUID::get_id_path(ID p_id) const { MutexLock l(mutex); ERR_FAIL_COND_V(!unique_ids.has(p_id), String()); const CharString &cs = unique_ids[p_id].cs; - String s(cs.ptr()); - return s; + return String::utf8(cs.ptr()); } void ResourceUID::remove_id(ID p_id) { MutexLock l(mutex); diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 27f8d4e88f..8ab025dda1 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -410,6 +410,63 @@ void StreamPeer::_bind_methods() { //////////////////////////////// +int StreamPeerExtension::get_available_bytes() const { + int count; + if (GDVIRTUAL_CALL(_get_available_bytes, count)) { + return count; + } + WARN_PRINT_ONCE("StreamPeerExtension::_get_available_bytes is unimplemented!"); + return -1; +} + +Error StreamPeerExtension::get_data(uint8_t *r_buffer, int p_bytes) { + int err; + int received = 0; + if (GDVIRTUAL_CALL(_get_data, r_buffer, p_bytes, &received, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("StreamPeerExtension::_get_data is unimplemented!"); + return FAILED; +} + +Error StreamPeerExtension::get_partial_data(uint8_t *r_buffer, int p_bytes, int &r_received) { + int err; + if (GDVIRTUAL_CALL(_get_partial_data, r_buffer, p_bytes, &r_received, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("StreamPeerExtension::_get_partial_data is unimplemented!"); + return FAILED; +} + +Error StreamPeerExtension::put_data(const uint8_t *p_data, int p_bytes) { + int err; + int sent = 0; + if (GDVIRTUAL_CALL(_put_data, p_data, p_bytes, &sent, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("StreamPeerExtension::_put_data is unimplemented!"); + return FAILED; +} + +Error StreamPeerExtension::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { + int err; + if (GDVIRTUAL_CALL(_put_data, p_data, p_bytes, &r_sent, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("StreamPeerExtension::_put_partial_data is unimplemented!"); + return FAILED; +} + +void StreamPeerExtension::_bind_methods() { + GDVIRTUAL_BIND(_get_data, "r_buffer", "r_bytes", "r_received"); + GDVIRTUAL_BIND(_get_partial_data, "r_buffer", "r_bytes", "r_received"); + GDVIRTUAL_BIND(_put_data, "p_data", "p_bytes", "r_sent"); + GDVIRTUAL_BIND(_put_partial_data, "p_data", "p_bytes", "r_sent"); + GDVIRTUAL_BIND(_get_available_bytes); +} + +//////////////////////////////// + void StreamPeerBuffer::_bind_methods() { ClassDB::bind_method(D_METHOD("seek", "position"), &StreamPeerBuffer::seek); ClassDB::bind_method(D_METHOD("get_size"), &StreamPeerBuffer::get_size); diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h index effc3850af..89432951c5 100644 --- a/core/io/stream_peer.h +++ b/core/io/stream_peer.h @@ -33,6 +33,10 @@ #include "core/object/ref_counted.h" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" +#include "core/variant/native_ptr.h" + class StreamPeer : public RefCounted { GDCLASS(StreamPeer, RefCounted); OBJ_CATEGORY("Networking"); @@ -58,6 +62,7 @@ public: virtual int get_available_bytes() const = 0; + /* helpers */ void set_big_endian(bool p_big_endian); bool is_big_endian_enabled() const; @@ -92,6 +97,26 @@ public: StreamPeer() {} }; +class StreamPeerExtension : public StreamPeer { + GDCLASS(StreamPeerExtension, StreamPeer); + +protected: + static void _bind_methods(); + +public: + virtual Error put_data(const uint8_t *p_data, int p_bytes) override; + virtual Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override; + virtual Error get_data(uint8_t *p_buffer, int p_bytes) override; + virtual Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override; + virtual int get_available_bytes() const override; + + GDVIRTUAL3R(int, _put_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>); + GDVIRTUAL3R(int, _put_partial_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>); + GDVIRTUAL3R(int, _get_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>); + GDVIRTUAL3R(int, _get_partial_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>); + GDVIRTUAL0RC(int, _get_available_bytes); +}; + class StreamPeerBuffer : public StreamPeer { GDCLASS(StreamPeerBuffer, StreamPeer); diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp index 33aa65f15d..51a1309f0e 100644 --- a/core/math/aabb.cpp +++ b/core/math/aabb.cpp @@ -52,8 +52,8 @@ void AABB::merge_with(const AABB &p_aabb) { beg_1 = position; beg_2 = p_aabb.position; - end_1 = Vector3(size.x, size.y, size.z) + beg_1; - end_2 = Vector3(p_aabb.size.x, p_aabb.size.y, p_aabb.size.z) + beg_2; + end_1 = size + beg_1; + end_2 = p_aabb.size + beg_2; min.x = (beg_1.x < beg_2.x) ? beg_1.x : beg_2.x; min.y = (beg_1.y < beg_2.y) ? beg_1.y : beg_2.y; diff --git a/core/math/face3.cpp b/core/math/face3.cpp index 9af3f868d2..045ab67ce8 100644 --- a/core/math/face3.cpp +++ b/core/math/face3.cpp @@ -151,8 +151,8 @@ Face3::Side Face3::get_side_of(const Face3 &p_face, ClockDirection p_clock_dir) } Vector3 Face3::get_random_point_inside() const { - real_t a = Math::random(0, 1); - real_t b = Math::random(0, 1); + real_t a = Math::random(0.0, 1.0); + real_t b = Math::random(0.0, 1.0); if (a > b) { SWAP(a, b); } diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index 060b619892..496a557844 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -279,7 +279,7 @@ Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, const } //construct matrix - Transform2D res(Math::atan2(v.y, v.x), p1.lerp(p2, p_c)); + Transform2D res(v.angle(), p1.lerp(p2, p_c)); res.scale_basis(s1.lerp(s2, p_c)); return res; } diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp index b53dc05a00..16e43d7d06 100644 --- a/core/math/vector2.cpp +++ b/core/math/vector2.cpp @@ -79,7 +79,7 @@ real_t Vector2::angle_to(const Vector2 &p_vector2) const { } real_t Vector2::angle_to_point(const Vector2 &p_vector2) const { - return Math::atan2(y - p_vector2.y, x - p_vector2.x); + return (*this - p_vector2).angle(); } real_t Vector2::dot(const Vector2 &p_other) const { diff --git a/core/multiplayer/multiplayer_peer.cpp b/core/multiplayer/multiplayer_peer.cpp index 40847102d8..3c33948e2f 100644 --- a/core/multiplayer/multiplayer_peer.cpp +++ b/core/multiplayer/multiplayer_peer.cpp @@ -53,6 +53,30 @@ uint32_t MultiplayerPeer::generate_unique_id() const { return hash; } +void MultiplayerPeer::set_transfer_channel(int p_channel) { + transfer_channel = p_channel; +} + +int MultiplayerPeer::get_transfer_channel() const { + return transfer_channel; +} + +void MultiplayerPeer::set_transfer_mode(Multiplayer::TransferMode p_mode) { + transfer_mode = p_mode; +} + +Multiplayer::TransferMode MultiplayerPeer::get_transfer_mode() const { + return transfer_mode; +} + +void MultiplayerPeer::set_refuse_new_connections(bool p_enable) { + refuse_connections = p_enable; +} + +bool MultiplayerPeer::is_refusing_new_connections() const { + return refuse_connections; +} + void MultiplayerPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &MultiplayerPeer::set_transfer_channel); ClassDB::bind_method(D_METHOD("get_transfer_channel"), &MultiplayerPeer::get_transfer_channel); @@ -88,3 +112,160 @@ void MultiplayerPeer::_bind_methods() { ADD_SIGNAL(MethodInfo("connection_succeeded")); ADD_SIGNAL(MethodInfo("connection_failed")); } + +/*************/ + +int MultiplayerPeerExtension::get_available_packet_count() const { + int count; + if (GDVIRTUAL_CALL(_get_available_packet_count, count)) { + return count; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_available_packet_count is unimplemented!"); + return -1; +} + +Error MultiplayerPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + int err; + if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_native is unimplemented!"); + return FAILED; +} + +Error MultiplayerPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + int err; + if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_put_packet_native is unimplemented!"); + return FAILED; +} + +int MultiplayerPeerExtension::get_max_packet_size() const { + int size; + if (GDVIRTUAL_CALL(_get_max_packet_size, size)) { + return size; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_max_packet_size is unimplemented!"); + return 0; +} + +void MultiplayerPeerExtension::set_transfer_channel(int p_channel) { + if (GDVIRTUAL_CALL(_set_transfer_channel, p_channel)) { + return; + } + MultiplayerPeer::set_transfer_channel(p_channel); +} + +int MultiplayerPeerExtension::get_transfer_channel() const { + int channel; + if (GDVIRTUAL_CALL(_get_transfer_channel, channel)) { + return channel; + } + return MultiplayerPeer::get_transfer_channel(); +} + +void MultiplayerPeerExtension::set_transfer_mode(Multiplayer::TransferMode p_mode) { + if (GDVIRTUAL_CALL(_set_transfer_mode, p_mode)) { + return; + } + MultiplayerPeer::set_transfer_mode(p_mode); +} + +Multiplayer::TransferMode MultiplayerPeerExtension::get_transfer_mode() const { + int mode; + if (GDVIRTUAL_CALL(_get_transfer_mode, mode)) { + return (Multiplayer::TransferMode)mode; + } + return MultiplayerPeer::get_transfer_mode(); +} + +void MultiplayerPeerExtension::set_target_peer(int p_peer_id) { + if (GDVIRTUAL_CALL(_set_target_peer, p_peer_id)) { + return; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_set_target_peer is unimplemented!"); +} + +int MultiplayerPeerExtension::get_packet_peer() const { + int peer; + if (GDVIRTUAL_CALL(_get_packet_peer, peer)) { + return peer; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_peer is unimplemented!"); + return 0; +} + +bool MultiplayerPeerExtension::is_server() const { + bool server; + if (GDVIRTUAL_CALL(_is_server, server)) { + return server; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_is_server is unimplemented!"); + return false; +} + +void MultiplayerPeerExtension::poll() { + int err; + if (GDVIRTUAL_CALL(_poll, err)) { + return; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_poll is unimplemented!"); +} + +int MultiplayerPeerExtension::get_unique_id() const { + int id; + if (GDVIRTUAL_CALL(_get_unique_id, id)) { + return id; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_unique_id is unimplemented!"); + return 0; +} + +void MultiplayerPeerExtension::set_refuse_new_connections(bool p_enable) { + if (GDVIRTUAL_CALL(_set_refuse_new_connections, p_enable)) { + return; + } + MultiplayerPeer::set_refuse_new_connections(p_enable); +} + +bool MultiplayerPeerExtension::is_refusing_new_connections() const { + bool refusing; + if (GDVIRTUAL_CALL(_is_refusing_new_connections, refusing)) { + return refusing; + } + return MultiplayerPeer::is_refusing_new_connections(); +} + +MultiplayerPeer::ConnectionStatus MultiplayerPeerExtension::get_connection_status() const { + int status; + if (GDVIRTUAL_CALL(_get_connection_status, status)) { + return (ConnectionStatus)status; + } + WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_connection_status is unimplemented!"); + return CONNECTION_DISCONNECTED; +} + +void MultiplayerPeerExtension::_bind_methods() { + GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size"); + GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size"); + GDVIRTUAL_BIND(_get_available_packet_count); + GDVIRTUAL_BIND(_get_max_packet_size); + + GDVIRTUAL_BIND(_set_transfer_channel, "p_channel"); + GDVIRTUAL_BIND(_get_transfer_channel); + + GDVIRTUAL_BIND(_set_transfer_mode, "p_mode"); + GDVIRTUAL_BIND(_get_transfer_mode); + + GDVIRTUAL_BIND(_set_target_peer, "p_peer"); + + GDVIRTUAL_BIND(_get_packet_peer); + GDVIRTUAL_BIND(_is_server); + GDVIRTUAL_BIND(_poll); + GDVIRTUAL_BIND(_get_unique_id); + GDVIRTUAL_BIND(_set_refuse_new_connections, "p_enable"); + GDVIRTUAL_BIND(_is_refusing_new_connections); + GDVIRTUAL_BIND(_get_connection_status); +} diff --git a/core/multiplayer/multiplayer_peer.h b/core/multiplayer/multiplayer_peer.h index ba00c3b41b..126ba9e645 100644 --- a/core/multiplayer/multiplayer_peer.h +++ b/core/multiplayer/multiplayer_peer.h @@ -34,12 +34,21 @@ #include "core/io/packet_peer.h" #include "core/multiplayer/multiplayer.h" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" +#include "core/variant/native_ptr.h" + class MultiplayerPeer : public PacketPeer { GDCLASS(MultiplayerPeer, PacketPeer); protected: static void _bind_methods(); +private: + int transfer_channel = 0; + Multiplayer::TransferMode transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE; + bool refuse_connections = false; + public: enum { TARGET_PEER_BROADCAST = 0, @@ -52,10 +61,13 @@ public: CONNECTION_CONNECTED, }; - virtual void set_transfer_channel(int p_channel) = 0; - virtual int get_transfer_channel() const = 0; - virtual void set_transfer_mode(Multiplayer::TransferMode p_mode) = 0; - virtual Multiplayer::TransferMode get_transfer_mode() const = 0; + virtual void set_transfer_channel(int p_channel); + virtual int get_transfer_channel() const; + virtual void set_transfer_mode(Multiplayer::TransferMode p_mode); + virtual Multiplayer::TransferMode get_transfer_mode() const; + virtual void set_refuse_new_connections(bool p_enable); + virtual bool is_refusing_new_connections() const; + virtual void set_target_peer(int p_peer_id) = 0; virtual int get_packet_peer() const = 0; @@ -66,15 +78,67 @@ public: virtual int get_unique_id() const = 0; - virtual void set_refuse_new_connections(bool p_enable) = 0; - virtual bool is_refusing_new_connections() const = 0; - virtual ConnectionStatus get_connection_status() const = 0; + uint32_t generate_unique_id() const; MultiplayerPeer() {} }; -VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus) +VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus); + +class MultiplayerPeerExtension : public MultiplayerPeer { + GDCLASS(MultiplayerPeerExtension, MultiplayerPeer); + +protected: + static void _bind_methods(); + +public: + /* PacketPeer */ + virtual int get_available_packet_count() const override; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; + virtual int get_max_packet_size() const override; + + /* MultiplayerPeer */ + virtual void set_transfer_channel(int p_channel) override; + virtual int get_transfer_channel() const override; + virtual void set_transfer_mode(Multiplayer::TransferMode p_mode) override; + virtual Multiplayer::TransferMode get_transfer_mode() const override; + virtual void set_target_peer(int p_peer_id) override; + + virtual int get_packet_peer() const override; + + virtual bool is_server() const override; + + virtual void poll() override; + + virtual int get_unique_id() const override; + + virtual void set_refuse_new_connections(bool p_enable) override; + virtual bool is_refusing_new_connections() const override; + + virtual ConnectionStatus get_connection_status() const override; + + /* PacketPeer GDExtension */ + GDVIRTUAL0RC(int, _get_available_packet_count); + GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); + GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int); + GDVIRTUAL0RC(int, _get_max_packet_size); + + /* MultiplayerPeer GDExtension */ + GDVIRTUAL1(_set_transfer_channel, int); + GDVIRTUAL0RC(int, _get_transfer_channel); + GDVIRTUAL1(_set_transfer_mode, int); + GDVIRTUAL0RC(int, _get_transfer_mode); + GDVIRTUAL1(_set_target_peer, int); + GDVIRTUAL0RC(int, _get_packet_peer); + GDVIRTUAL0RC(bool, _is_server); + GDVIRTUAL0R(int, _poll); + GDVIRTUAL0RC(int, _get_unique_id); + GDVIRTUAL1(_set_refuse_new_connections, bool); + GDVIRTUAL0RC(bool, _is_refusing_new_connections); + GDVIRTUAL0RC(int, _get_connection_status); +}; #endif // NETWORKED_MULTIPLAYER_PEER_H diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 8e92340c1e..8ba46e49eb 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -545,6 +545,15 @@ Object *ClassDB::instantiate(const StringName &p_class) { return ti->creation_func(); } +Object *ClassDB::construct_object(Object *(*p_create_func)(), ObjectNativeExtension *p_extension) { + if (p_extension) { + initializing_with_extension = true; + initializing_extension = p_extension; + initializing_extension_instance = p_extension->create_instance(p_extension->class_userdata); + } + return p_create_func(); +} + bool ClassDB::can_instantiate(const StringName &p_class) { OBJTYPE_RLOCK; diff --git a/core/object/class_db.h b/core/object/class_db.h index e89c7fffd7..3a1cbf8559 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -234,6 +234,7 @@ public: static bool is_parent_class(const StringName &p_class, const StringName &p_inherits); static bool can_instantiate(const StringName &p_class); static Object *instantiate(const StringName &p_class); + static Object *construct_object(Object *(*p_create_func)(), ObjectNativeExtension *p_extension); static void instance_get_native_extension_data(ObjectNativeExtension **r_extension, GDExtensionClassInstancePtr *r_extension_instance, Object *p_base); static APIType get_api_type(const StringName &p_class); diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 3a037f9dd1..e33c21cc00 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -169,11 +169,13 @@ void register_core_types() { GDREGISTER_VIRTUAL_CLASS(IP); GDREGISTER_VIRTUAL_CLASS(StreamPeer); + GDREGISTER_CLASS(StreamPeerExtension); GDREGISTER_CLASS(StreamPeerBuffer); GDREGISTER_CLASS(StreamPeerTCP); GDREGISTER_CLASS(TCPServer); GDREGISTER_VIRTUAL_CLASS(PacketPeer); + GDREGISTER_CLASS(PacketPeerExtension); GDREGISTER_CLASS(PacketPeerStream); GDREGISTER_CLASS(PacketPeerUDP); GDREGISTER_CLASS(UDPServer); @@ -197,6 +199,7 @@ void register_core_types() { ResourceLoader::add_resource_format_loader(resource_format_loader_crypto); GDREGISTER_VIRTUAL_CLASS(MultiplayerPeer); + GDREGISTER_VIRTUAL_CLASS(MultiplayerPeerExtension); GDREGISTER_VIRTUAL_CLASS(MultiplayerReplicator); GDREGISTER_CLASS(MultiplayerAPI); GDREGISTER_CLASS(MainLoop); diff --git a/core/templates/pooled_list.h b/core/templates/pooled_list.h index b4a6d2d1dd..b139dadb75 100644 --- a/core/templates/pooled_list.h +++ b/core/templates/pooled_list.h @@ -28,13 +28,16 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#pragma once +#ifndef POOLED_LIST_H +#define POOLED_LIST_H + +#include "core/templates/local_vector.h" // Simple template to provide a pool with O(1) allocate and free. // The freelist could alternatively be a linked list placed within the unused elements // to use less memory, however a separate freelist is probably more cache friendly. - -// NOTE : Take great care when using this with non POD types. The construction and destruction +// +// NOTE: Take great care when using this with non POD types. The construction and destruction // is done in the LocalVector, NOT as part of the pool. So requesting a new item does not guarantee // a constructor is run, and free does not guarantee a destructor. // You should generally handle clearing @@ -42,9 +45,6 @@ // This is by design for fastest use in the BVH. If you want a more general pool // that does call constructors / destructors on request / free, this should probably be // a separate template. - -#include "core/templates/local_vector.h" - template <class T, bool force_trivial = false> class PooledList { LocalVector<T, uint32_t, force_trivial> list; @@ -93,3 +93,5 @@ public: _used_size--; } }; + +#endif // POOLED_LIST_H diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp index f487e718f4..dcded6e61f 100644 --- a/core/variant/callable.cpp +++ b/core/variant/callable.cpp @@ -377,11 +377,11 @@ Error Signal::emit(const Variant **p_arguments, int p_argcount) const { return obj->emit_signal(name, p_arguments, p_argcount); } -Error Signal::connect(const Callable &p_callable, const Vector<Variant> &p_binds, uint32_t p_flags) { +Error Signal::connect(const Callable &p_callable, uint32_t p_flags) { Object *object = get_object(); ERR_FAIL_COND_V(!object, ERR_UNCONFIGURED); - return object->connect(name, p_callable, p_binds, p_flags); + return object->connect(name, p_callable, varray(), p_flags); } void Signal::disconnect(const Callable &p_callable) { diff --git a/core/variant/callable.h b/core/variant/callable.h index 52094af3aa..de886492ea 100644 --- a/core/variant/callable.h +++ b/core/variant/callable.h @@ -159,7 +159,7 @@ public: operator String() const; Error emit(const Variant **p_arguments, int p_argcount) const; - Error connect(const Callable &p_callable, const Vector<Variant> &p_binds = Vector<Variant>(), uint32_t p_flags = 0); + Error connect(const Callable &p_callable, uint32_t p_flags = 0); void disconnect(const Callable &p_callable); bool is_connected(const Callable &p_callable) const; diff --git a/core/variant/native_ptr.h b/core/variant/native_ptr.h index b4ec0df7d6..b7e8d92f62 100644 --- a/core/variant/native_ptr.h +++ b/core/variant/native_ptr.h @@ -117,6 +117,7 @@ GDVIRTUAL_NATIVE_PTR(char16_t) GDVIRTUAL_NATIVE_PTR(char32_t) GDVIRTUAL_NATIVE_PTR(wchar_t) GDVIRTUAL_NATIVE_PTR(uint8_t) +GDVIRTUAL_NATIVE_PTR(uint8_t *) GDVIRTUAL_NATIVE_PTR(int8_t) GDVIRTUAL_NATIVE_PTR(uint16_t) GDVIRTUAL_NATIVE_PTR(int16_t) diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index c3fe4117ac..32d6778a2b 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1690,7 +1690,7 @@ static void _register_variant_builtin_methods() { bind_method(Signal, get_object_id, sarray(), varray()); bind_method(Signal, get_name, sarray(), varray()); - bind_method(Signal, connect, sarray("callable", "binds", "flags"), varray(Array(), 0)); + bind_method(Signal, connect, sarray("callable", "flags"), varray(0)); bind_method(Signal, disconnect, sarray("callable"), varray()); bind_method(Signal, is_connected, sarray("callable"), varray()); bind_method(Signal, get_connections, sarray(), varray()); diff --git a/doc/classes/AnimatableBody2D.xml b/doc/classes/AnimatableBody2D.xml index e58f4bd692..bc169cf9c9 100644 --- a/doc/classes/AnimatableBody2D.xml +++ b/doc/classes/AnimatableBody2D.xml @@ -11,7 +11,7 @@ <tutorials> </tutorials> <members> - <member name="sync_to_physics" type="bool" setter="set_sync_to_physics" getter="is_sync_to_physics_enabled" default="false"> + <member name="sync_to_physics" type="bool" setter="set_sync_to_physics" getter="is_sync_to_physics_enabled" default="true"> If [code]true[/code], the body's movement will be synchronized to the physics frame. This is useful when animating movement via [AnimationPlayer], for example on moving platforms. Do [b]not[/b] use together with [method PhysicsBody2D.move_and_collide]. </member> </members> diff --git a/doc/classes/AnimatableBody3D.xml b/doc/classes/AnimatableBody3D.xml index 71a48a5aa6..86cff38c51 100644 --- a/doc/classes/AnimatableBody3D.xml +++ b/doc/classes/AnimatableBody3D.xml @@ -14,7 +14,7 @@ <link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link> </tutorials> <members> - <member name="sync_to_physics" type="bool" setter="set_sync_to_physics" getter="is_sync_to_physics_enabled" default="false"> + <member name="sync_to_physics" type="bool" setter="set_sync_to_physics" getter="is_sync_to_physics_enabled" default="true"> If [code]true[/code], the body's movement will be synchronized to the physics frame. This is useful when animating movement via [AnimationPlayer], for example on moving platforms. Do [b]not[/b] use together with [method PhysicsBody3D.move_and_collide]. </member> </members> diff --git a/doc/classes/CharacterBody2D.xml b/doc/classes/CharacterBody2D.xml index 882aa8bd71..35702070be 100644 --- a/doc/classes/CharacterBody2D.xml +++ b/doc/classes/CharacterBody2D.xml @@ -133,11 +133,11 @@ <member name="floor_max_angle" type="float" setter="set_floor_max_angle" getter="get_floor_max_angle" default="0.785398"> Maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall, when calling [method move_and_slide]. The default value equals 45 degrees. </member> - <member name="floor_snap_length" type="float" setter="set_floor_snap_length" getter="get_floor_snap_length" default="0.0"> + <member name="floor_snap_length" type="float" setter="set_floor_snap_length" getter="get_floor_snap_length" default="1.0"> Sets a snapping distance. When set to a value different from [code]0.0[/code], the body is kept attached to slopes when calling [method move_and_slide]. The snapping vector is determined by the given distance along the opposite direction of the [member up_direction]. As long as the snapping vector is in contact with the ground and the body moves against `up_direction`, the body will remain attached to the surface. Snapping is not applied if the body moves along `up_direction`, so it will be able to detach from the ground when jumping. </member> - <member name="floor_stop_on_slope" type="bool" setter="set_floor_stop_on_slope_enabled" getter="is_floor_stop_on_slope_enabled" default="false"> + <member name="floor_stop_on_slope" type="bool" setter="set_floor_stop_on_slope_enabled" getter="is_floor_stop_on_slope_enabled" default="true"> If [code]true[/code], the body will not slide on floor's slopes when you include gravity in [code]linear_velocity[/code] when calling [method move_and_slide] and the body is standing still. </member> <member name="free_mode_min_slide_angle" type="float" setter="set_free_mode_min_slide_angle" getter="get_free_mode_min_slide_angle" default="0.261799"> diff --git a/doc/classes/CharacterBody3D.xml b/doc/classes/CharacterBody3D.xml index f13796bfe7..66d9940b60 100644 --- a/doc/classes/CharacterBody3D.xml +++ b/doc/classes/CharacterBody3D.xml @@ -146,7 +146,7 @@ Sets a snapping distance. When set to a value different from [code]0.0[/code], the body is kept attached to slopes when calling [method move_and_slide]. The snapping vector is determined by the given distance along the opposite direction of the [member up_direction]. As long as the snapping vector is in contact with the ground and the body moves against `up_direction`, the body will remain attached to the surface. Snapping is not applied if the body moves along `up_direction`, so it will be able to detach from the ground when jumping. </member> - <member name="floor_stop_on_slope" type="bool" setter="set_floor_stop_on_slope_enabled" getter="is_floor_stop_on_slope_enabled" default="false"> + <member name="floor_stop_on_slope" type="bool" setter="set_floor_stop_on_slope_enabled" getter="is_floor_stop_on_slope_enabled" default="true"> If [code]true[/code], the body will not slide on slopes when you include gravity in [code]linear_velocity[/code] when calling [method move_and_slide] and the body is standing still. </member> <member name="linear_velocity" type="Vector3" setter="set_linear_velocity" getter="get_linear_velocity" default="Vector3(0, 0, 0)"> diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml index 571ffd592a..f33016cc6c 100644 --- a/doc/classes/ColorPicker.xml +++ b/doc/classes/ColorPicker.xml @@ -5,6 +5,7 @@ </brief_description> <description> Displays a color picker widget. Useful for selecting a color from an RGB/RGBA colorspace. + [b]Note:[/b] This control is the color picker widget itself. You can use a [ColorPickerButton] instead if you need a button that brings up a [ColorPicker] in a pop-up. </description> <tutorials> <link title="Tween Demo">https://godotengine.org/asset-library/asset/146</link> diff --git a/doc/classes/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml index dcef1c55f7..1cdfbd760e 100644 --- a/doc/classes/ColorPickerButton.xml +++ b/doc/classes/ColorPickerButton.xml @@ -6,6 +6,7 @@ <description> Encapsulates a [ColorPicker] making it accessible by pressing a button. Pressing the button will toggle the [ColorPicker] visibility. See also [BaseButton] which contains common properties and methods associated with this node. + [b]Note:[/b] By default, the button may not be wide enough for the color preview swatch to be visible. Make sure to set [member Control.rect_min_size] to a big enough value to give the button enough space. </description> <tutorials> <link title="GUI Drag And Drop Demo">https://godotengine.org/asset-library/asset/133</link> diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml index 6e22c58024..f05a216301 100644 --- a/doc/classes/Engine.xml +++ b/doc/classes/Engine.xml @@ -147,6 +147,20 @@ Returns [code]true[/code] if a singleton with given [code]name[/code] exists in global scope. </description> </method> + <method name="is_editor_hint" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if the script is currently running inside the editor, [code]false[/code] otherwise. This is useful for [code]@tool[/code] scripts to conditionally draw editor helpers, or prevent accidentally running "game" code that would affect the scene state while in the editor: + [codeblock] + if Engine.is_editor_hint(): + draw_gizmos() + else: + simulate_physics() + [/codeblock] + See [url=https://docs.godotengine.org/en/latest/tutorials/plugins/running_code_in_the_editor.html]Running code in the editor[/url] in the documentation for more information. + [b]Note:[/b] To detect whether the script is run from an editor [i]build[/i] (e.g. when pressing [kbd]F5[/kbd]), use [method OS.has_feature] with the [code]"editor"[/code] argument instead. [code]OS.has_feature("editor")[/code] will evaluate to [code]true[/code] both when the code is running in the editor and when running the project from the editor, but it will evaluate to [code]false[/code] when the code is run from an exported project. + </description> + </method> <method name="is_in_physics_frame" qualifiers="const"> <return type="bool" /> <description> @@ -168,17 +182,6 @@ </method> </methods> <members> - <member name="editor_hint" type="bool" setter="set_editor_hint" getter="is_editor_hint" default="true"> - If [code]true[/code], the script is currently running inside the editor. This is useful for [code]@tool[/code] scripts to conditionally draw editor helpers, or prevent accidentally running "game" code that would affect the scene state while in the editor: - [codeblock] - if Engine.editor_hint: - draw_gizmos() - else: - simulate_physics() - [/codeblock] - See [url=https://docs.godotengine.org/en/latest/tutorials/plugins/running_code_in_the_editor.html]Running code in the editor[/url] in the documentation for more information. - [b]Note:[/b] To detect whether the script is run from an editor [i]build[/i] (e.g. when pressing [kbd]F5[/kbd]), use [method OS.has_feature] with the [code]"editor"[/code] argument instead. [code]OS.has_feature("editor")[/code] will evaluate to [code]true[/code] both when the code is running in the editor and when running the project from the editor, but it will evaluate to [code]false[/code] when the code is run from an exported project. - </member> <member name="physics_jitter_fix" type="float" setter="set_physics_jitter_fix" getter="get_physics_jitter_fix" default="0.5"> Controls how much physics ticks are synchronized with real time. For 0 or less, the ticks are synchronized. Such values are recommended for network games, where clock synchronization matters. Higher values cause higher deviation of the in-game clock and real clock but smooth out framerate jitters. The default value of 0.5 should be fine for most; values above 2 could cause the game to react to dropped frames with a noticeable delay and are not recommended. [b]Note:[/b] For best results, when using a custom physics interpolation solution, the physics jitter fix should be disabled by setting [member physics_jitter_fix] to [code]0[/code]. diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml index 29aaf3c756..9f33c400f4 100644 --- a/doc/classes/HTTPClient.xml +++ b/doc/classes/HTTPClient.xml @@ -8,6 +8,7 @@ [b]Note:[/b] This client only needs to connect to a host once (see [method connect_to_host]) to send multiple requests. Because of this, methods that take URLs usually take just the part after the host instead of the full URL, as the client is already connected to a host. See [method request] for a full example and to get started. A [HTTPClient] should be reused between multiple requests or to connect to different hosts instead of creating one client per request. Supports SSL and SSL server certificate verification. HTTP status codes in the 2xx range indicate success, 3xx redirection (i.e. "try again, but over here"), 4xx something was wrong with the request, and 5xx something went wrong on the server's side. For more information on HTTP, see https://developer.mozilla.org/en-US/docs/Web/HTTP (or read RFC 2616 to get it straight from the source: https://tools.ietf.org/html/rfc2616). + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. [b]Note:[/b] It's recommended to use transport encryption (SSL/TLS) and to avoid sending sensitive information (such as login credentials) in HTTP GET URL parameters. Consider using HTTP POST requests or HTTP headers for such information instead. [b]Note:[/b] When performing HTTP requests from a project exported to HTML5, keep in mind the remote server may not allow requests from foreign origins due to [url=https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS]CORS[/url]. If you host the server in question, you should modify its backend to allow requests from foreign origins by adding the [code]Access-Control-Allow-Origin: *[/code] HTTP header. [b]Note:[/b] SSL/TLS support is currently limited to TLS 1.0, TLS 1.1, and TLS 1.2. Attempting to connect to a TLS 1.3-only server will return an error. diff --git a/doc/classes/HTTPRequest.xml b/doc/classes/HTTPRequest.xml index 00927b98c5..558e51aefe 100644 --- a/doc/classes/HTTPRequest.xml +++ b/doc/classes/HTTPRequest.xml @@ -7,6 +7,7 @@ A node with the ability to send HTTP requests. Uses [HTTPClient] internally. Can be used to make HTTP requests, i.e. download or upload files or web content via HTTP. [b]Warning:[/b] See the notes and warnings on [HTTPClient] for limitations, especially regarding SSL security. + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. [b]Example of contacting a REST API and printing one of its returned fields:[/b] [codeblocks] [gdscript] @@ -149,7 +150,6 @@ } [/csharp] [/codeblocks] - [b]Gzipped response bodies[/b]: HTTPRequest will automatically handle decompression of response bodies. A [code]Accept-Encoding[/code] header will be automatically added to each of your requests, unless one is already specified. Any response with a [code]Content-Encoding: gzip[/code] header will automatically be decompressed and delivered to you as uncompressed bytes. </description> <tutorials> diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index 834b5a41db..83e3f5b05a 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -82,6 +82,24 @@ Returns the scroll offset due to [member caret_column], as a number of characters. </description> </method> + <method name="get_selection_from_column" qualifiers="const"> + <return type="int" /> + <description> + Returns the selection begin column. + </description> + </method> + <method name="get_selection_to_column" qualifiers="const"> + <return type="int" /> + <description> + Returns the selection end column. + </description> + </method> + <method name="has_selection" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if the user has selected text. + </description> + </method> <method name="insert_text_at_caret"> <return type="void" /> <argument index="0" name="text" type="String" /> diff --git a/doc/classes/MultiplayerAPI.xml b/doc/classes/MultiplayerAPI.xml index 0da461dd61..e0da08f5bd 100644 --- a/doc/classes/MultiplayerAPI.xml +++ b/doc/classes/MultiplayerAPI.xml @@ -8,6 +8,7 @@ By default, [SceneTree] has a reference to this class that is used to provide multiplayer capabilities (i.e. RPCs) across the whole scene. It is possible to override the MultiplayerAPI instance used by specific Nodes by setting the [member Node.custom_multiplayer] property, effectively allowing to run both client and server in the same scene. [b]Note:[/b] The high-level multiplayer API protocol is an implementation detail and isn't meant to be used by non-Godot servers. It may change without notice. + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> </tutorials> diff --git a/doc/classes/MultiplayerPeer.xml b/doc/classes/MultiplayerPeer.xml index 411317cdc8..15517f7c2f 100644 --- a/doc/classes/MultiplayerPeer.xml +++ b/doc/classes/MultiplayerPeer.xml @@ -6,6 +6,7 @@ <description> Manages the connection to multiplayer peers. Assigns unique IDs to each client connected to the server. See also [MultiplayerAPI]. [b]Note:[/b] The high-level multiplayer API protocol is an implementation detail and isn't meant to be used by non-Godot servers. It may change without notice. + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> <link title="High-level multiplayer">https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link> @@ -52,14 +53,14 @@ </method> </methods> <members> - <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" default="true"> + <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" default="false"> If [code]true[/code], this [MultiplayerPeer] refuses new connections. </member> <member name="transfer_channel" type="int" setter="set_transfer_channel" getter="get_transfer_channel" default="0"> The channel to use to send packets. Many network APIs such as ENet and WebRTC allow the creation of multiple independent channels which behaves, in a way, like separate connections. This means that reliable data will only block delivery of other packets on that channel, and ordering will only be in respect to the channel the packet is being sent on. Using different channels to send [b]different and independent[/b] state updates is a common way to optimize network usage and decrease latency in fast-paced games. [b]Note:[/b] The default channel ([code]0[/code]) actually works as 3 separate channels (one for each [enum TransferMode]) so that [constant TRANSFER_MODE_RELIABLE] and [constant TRANSFER_MODE_ORDERED] does not interact with each other by default. Refer to the specific network API documentation (e.g. ENet or WebRTC) to learn how to set up channels correctly. </member> - <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" enum="TransferMode" default="0"> + <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" enum="TransferMode" default="2"> The manner in which to send packets to the [code]target_peer[/code]. See [enum TransferMode]. </member> </members> diff --git a/doc/classes/MultiplayerPeerExtension.xml b/doc/classes/MultiplayerPeerExtension.xml new file mode 100644 index 0000000000..d9c173a4a1 --- /dev/null +++ b/doc/classes/MultiplayerPeerExtension.xml @@ -0,0 +1,99 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="MultiplayerPeerExtension" inherits="MultiplayerPeer" version="4.0"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="_get_available_packet_count" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_connection_status" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_max_packet_size" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_packet" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="r_buffer" type="const void*" /> + <argument index="1" name="r_buffer_size" type="int32_t*" /> + <description> + </description> + </method> + <method name="_get_packet_peer" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_transfer_channel" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_transfer_mode" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_unique_id" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_is_refusing_new_connections" qualifiers="virtual const"> + <return type="bool" /> + <description> + </description> + </method> + <method name="_is_server" qualifiers="virtual const"> + <return type="bool" /> + <description> + </description> + </method> + <method name="_poll" qualifiers="virtual"> + <return type="int" /> + <description> + </description> + </method> + <method name="_put_packet" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="p_buffer" type="const void*" /> + <argument index="1" name="p_buffer_size" type="int" /> + <description> + </description> + </method> + <method name="_set_refuse_new_connections" qualifiers="virtual"> + <return type="void" /> + <argument index="0" name="p_enable" type="bool" /> + <description> + </description> + </method> + <method name="_set_target_peer" qualifiers="virtual"> + <return type="void" /> + <argument index="0" name="p_peer" type="int" /> + <description> + </description> + </method> + <method name="_set_transfer_channel" qualifiers="virtual"> + <return type="void" /> + <argument index="0" name="p_channel" type="int" /> + <description> + </description> + </method> + <method name="_set_transfer_mode" qualifiers="virtual"> + <return type="void" /> + <argument index="0" name="p_mode" type="int" /> + <description> + </description> + </method> + </methods> +</class> diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index e2ac8568c9..9d0d185c42 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -49,6 +49,7 @@ [/codeblocks] See [method execute] if you wish to run an external command and retrieve the results. [b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows. + [b]Note:[/b] On macOS, sandboxed applications are limited to run only embedded helper executables, specified during export. </description> </method> <method name="delay_msec" qualifiers="const"> @@ -119,6 +120,7 @@ [/csharp] [/codeblocks] [b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows. + [b]Note:[/b] On macOS, sandboxed applications are limited to run only embedded helper executables, specified during export. </description> </method> <method name="find_keycode_from_string" qualifiers="const"> diff --git a/doc/classes/PacketPeer.xml b/doc/classes/PacketPeer.xml index fb94209d72..7c02ccc250 100644 --- a/doc/classes/PacketPeer.xml +++ b/doc/classes/PacketPeer.xml @@ -5,6 +5,7 @@ </brief_description> <description> PacketPeer is an abstraction and base class for packet-based protocols (such as UDP). It provides an API for sending and receiving packets both as raw data or variables. This makes it easy to transfer data over a protocol, without having to encode data as low-level bytes or having to worry about network ordering. + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> </tutorials> diff --git a/doc/classes/PacketPeerDTLS.xml b/doc/classes/PacketPeerDTLS.xml index b05743fb5a..4d1c83afe7 100644 --- a/doc/classes/PacketPeerDTLS.xml +++ b/doc/classes/PacketPeerDTLS.xml @@ -5,6 +5,7 @@ </brief_description> <description> This class represents a DTLS peer connection. It can be used to connect to a DTLS server, and is returned by [method DTLSServer.take_connection]. + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. [b]Warning:[/b] SSL/TLS certificate revocation and certificate pinning are currently not supported. Revoked certificates are accepted as long as they are otherwise valid. If this is a concern, you may want to use automatically managed certificates with a short validity period. </description> <tutorials> diff --git a/doc/classes/PacketPeerExtension.xml b/doc/classes/PacketPeerExtension.xml new file mode 100644 index 0000000000..6804053484 --- /dev/null +++ b/doc/classes/PacketPeerExtension.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="PacketPeerExtension" inherits="PacketPeer" version="4.0"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="_get_available_packet_count" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_max_packet_size" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_packet" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="r_buffer" type="const void*" /> + <argument index="1" name="r_buffer_size" type="int32_t*" /> + <description> + </description> + </method> + <method name="_put_packet" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="p_buffer" type="const void*" /> + <argument index="1" name="p_buffer_size" type="int" /> + <description> + </description> + </method> + </methods> +</class> diff --git a/doc/classes/PacketPeerStream.xml b/doc/classes/PacketPeerStream.xml index a92aaf037e..1c5bec297c 100644 --- a/doc/classes/PacketPeerStream.xml +++ b/doc/classes/PacketPeerStream.xml @@ -5,6 +5,7 @@ </brief_description> <description> PacketStreamPeer provides a wrapper for working using packets over a stream. This allows for using packet based code with StreamPeers. PacketPeerStream implements a custom protocol over the StreamPeer, so the user should not read or write to the wrapped StreamPeer directly. + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> </tutorials> diff --git a/doc/classes/PacketPeerUDP.xml b/doc/classes/PacketPeerUDP.xml index ecaddc5b9f..9b77859b50 100644 --- a/doc/classes/PacketPeerUDP.xml +++ b/doc/classes/PacketPeerUDP.xml @@ -5,6 +5,7 @@ </brief_description> <description> UDP packet peer. Can be used to send raw UDP packets as well as [Variant]s. + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> </tutorials> diff --git a/doc/classes/PhysicsBody2D.xml b/doc/classes/PhysicsBody2D.xml index ee28500838..e108ab6298 100644 --- a/doc/classes/PhysicsBody2D.xml +++ b/doc/classes/PhysicsBody2D.xml @@ -25,11 +25,12 @@ </method> <method name="move_and_collide"> <return type="KinematicCollision2D" /> - <argument index="0" name="rel_vec" type="Vector2" /> + <argument index="0" name="linear_velocity" type="Vector2" /> <argument index="1" name="test_only" type="bool" default="false" /> <argument index="2" name="safe_margin" type="float" default="0.08" /> <description> - Moves the body along the vector [code]rel_vec[/code]. The body will stop if it collides. Returns a [KinematicCollision2D], which contains information about the collision. + Moves the body along the vector [code]linear_velocity[/code]. This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed. + The body will stop if it collides. Returns a [KinematicCollision2D], which contains information about the collision. If [code]test_only[/code] is [code]true[/code], the body does not move but the would-be collision information is given. [code]safe_margin[/code] is the extra margin used for collision recovery (see [member CharacterBody2D.collision/safe_margin] for more details). </description> @@ -44,11 +45,12 @@ <method name="test_move"> <return type="bool" /> <argument index="0" name="from" type="Transform2D" /> - <argument index="1" name="rel_vec" type="Vector2" /> + <argument index="1" name="linear_velocity" type="Vector2" /> <argument index="2" name="collision" type="KinematicCollision2D" default="null" /> <argument index="3" name="safe_margin" type="float" default="0.08" /> <description> - Checks for collisions without moving the body. Virtually sets the node's position, scale and rotation to that of the given [Transform2D], then tries to move the body along the vector [code]rel_vec[/code]. Returns [code]true[/code] if a collision would occur. + Checks for collisions without moving the body. This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed. + Virtually sets the node's position, scale and rotation to that of the given [Transform2D], then tries to move the body along the vector [code]linear_velocity[/code]. Returns [code]true[/code] if a collision would occur. [code]collision[/code] is an optional object of type [KinematicCollision2D], which contains additional information about the collision (should there be one). [code]safe_margin[/code] is the extra margin used for collision recovery (see [member CharacterBody2D.collision/safe_margin] for more details). </description> diff --git a/doc/classes/PhysicsBody3D.xml b/doc/classes/PhysicsBody3D.xml index 174e49ea2d..8718c0caa2 100644 --- a/doc/classes/PhysicsBody3D.xml +++ b/doc/classes/PhysicsBody3D.xml @@ -32,12 +32,13 @@ </method> <method name="move_and_collide"> <return type="KinematicCollision3D" /> - <argument index="0" name="rel_vec" type="Vector3" /> + <argument index="0" name="linear_velocity" type="Vector3" /> <argument index="1" name="test_only" type="bool" default="false" /> <argument index="2" name="safe_margin" type="float" default="0.001" /> <argument index="3" name="max_collisions" type="int" default="1" /> <description> - Moves the body along the vector [code]rel_vec[/code]. The body will stop if it collides. Returns a [KinematicCollision3D], which contains information about the collision. + Moves the body along the vector [code]linear_velocity[/code]. This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed. + The body will stop if it collides. Returns a [KinematicCollision3D], which contains information about the collision. If [code]test_only[/code] is [code]true[/code], the body does not move but the would-be collision information is given. [code]safe_margin[/code] is the extra margin used for collision recovery (see [member CharacterBody3D.collision/safe_margin] for more details). [code]max_collisions[/code] allows to retrieve more than one collision result. @@ -61,12 +62,13 @@ <method name="test_move"> <return type="bool" /> <argument index="0" name="from" type="Transform3D" /> - <argument index="1" name="rel_vec" type="Vector3" /> + <argument index="1" name="linear_velocity" type="Vector3" /> <argument index="2" name="collision" type="KinematicCollision3D" default="null" /> <argument index="3" name="safe_margin" type="float" default="0.001" /> <argument index="4" name="max_collisions" type="int" default="1" /> <description> - Checks for collisions without moving the body. Virtually sets the node's position, scale and rotation to that of the given [Transform3D], then tries to move the body along the vector [code]rel_vec[/code]. Returns [code]true[/code] if a collision would occur. + Checks for collisions without moving the body. This method should be used in [method Node._physics_process] (or in a method called by [method Node._physics_process]), as it uses the physics step's [code]delta[/code] value automatically in calculations. Otherwise, the simulation will run at an incorrect speed. + Virtually sets the node's position, scale and rotation to that of the given [Transform3D], then tries to move the body along the vector [code]linear_velocity[/code]. Returns [code]true[/code] if a collision would occur. [code]collision[/code] is an optional object of type [KinematicCollision3D], which contains additional information about the collision (should there be one). [code]safe_margin[/code] is the extra margin used for collision recovery (see [member CharacterBody3D.collision/safe_margin] for more details). [code]max_collisions[/code] allows to retrieve more than one collision result. diff --git a/doc/classes/PhysicsServer2D.xml b/doc/classes/PhysicsServer2D.xml index 7e5d7ca704..791a04e4c2 100644 --- a/doc/classes/PhysicsServer2D.xml +++ b/doc/classes/PhysicsServer2D.xml @@ -851,8 +851,6 @@ <constant name="SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS" value="6" enum="SpaceParameter"> Constant to set/get the default solver bias for all physics constraints. A solver bias is a factor controlling how much two objects "rebound", after violating a constraint, to avoid leaving them in that state because of numerical imprecision. </constant> - <constant name="SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH" value="7" enum="SpaceParameter"> - </constant> <constant name="SHAPE_WORLD_BOUNDARY" value="0" enum="ShapeType"> This is the constant for creating world boundary shapes. A world boundary shape is an [i]infinite[/i] line with an origin point, and a normal. Thus, it can be used for front/behind checks. </constant> @@ -928,8 +926,8 @@ <constant name="BODY_MODE_DYNAMIC" value="2" enum="BodyMode"> Constant for dynamic bodies. In this mode, a body can be pushed by other bodies and has forces applied. </constant> - <constant name="BODY_MODE_DYNAMIC_LOCKED" value="3" enum="BodyMode"> - Constant for locked dynamic bodies. In this mode, a body is dynamic but can not rotate, and only its linear velocity is affected by external forces. + <constant name="BODY_MODE_DYNAMIC_LINEAR" value="3" enum="BodyMode"> + Constant for linear dynamic bodies. In this mode, a body is dynamic but can not rotate, and only its linear velocity is affected by external forces. </constant> <constant name="BODY_PARAM_BOUNCE" value="0" enum="BodyParameter"> Constant to set/get a body's bounce factor. diff --git a/doc/classes/PhysicsServer3D.xml b/doc/classes/PhysicsServer3D.xml index 9f48c36b62..1d1ca54dbb 100644 --- a/doc/classes/PhysicsServer3D.xml +++ b/doc/classes/PhysicsServer3D.xml @@ -1278,8 +1278,8 @@ <constant name="BODY_MODE_DYNAMIC" value="2" enum="BodyMode"> Constant for dynamic bodies. In this mode, a body can be pushed by other bodies and has forces applied. </constant> - <constant name="BODY_MODE_DYNAMIC_LOCKED" value="3" enum="BodyMode"> - Constant for locked dynamic bodies. In this mode, a body is dynamic but can not rotate, and only its linear velocity is affected by external forces. + <constant name="BODY_MODE_DYNAMIC_LINEAR" value="3" enum="BodyMode"> + Constant for linear dynamic bodies. In this mode, a body is dynamic but can not rotate, and only its linear velocity is affected by external forces. </constant> <constant name="BODY_PARAM_BOUNCE" value="0" enum="BodyParameter"> Constant to set/get a body's bounce factor. @@ -1361,8 +1361,6 @@ <constant name="SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS" value="7" enum="SpaceParameter"> Constant to set/get the default solver bias for all physics constraints. A solver bias is a factor controlling how much two objects "rebound", after violating a constraint, to avoid leaving them in that state because of numerical imprecision. </constant> - <constant name="SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH" value="8" enum="SpaceParameter"> - </constant> <constant name="BODY_AXIS_LINEAR_X" value="1" enum="BodyAxis"> </constant> <constant name="BODY_AXIS_LINEAR_Y" value="2" enum="BodyAxis"> diff --git a/doc/classes/RigidDynamicBody2D.xml b/doc/classes/RigidDynamicBody2D.xml index 059379242b..9baed392eb 100644 --- a/doc/classes/RigidDynamicBody2D.xml +++ b/doc/classes/RigidDynamicBody2D.xml @@ -5,11 +5,10 @@ </brief_description> <description> This node implements simulated 2D physics. You do not control a RigidDynamicBody2D directly. Instead, you apply forces to it (gravity, impulses, etc.) and the physics simulation calculates the resulting movement based on its mass, friction, and other physical properties. - A RigidDynamicBody2D has 4 behavior [member mode]s: Dynamic, Static, DynamicLocked, and Kinematic. + You can switch the body's behavior using [member lock_rotation], [member freeze], and [member freeze_mode]. [b]Note:[/b] You should not change a RigidDynamicBody2D's [code]position[/code] or [code]linear_velocity[/code] every frame or even very often. If you need to directly affect the body's state, use [method _integrate_forces], which allows you to directly access the physics state. Please also keep in mind that physics bodies manage their own transform which overwrites the ones you set. So any direct or indirect transformation (including scaling of the node or its parent) will be visible in the editor only, and immediately reset at runtime. If you need to override the default physics behavior or add a transformation at runtime, you can write a custom force integration. See [member custom_integrator]. - The center of mass is always located at the node's origin without taking into account the [CollisionShape2D] centroid offsets. </description> <tutorials> <link title="2D Physics Platformer Demo">https://godotengine.org/asset-library/asset/119</link> @@ -120,6 +119,15 @@ <member name="custom_integrator" type="bool" setter="set_use_custom_integrator" getter="is_using_custom_integrator" default="false"> If [code]true[/code], internal force integration is disabled for this body. Aside from collision response, the body will only move as determined by the [method _integrate_forces] function. </member> + <member name="freeze" type="bool" setter="set_freeze_enabled" getter="is_freeze_enabled" default="false"> + If [code]true[/code], the body is frozen. Gravity and forces are not applied anymore. + See [member freeze_mode] to set the body's behavior when frozen. + For a body that is always frozen, use [StaticBody2D] or [AnimatableBody2D] instead. + </member> + <member name="freeze_mode" type="int" setter="set_freeze_mode" getter="get_freeze_mode" enum="RigidDynamicBody2D.FreezeMode" default="0"> + The body's freeze mode. Can be used to set the body's behavior when [member freeze] is enabled. See [enum FreezeMode] for possible values. + For a body that is always frozen, use [StaticBody2D] or [AnimatableBody2D] instead. + </member> <member name="gravity_scale" type="float" setter="set_gravity_scale" getter="get_gravity_scale" default="1.0"> Multiplies the gravity applied to the body. The body's gravity is calculated from the [b]Default Gravity[/b] value in [b]Project > Project Settings > Physics > 2d[/b] and/or any additional gravity vector applied by [Area2D]s. </member> @@ -134,13 +142,12 @@ <member name="linear_velocity" type="Vector2" setter="set_linear_velocity" getter="get_linear_velocity" default="Vector2(0, 0)"> The body's linear velocity. </member> + <member name="lock_rotation" type="bool" setter="set_lock_rotation_enabled" getter="is_lock_rotation_enabled" default="false"> + If [code]true[/code], the body cannot rotate. Gravity and forces only apply linear movement. + </member> <member name="mass" type="float" setter="set_mass" getter="get_mass" default="1.0"> The body's mass. </member> - <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="RigidDynamicBody2D.Mode" default="0"> - The body's mode. See [enum Mode] for possible values. - For a body that uses only Static or Kinematic mode, use [StaticBody2D] or [AnimatableBody2D] instead. - </member> <member name="physics_material_override" type="PhysicsMaterial" setter="set_physics_material_override" getter="get_physics_material_override"> The physics material override for the body. If a material is assigned to this property, it will be used instead of any other physics material, such as an inherited one. @@ -198,17 +205,11 @@ </signal> </signals> <constants> - <constant name="MODE_DYNAMIC" value="0" enum="Mode"> - Dynamic body mode. This is the default mode of a rigid body. It is affected by forces, and can move, rotate, and be affected by user code. - </constant> - <constant name="MODE_STATIC" value="1" enum="Mode"> - Static body mode. The body behaves like a [StaticBody2D], and must be moved by code. - </constant> - <constant name="MODE_DYNAMIC_LOCKED" value="2" enum="Mode"> - Locked dynamic body mode. Similar to [constant MODE_DYNAMIC], but the body can not rotate. + <constant name="FREEZE_MODE_STATIC" value="0" enum="FreezeMode"> + Static body freeze mode (default). The body is not affected by gravity and forces. It can be only moved by user code and doesn't collide with other bodies along its path. </constant> - <constant name="MODE_KINEMATIC" value="3" enum="Mode"> - Kinematic body mode. The body behaves like a [AnimatableBody2D], and must be moved by code. + <constant name="FREEZE_MODE_KINEMATIC" value="1" enum="FreezeMode"> + Kinematic body freeze mode. Similar to [constant FREEZE_MODE_STATIC], but collides with other bodies along its path when moved. Useful for a frozen body that needs to be animated. </constant> <constant name="CENTER_OF_MASS_MODE_AUTO" value="0" enum="CenterOfMassMode"> In this mode, the body's center of mass is calculated automatically based on its shapes. diff --git a/doc/classes/RigidDynamicBody3D.xml b/doc/classes/RigidDynamicBody3D.xml index 9b6bcd840b..7d1c7fecfa 100644 --- a/doc/classes/RigidDynamicBody3D.xml +++ b/doc/classes/RigidDynamicBody3D.xml @@ -5,10 +5,9 @@ </brief_description> <description> This is the node that implements full 3D physics. This means that you do not control a RigidDynamicBody3D directly. Instead, you can apply forces to it (gravity, impulses, etc.), and the physics simulation will calculate the resulting movement, collision, bouncing, rotating, etc. - A RigidDynamicBody3D has 4 behavior [member mode]s: Dynamic, Static, DynamicLocked, and Kinematic. + You can switch the body's behavior using [member lock_rotation], [member freeze], and [member freeze_mode]. [b]Note:[/b] Don't change a RigidDynamicBody3D's position every frame or very often. Sporadic changes work fine, but physics runs at a different granularity (fixed Hz) than usual rendering (process callback) and maybe even in a separate thread, so changing this from a process loop may result in strange behavior. If you need to directly affect the body's state, use [method _integrate_forces], which allows you to directly access the physics state. If you need to override the default physics behavior, you can write a custom force integration function. See [member custom_integrator]. - With Bullet physics (the default), the center of mass is the RigidDynamicBody3D center. With GodotPhysics, the center of mass is the average of the [CollisionShape3D] centers. </description> <tutorials> <link title="Physics introduction">https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html</link> @@ -123,6 +122,15 @@ <member name="custom_integrator" type="bool" setter="set_use_custom_integrator" getter="is_using_custom_integrator" default="false"> If [code]true[/code], internal force integration will be disabled (like gravity or air friction) for this body. Other than collision response, the body will only move as determined by the [method _integrate_forces] function, if defined. </member> + <member name="freeze" type="bool" setter="set_freeze_enabled" getter="is_freeze_enabled" default="false"> + If [code]true[/code], the body is frozen. Gravity and forces are not applied anymore. + See [member freeze_mode] to set the body's behavior when frozen. + For a body that is always frozen, use [StaticBody3D] or [AnimatableBody3D] instead. + </member> + <member name="freeze_mode" type="int" setter="set_freeze_mode" getter="get_freeze_mode" enum="RigidDynamicBody3D.FreezeMode" default="0"> + The body's freeze mode. Can be used to set the body's behavior when [member freeze] is enabled. See [enum FreezeMode] for possible values. + For a body that is always frozen, use [StaticBody3D] or [AnimatableBody3D] instead. + </member> <member name="gravity_scale" type="float" setter="set_gravity_scale" getter="get_gravity_scale" default="1.0"> This is multiplied by the global 3D gravity setting found in [b]Project > Project Settings > Physics > 3d[/b] to produce RigidDynamicBody3D's gravity. For example, a value of 1 will be normal gravity, 2 will apply double gravity, and 0.5 will apply half gravity to this object. </member> @@ -137,13 +145,12 @@ <member name="linear_velocity" type="Vector3" setter="set_linear_velocity" getter="get_linear_velocity" default="Vector3(0, 0, 0)"> The body's linear velocity. Can be used sporadically, but [b]don't set this every frame[/b], because physics may run in another thread and runs at a different granularity. Use [method _integrate_forces] as your process loop for precise control of the body state. </member> + <member name="lock_rotation" type="bool" setter="set_lock_rotation_enabled" getter="is_lock_rotation_enabled" default="false"> + If [code]true[/code], the body cannot rotate. Gravity and forces only apply linear movement. + </member> <member name="mass" type="float" setter="set_mass" getter="get_mass" default="1.0"> The body's mass. </member> - <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="RigidDynamicBody3D.Mode" default="0"> - The body's mode. See [enum Mode] for possible values. - For a body that uses only Static or Kinematic mode, use [StaticBody3D] or [AnimatableBody3D] instead. - </member> <member name="physics_material_override" type="PhysicsMaterial" setter="set_physics_material_override" getter="get_physics_material_override"> The physics material override for the body. If a material is assigned to this property, it will be used instead of any other physics material, such as an inherited one. @@ -203,17 +210,11 @@ </signal> </signals> <constants> - <constant name="MODE_DYNAMIC" value="0" enum="Mode"> - Dynamic body mode. This is the default mode of a rigid body. It is affected by forces, and can move, rotate, and be affected by user code. - </constant> - <constant name="MODE_STATIC" value="1" enum="Mode"> - Static body mode. The body behaves like a [StaticBody3D], and can only move by user code. - </constant> - <constant name="MODE_DYNAMIC_LOCKED" value="2" enum="Mode"> - Locked dynamic body mode. Similar to [constant MODE_DYNAMIC], but the body can not rotate. + <constant name="FREEZE_MODE_STATIC" value="0" enum="FreezeMode"> + Static body freeze mode (default). The body is not affected by gravity and forces. It can be only moved by user code and doesn't collide with other bodies along its path. </constant> - <constant name="MODE_KINEMATIC" value="3" enum="Mode"> - Kinematic body mode. The body behaves like a [AnimatableBody3D], and can only move by user code. + <constant name="FREEZE_MODE_KINEMATIC" value="1" enum="FreezeMode"> + Kinematic body freeze mode. Similar to [constant FREEZE_MODE_STATIC], but collides with other bodies along its path when moved. Useful for a frozen body that needs to be animated. </constant> <constant name="CENTER_OF_MASS_MODE_AUTO" value="0" enum="CenterOfMassMode"> In this mode, the body's center of mass is calculated automatically based on its shapes. diff --git a/doc/classes/Signal.xml b/doc/classes/Signal.xml index b70725123b..f78266a20c 100644 --- a/doc/classes/Signal.xml +++ b/doc/classes/Signal.xml @@ -32,10 +32,16 @@ <method name="connect"> <return type="int" /> <argument index="0" name="callable" type="Callable" /> - <argument index="1" name="binds" type="Array" default="[]" /> - <argument index="2" name="flags" type="int" default="0" /> - <description> - Connects this signal to the specified [Callable], optionally providing binds and connection flags. + <argument index="1" name="flags" type="int" default="0" /> + <description> + Connects this signal to the specified [Callable], optionally providing connection flags. You can provide additional arguments to the connected method call by using [method Callable.bind]. + [codeblock] + for button in $Buttons.get_children(): + button.pressed.connect(on_pressed.bind(button)) + + func on_pressed(button): + print(button.name, " was pressed") + [/codeblock] </description> </method> <method name="disconnect"> diff --git a/doc/classes/StreamPeer.xml b/doc/classes/StreamPeer.xml index 316bd77a16..0622626846 100644 --- a/doc/classes/StreamPeer.xml +++ b/doc/classes/StreamPeer.xml @@ -5,6 +5,7 @@ </brief_description> <description> StreamPeer is an abstraction and base class for stream-based protocols (such as TCP). It provides an API for sending and receiving data through streams as raw data or strings. + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> </tutorials> diff --git a/doc/classes/StreamPeerExtension.xml b/doc/classes/StreamPeerExtension.xml new file mode 100644 index 0000000000..93fda8cf5d --- /dev/null +++ b/doc/classes/StreamPeerExtension.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="StreamPeerExtension" inherits="StreamPeer" version="4.0"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="_get_available_bytes" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_data" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="r_buffer" type="uint8_t*" /> + <argument index="1" name="r_bytes" type="int" /> + <argument index="2" name="r_received" type="int32_t*" /> + <description> + </description> + </method> + <method name="_get_partial_data" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="r_buffer" type="uint8_t*" /> + <argument index="1" name="r_bytes" type="int" /> + <argument index="2" name="r_received" type="int32_t*" /> + <description> + </description> + </method> + <method name="_put_data" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="p_data" type="const void*" /> + <argument index="1" name="p_bytes" type="int" /> + <argument index="2" name="r_sent" type="int32_t*" /> + <description> + </description> + </method> + <method name="_put_partial_data" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="p_data" type="const void*" /> + <argument index="1" name="p_bytes" type="int" /> + <argument index="2" name="r_sent" type="int32_t*" /> + <description> + </description> + </method> + </methods> +</class> diff --git a/doc/classes/StreamPeerSSL.xml b/doc/classes/StreamPeerSSL.xml index 034168c2a0..50389f912d 100644 --- a/doc/classes/StreamPeerSSL.xml +++ b/doc/classes/StreamPeerSSL.xml @@ -5,6 +5,7 @@ </brief_description> <description> SSL stream peer. This object can be used to connect to an SSL server or accept a single SSL client connection. + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> <link title="SSL certificates">https://docs.godotengine.org/en/latest/tutorials/networking/ssl_certificates.html</link> diff --git a/doc/classes/StreamPeerTCP.xml b/doc/classes/StreamPeerTCP.xml index bba48e7275..6b700593a2 100644 --- a/doc/classes/StreamPeerTCP.xml +++ b/doc/classes/StreamPeerTCP.xml @@ -5,6 +5,7 @@ </brief_description> <description> TCP stream peer. This object can be used to connect to TCP servers, or also is returned by a TCP server. + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> </tutorials> diff --git a/doc/classes/TCPServer.xml b/doc/classes/TCPServer.xml index 8676e33bb4..4fbaf9412f 100644 --- a/doc/classes/TCPServer.xml +++ b/doc/classes/TCPServer.xml @@ -5,6 +5,7 @@ </brief_description> <description> A TCP server. Listens to connections on a port and returns a [StreamPeerTCP] when it gets an incoming connection. + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> </tutorials> diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml index 77bd7b1a0a..4c50aa4e1f 100644 --- a/doc/classes/TabContainer.xml +++ b/doc/classes/TabContainer.xml @@ -6,8 +6,6 @@ <description> Sets the active tab's [code]visible[/code] property to the value [code]true[/code]. Sets all other children's to [code]false[/code]. Ignores non-[Control] children. - Individual tabs are always visible unless you use [method set_tab_disabled] and [method set_tab_title] to hide it. - To hide only a tab's content, nest the content inside a child [Control], so it receives the [TabContainer]'s visibility setting instead. </description> <tutorials> </tutorials> @@ -50,6 +48,13 @@ Returns [code]true[/code] if the tab at index [code]tab_idx[/code] is disabled. </description> </method> + <method name="get_tab_hidden" qualifiers="const"> + <return type="bool" /> + <argument index="0" name="tab_idx" type="int" /> + <description> + Returns [code]true[/code] if the tab at index [code]tab_idx[/code] is hidden. + </description> + </method> <method name="get_tab_icon" qualifiers="const"> <return type="Texture2D" /> <argument index="0" name="tab_idx" type="int" /> @@ -89,8 +94,15 @@ <argument index="0" name="tab_idx" type="int" /> <argument index="1" name="disabled" type="bool" /> <description> - If [code]disabled[/code] is [code]false[/code], hides the tab at index [code]tab_idx[/code]. - [b]Note:[/b] Its title text will remain, unless also removed with [method set_tab_title]. + If [code]disabled[/code] is [code]true[/code], disables the tab at index [code]tab_idx[/code], making it non-interactable. + </description> + </method> + <method name="set_tab_hidden"> + <return type="void" /> + <argument index="0" name="tab_idx" type="int" /> + <argument index="1" name="hidden" type="bool" /> + <description> + If [code]hidden[/code] is [code]true[/code], hides the tab at index [code]tab_idx[/code], making it disappear from the tab area. </description> </method> <method name="set_tab_icon"> @@ -106,14 +118,14 @@ <argument index="0" name="tab_idx" type="int" /> <argument index="1" name="title" type="String" /> <description> - Sets a title for the tab at index [code]tab_idx[/code]. Tab titles default to the name of the indexed child node, but this can be overridden with [method set_tab_title]. + Sets a title for the tab at index [code]tab_idx[/code]. Tab titles default to the name of the indexed child node. </description> </method> <method name="set_tabs_rearrange_group"> <return type="void" /> <argument index="0" name="group_id" type="int" /> <description> - Defines rearrange group id, choose for each [TabContainer] the same value to enable tab drag between [TabContainer]. Enable drag with [code]set_drag_to_rearrange_enabled(true)[/code]. + Defines rearrange group id, choose for each [TabContainer] the same value to enable tab drag between [TabContainer]. Enable drag with [member drag_to_rearrange_enabled]. </description> </method> </methods> diff --git a/doc/classes/Tabs.xml b/doc/classes/Tabs.xml index f4c89a8b16..3ee3feb365 100644 --- a/doc/classes/Tabs.xml +++ b/doc/classes/Tabs.xml @@ -108,7 +108,7 @@ <return type="String" /> <argument index="0" name="tab_idx" type="int" /> <description> - Returns the title of the tab at index [code]tab_idx[/code]. Tab titles default to the name of the indexed child node, but this can be overridden with [method set_tab_title]. + Returns the title of the tab at index [code]tab_idx[/code]. </description> </method> <method name="get_tabs_rearrange_group" qualifiers="const"> @@ -144,8 +144,7 @@ <argument index="0" name="tab_idx" type="int" /> <argument index="1" name="disabled" type="bool" /> <description> - If [code]disabled[/code] is [code]false[/code], hides the tab at index [code]tab_idx[/code]. - [b]Note:[/b] Its title text will remain unless it is also removed with [method set_tab_title]. + If [code]disabled[/code] is [code]true[/code], disables the tab at index [code]tab_idx[/code], making it non-interactable. </description> </method> <method name="set_tab_icon"> @@ -193,7 +192,7 @@ <return type="void" /> <argument index="0" name="group_id" type="int" /> <description> - Defines the rearrange group ID. Choose for each [Tabs] the same value to dragging tabs between [Tabs]. Enable drag with [code]set_drag_to_rearrange_enabled(true)[/code]. + Defines the rearrange group ID. Choose for each [Tabs] the same value to dragging tabs between [Tabs]. Enable drag with [member drag_to_rearrange_enabled]. </description> </method> </methods> diff --git a/doc/classes/Thread.xml b/doc/classes/Thread.xml index 9c9119c664..ae5c0761b1 100644 --- a/doc/classes/Thread.xml +++ b/doc/classes/Thread.xml @@ -27,12 +27,11 @@ </method> <method name="start"> <return type="int" enum="Error" /> - <argument index="0" name="instance" type="Object" /> - <argument index="1" name="method" type="StringName" /> - <argument index="2" name="userdata" type="Variant" default="null" /> - <argument index="3" name="priority" type="int" enum="Thread.Priority" default="1" /> + <argument index="0" name="callable" type="Callable" /> + <argument index="1" name="userdata" type="Variant" default="null" /> + <argument index="2" name="priority" type="int" enum="Thread.Priority" default="1" /> <description> - Starts a new [Thread] that runs [code]method[/code] on object [code]instance[/code] with [code]userdata[/code] passed as an argument. Even if no userdata is passed, [code]method[/code] must accept one argument and it will be null. The [code]priority[/code] of the [Thread] can be changed by passing a value from the [enum Priority] enum. + Starts a new [Thread] that calls [code]callable[/code] with [code]userdata[/code] passed as an argument. Even if no userdata is passed, [code]method[/code] must accept one argument and it will be null. The [code]priority[/code] of the [Thread] can be changed by passing a value from the [enum Priority] enum. Returns [constant OK] on success, or [constant ERR_CANT_CREATE] on failure. </description> </method> diff --git a/doc/classes/TileSetAtlasSource.xml b/doc/classes/TileSetAtlasSource.xml index 75f7d19b31..d12ac840f4 100644 --- a/doc/classes/TileSetAtlasSource.xml +++ b/doc/classes/TileSetAtlasSource.xml @@ -15,16 +15,6 @@ <tutorials> </tutorials> <methods> - <method name="can_move_tile_in_atlas" qualifiers="const"> - <return type="bool" /> - <argument index="0" name="atlas_coords" type="Vector2i" /> - <argument index="1" name="new_atlas_coords" type="Vector2i" default="Vector2i(-1, -1)" /> - <argument index="2" name="new_size" type="Vector2i" default="Vector2i(-1, -1)" /> - <description> - Returns true if the tile at the [code]atlas_coords[/code] coordinates can be moved to the [code]new_atlas_coords[/code] coordinates with the [code]new_size[/code] size. This functions returns false if a tile is already present in the given area, or if this area is outside the atlas boundaries. - If [code]new_atlas_coords[/code] is [code]Vector2i(-1, -1)[/code], keeps the tile's coordinates. If [code]new_size[/code] is [code]Vector2i(-1, -1)[/code], keeps the tile's size. - </description> - </method> <method name="clear_tiles_outside_texture"> <return type="void" /> <description> @@ -61,6 +51,49 @@ Returns the alternative ID a following call to [method create_alternative_tile] would return. </description> </method> + <method name="get_tile_animation_columns" qualifiers="const"> + <return type="int" /> + <argument index="0" name="atlas_coords" type="Vector2i" /> + <description> + Returns how many columns the tile at [code]atlas_coords[/code] has in its animation layout. + </description> + </method> + <method name="get_tile_animation_frame_duration" qualifiers="const"> + <return type="float" /> + <argument index="0" name="atlas_coords" type="Vector2i" /> + <argument index="1" name="frame_index" type="int" /> + <description> + Returns the animation frame duration of frame [code]frame_index[/code] for the tile at coordinates [code]atlas_coords[/code]. + </description> + </method> + <method name="get_tile_animation_frames_count" qualifiers="const"> + <return type="int" /> + <argument index="0" name="atlas_coords" type="Vector2i" /> + <description> + Returns how many animation frames has the tile at coordinates [code]atlas_coords[/code]. + </description> + </method> + <method name="get_tile_animation_separation" qualifiers="const"> + <return type="Vector2i" /> + <argument index="0" name="atlas_coords" type="Vector2i" /> + <description> + Returns the separation (as in the atlas grid) between each frame of an animated tile at coordinates [code]atlas_coords[/code]. + </description> + </method> + <method name="get_tile_animation_speed" qualifiers="const"> + <return type="float" /> + <argument index="0" name="atlas_coords" type="Vector2i" /> + <description> + Returns the animation speed of the tile at coordinates [code]atlas_coords[/code]. + </description> + </method> + <method name="get_tile_animation_total_duration" qualifiers="const"> + <return type="float" /> + <argument index="0" name="atlas_coords" type="Vector2i" /> + <description> + Returns the sum of the sum of the frame durations of the tile at coordinates [code]atlas_coords[/code]. This value needs to be divided by the animation speed to get the actual animation loop duration. + </description> + </method> <method name="get_tile_at_coords" qualifiers="const"> <return type="Vector2i" /> <argument index="0" name="atlas_coords" type="Vector2i" /> @@ -86,8 +119,21 @@ <method name="get_tile_texture_region" qualifiers="const"> <return type="Rect2i" /> <argument index="0" name="atlas_coords" type="Vector2i" /> + <argument index="1" name="frame" type="int" default="0" /> + <description> + Returns a tile's texture region in the atlas texture. For animated tiles, a [code]frame[/code] argument might be provided for the different frames of the animation. + </description> + </method> + <method name="has_room_for_tile" qualifiers="const"> + <return type="bool" /> + <argument index="0" name="atlas_coords" type="Vector2i" /> + <argument index="1" name="size" type="Vector2i" /> + <argument index="2" name="animation_columns" type="int" /> + <argument index="3" name="animation_separation" type="Vector2i" /> + <argument index="4" name="frames_count" type="int" /> + <argument index="5" name="ignored_tile" type="Vector2i" default="Vector2i(-1, -1)" /> <description> - Returns a tile's texture region in the atlas texture. + Returns whether there is enough room in an atlas to create/modify a tile with the given properties. If [code]ignored_tile[/code] is provided, act as is the given tile was not present in the atlas. This may be used when you want to modify a tile's properties. </description> </method> <method name="has_tiles_outside_texture"> @@ -104,7 +150,7 @@ <description> Move the tile and its alternatives at the [code]atlas_coords[/code] coordinates to the [code]new_atlas_coords[/code] coordinates with the [code]new_size[/code] size. This functions will fail if a tile is already present in the given area. If [code]new_atlas_coords[/code] is [code]Vector2i(-1, -1)[/code], keeps the tile's coordinates. If [code]new_size[/code] is [code]Vector2i(-1, -1)[/code], keeps the tile's size. - To avoid an error, first check if a move is possible using [method can_move_tile_in_atlas]. + To avoid an error, first check if a move is possible using [method has_room_for_tile]. </description> </method> <method name="remove_alternative_tile"> @@ -133,6 +179,47 @@ Calling this function with [code]alternative_id[/code] equals to 0 will fail, as the base tile alternative cannot be moved. </description> </method> + <method name="set_tile_animation_columns"> + <return type="void" /> + <argument index="0" name="atlas_coords" type="Vector2i" /> + <argument index="1" name="frame_columns" type="int" /> + <description> + Sets the number of columns in the animation layout of the tile at coordinates [code]atlas_coords[/code]. If set to 0, then the different frames of the animation are laid out as a single horizontal line in the atlas. + </description> + </method> + <method name="set_tile_animation_frame_duration"> + <return type="void" /> + <argument index="0" name="atlas_coords" type="Vector2i" /> + <argument index="1" name="frame_index" type="int" /> + <argument index="2" name="duration" type="float" /> + <description> + Sets the animation frame duration of frame [code]frame_index[/code] for the tile at coordinates [code]atlas_coords[/code]. + </description> + </method> + <method name="set_tile_animation_frames_count"> + <return type="void" /> + <argument index="0" name="atlas_coords" type="Vector2i" /> + <argument index="1" name="frames_count" type="int" /> + <description> + Sets how many animation frames the tile at coordinates [code]atlas_coords[/code] has. + </description> + </method> + <method name="set_tile_animation_separation"> + <return type="void" /> + <argument index="0" name="atlas_coords" type="Vector2i" /> + <argument index="1" name="separation" type="Vector2i" /> + <description> + Sets the margin (in grid tiles) between each tile in the animation layout of the tile at coordinates [code]atlas_coords[/code] has. + </description> + </method> + <method name="set_tile_animation_speed"> + <return type="void" /> + <argument index="0" name="atlas_coords" type="Vector2i" /> + <argument index="1" name="speed" type="float" /> + <description> + Sets the animation speed of the tile at coordinates [code]atlas_coords[/code] has. + </description> + </method> </methods> <members> <member name="margins" type="Vector2i" setter="set_margins" getter="get_margins" default="Vector2i(0, 0)"> diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml index ede6e2fdd5..f94018c96b 100644 --- a/doc/classes/Tween.xml +++ b/doc/classes/Tween.xml @@ -86,6 +86,7 @@ [code]initial_value[/code] is the starting value of the interpolation. [code]delta_value[/code] is the change of the value in the interpolation, i.e. it's equal to [code]final_value - initial_value[/code]. [code]duration[/code] is the total time of the interpolation. + [b]Note:[/b] If [code]duration[/code] is equal to [code]0[/code], the method will always return the final value, regardless of [code]elapsed_time[/code] provided. </description> </method> <method name="is_running"> diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 5fda1c76ef..d6650c3319 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -147,7 +147,7 @@ void ScriptEditorDebugger::update_tabs() { } void ScriptEditorDebugger::clear_style() { - tabs->add_theme_style_override("panel", nullptr); + tabs->remove_theme_style_override("panel"); } void ScriptEditorDebugger::save_node(ObjectID p_id, const String &p_file) { @@ -501,6 +501,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da error->set_text(0, time); error->set_text_align(0, TreeItem::ALIGN_LEFT); + const Color color = get_theme_color(oe.warning ? SNAME("warning_color") : SNAME("error_color"), SNAME("Editor")); + error->set_custom_color(0, color); + error->set_custom_color(1, color); + String error_title; if (oe.callstack.size() > 0) { // If available, use the script's stack in the error title. diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index 88087664d7..b9f1c1af54 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -535,7 +535,7 @@ void EditorAudioBus::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_RIGHT && mb->is_pressed()) { - Vector2 pos = Vector2(mb->get_position().x, mb->get_position().y); + Vector2 pos = mb->get_position(); bus_popup->set_position(get_global_position() + pos); bus_popup->popup(); } diff --git a/editor/editor_dir_dialog.cpp b/editor/editor_dir_dialog.cpp index 5df392b91e..f91dedf25d 100644 --- a/editor/editor_dir_dialog.cpp +++ b/editor/editor_dir_dialog.cpp @@ -44,6 +44,7 @@ void EditorDirDialog::_update_dir(TreeItem *p_item, EditorFileSystemDirectory *p p_item->set_metadata(0, p_dir->get_path()); p_item->set_icon(0, tree->get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); + p_item->set_icon_modulate(0, tree->get_theme_color(SNAME("folder_icon_modulate"), SNAME("FileDialog"))); if (!p_item->get_parent()) { p_item->set_text(0, "res://"); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index b4850e6ebb..9a79e1f942 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1757,13 +1757,15 @@ void EditorNode::restart_editor() { void EditorNode::_save_all_scenes() { for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { Node *scene = editor_data.get_edited_scene_root(i); - if (scene && scene->get_filename() != "") { + if (scene && scene->get_filename() != "" && DirAccess::exists(scene->get_filename().get_base_dir())) { if (i != editor_data.get_edited_scene()) { _save_scene(scene->get_filename(), i); } else { _save_scene_with_preview(scene->get_filename()); } - } // else: ignore new scenes + } else { + show_warning(TTR("Could not save one or more scenes!"), TTR("Save All Scenes")); + } } _save_default_environment(); @@ -2484,20 +2486,22 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { case SCENE_TAB_CLOSE: case FILE_SAVE_SCENE: { int scene_idx = (p_option == FILE_SAVE_SCENE) ? -1 : tab_closing; - Node *scene = editor_data.get_edited_scene_root(scene_idx); if (scene && scene->get_filename() != "") { - if (scene_idx != editor_data.get_edited_scene()) { - _save_scene_with_preview(scene->get_filename(), scene_idx); - } else { - _save_scene_with_preview(scene->get_filename()); - } + if (DirAccess::exists(scene->get_filename().get_base_dir())) { + if (scene_idx != editor_data.get_edited_scene()) { + _save_scene_with_preview(scene->get_filename(), scene_idx); + } else { + _save_scene_with_preview(scene->get_filename()); + } - if (scene_idx != -1) { - _discard_changes(); + if (scene_idx != -1) { + _discard_changes(); + } + save_layout(); + } else { + show_save_accept(vformat(TTR("%s no longer exists! Please specify a new save location."), scene->get_filename().get_base_dir()), TTR("OK")); } - save_layout(); - break; } [[fallthrough]]; @@ -4150,6 +4154,13 @@ void EditorNode::show_accept(const String &p_text, const String &p_title) { accept->popup_centered(); } +void EditorNode::show_save_accept(const String &p_text, const String &p_title) { + current_option = -1; + save_accept->get_ok_button()->set_text(p_title); + save_accept->set_text(p_text); + save_accept->popup_centered(); +} + void EditorNode::show_warning(const String &p_text, const String &p_title) { if (warning->is_inside_tree()) { warning->set_text(p_text); @@ -6105,9 +6116,9 @@ EditorNode::EditorNode() { dock_tab_move_right = memnew(Button); dock_tab_move_right->set_flat(true); if (gui_base->is_layout_rtl()) { - dock_tab_move_right->set_icon(theme->get_icon("Forward", "EditorIcons")); - } else { dock_tab_move_right->set_icon(theme->get_icon("Back", "EditorIcons")); + } else { + dock_tab_move_right->set_icon(theme->get_icon("Forward", "EditorIcons")); } dock_tab_move_right->set_focus_mode(Control::FOCUS_NONE); dock_tab_move_right->connect("pressed", callable_mp(this, &EditorNode::_dock_move_right)); @@ -6263,6 +6274,10 @@ EditorNode::EditorNode() { gui_base->add_child(accept); accept->connect("confirmed", callable_mp(this, &EditorNode::_menu_confirm_current)); + save_accept = memnew(AcceptDialog); + gui_base->add_child(save_accept); + save_accept->connect("confirmed", callable_mp(this, &EditorNode::_menu_option), make_binds((int)MenuOptions::FILE_SAVE_AS_SCENE)); + project_export = memnew(ProjectExportDialog); gui_base->add_child(project_export); diff --git a/editor/editor_node.h b/editor/editor_node.h index 2e8b850c7b..d1f4cf8452 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -308,6 +308,7 @@ private: ConfirmationDialog *pick_main_scene; Button *select_current_scene_button; AcceptDialog *accept; + AcceptDialog *save_accept; EditorAbout *about; AcceptDialog *warning; @@ -814,6 +815,7 @@ public: Ref<Texture2D> get_class_icon(const String &p_class, const String &p_fallback = "Object") const; void show_accept(const String &p_text, const String &p_title); + void show_save_accept(const String &p_text, const String &p_title); void show_warning(const String &p_text, const String &p_title = TTR("Warning!")); void _copy_warning(const String &p_str); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 1820804cfe..70f43e01cf 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -411,6 +411,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("interface/editor/automatically_open_screenshots", true); EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/single_window_mode", false, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) _initial_set("interface/editor/hide_console_window", false); + _initial_set("interface/editor/mouse_extra_buttons_navigate_history", true); _initial_set("interface/editor/save_each_scene_on_quit", true); // Regression // Inspector diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index afeba4f6fb..1890814da9 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -191,6 +191,59 @@ void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) { } } +void EditorSpinSlider::_value_input_gui_input(const Ref<InputEvent> &p_event) { + Ref<InputEventKey> k = p_event; + if (k.is_valid() && k->is_pressed()) { + double step = get_step(); + double real_step = step; + if (step < 1) { + double divisor = 1.0 / get_step(); + + if (trunc(divisor) == divisor) { + step = 1.0; + } + } + + if (k->is_ctrl_pressed()) { + step *= 100.0; + } else if (k->is_shift_pressed()) { + step *= 10.0; + } else if (k->is_alt_pressed()) { + step *= 0.1; + } + + uint32_t code = k->get_keycode(); + switch (code) { + case KEY_UP: { + _evaluate_input_text(); + + double last_value = get_value(); + set_value(last_value + step); + double new_value = get_value(); + + if (new_value < CLAMP(last_value + step, get_min(), get_max())) { + set_value(last_value + real_step); + } + + value_input->set_text(get_text_value()); + } break; + case KEY_DOWN: { + _evaluate_input_text(); + + double last_value = get_value(); + set_value(last_value - step); + double new_value = get_value(); + + if (new_value > CLAMP(last_value - step, get_min(), get_max())) { + set_value(last_value - real_step); + } + + value_input->set_text(get_text_value()); + } break; + } + } +} + void EditorSpinSlider::_update_value_input_stylebox() { if (!value_input) { return; @@ -585,11 +638,13 @@ void EditorSpinSlider::_ensure_input_popup() { value_input_popup->connect("popup_hide", callable_mp(this, &EditorSpinSlider::_value_input_closed)); value_input->connect("text_submitted", callable_mp(this, &EditorSpinSlider::_value_input_submitted)); value_input->connect("focus_exited", callable_mp(this, &EditorSpinSlider::_value_focus_exited)); + value_input->connect("gui_input", callable_mp(this, &EditorSpinSlider::_value_input_gui_input)); if (is_inside_tree()) { _update_value_input_stylebox(); } } + EditorSpinSlider::EditorSpinSlider() { flat = false; grabbing_spinner_attempt = false; diff --git a/editor/editor_spin_slider.h b/editor/editor_spin_slider.h index 1bf8e8eef9..7e10764491 100644 --- a/editor/editor_spin_slider.h +++ b/editor/editor_spin_slider.h @@ -71,6 +71,7 @@ class EditorSpinSlider : public Range { void _value_input_closed(); void _value_input_submitted(const String &); void _value_focus_exited(); + void _value_input_gui_input(const Ref<InputEvent> &p_event); bool hide_slider; bool flat; diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp index b646b3361d..cb88e9d75e 100644 --- a/editor/export_template_manager.cpp +++ b/editor/export_template_manager.cpp @@ -743,7 +743,7 @@ void ExportTemplateManager::_notification(int p_what) { current_missing_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); current_installed_label->add_theme_color_override("font_color", get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"))); - mirror_options_button->set_icon(get_theme_icon(SNAME("GuiTabMenu"), SNAME("EditorIcons"))); + mirror_options_button->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); } break; case NOTIFICATION_VISIBILITY_CHANGED: { diff --git a/editor/import/resource_importer_texture_atlas.cpp b/editor/import/resource_importer_texture_atlas.cpp index 869af209d3..36fd161c35 100644 --- a/editor/import/resource_importer_texture_atlas.cpp +++ b/editor/import/resource_importer_texture_atlas.cpp @@ -324,7 +324,7 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file atlas_texture.instantiate(); atlas_texture->set_atlas(cache); atlas_texture->set_region(Rect2(offset, pack_data.region.size)); - atlas_texture->set_margin(Rect2(pack_data.region.position, Size2(pack_data.image->get_width(), pack_data.image->get_height()) - pack_data.region.size)); + atlas_texture->set_margin(Rect2(pack_data.region.position, pack_data.image->get_size() - pack_data.region.size)); texture = atlas_texture; } else { diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index 648e60a554..5b52554335 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -127,12 +127,7 @@ void ImportDock::set_edit_path(const String &p_path) { } } - import_as->add_separator(); - import_as->add_item(TTR("Keep File (No Import)")); - import_as->set_item_metadata(import_as->get_item_count() - 1, "keep"); - if (importer_name == "keep") { - import_as->select(import_as->get_item_count() - 1); - } + _add_keep_import_option(importer_name); import->set_disabled(false); import_as->set_disabled(false); @@ -141,6 +136,15 @@ void ImportDock::set_edit_path(const String &p_path) { imported->set_text(p_path.get_file()); } +void ImportDock::_add_keep_import_option(const String &p_importer_name) { + import_as->add_separator(); + import_as->add_item(TTR("Keep File (No Import)")); + import_as->set_item_metadata(import_as->get_item_count() - 1, "keep"); + if (p_importer_name == "keep") { + import_as->select(import_as->get_item_count() - 1); + } +} + void ImportDock::_update_options(const Ref<ConfigFile> &p_config) { List<ResourceImporter::ImportOption> options; @@ -270,6 +274,8 @@ void ImportDock::set_edit_multiple_paths(const Vector<String> &p_paths) { } } + _add_keep_import_option(params->importer->get_importer_name()); + _update_preset_menu(); params->paths = p_paths; diff --git a/editor/import_dock.h b/editor/import_dock.h index 2be48dd505..3c28bbcd89 100644 --- a/editor/import_dock.h +++ b/editor/import_dock.h @@ -66,6 +66,7 @@ class ImportDock : public VBoxContainer { void _importer_selected(int i_idx); void _update_options(const Ref<ConfigFile> &p_config = Ref<ConfigFile>()); void _update_preset_menu(); + void _add_keep_import_option(const String &p_importer_name); void _property_toggled(const StringName &p_prop, bool p_checked); void _reimport_attempt(); diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index 49fcac512b..686a35e442 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -129,8 +129,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven add_point_pos += blend_space->get_min_space(); if (snap->is_pressed()) { - add_point_pos.x = Math::snapped(add_point_pos.x, blend_space->get_snap().x); - add_point_pos.y = Math::snapped(add_point_pos.y, blend_space->get_snap().y); + add_point_pos = add_point_pos.snapped(blend_space->get_snap()); } } @@ -215,8 +214,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven Vector2 point = blend_space->get_blend_point_position(selected_point); point += drag_ofs; if (snap->is_pressed()) { - point.x = Math::snapped(point.x, blend_space->get_snap().x); - point.y = Math::snapped(point.y, blend_space->get_snap().y); + point = point.snapped(blend_space->get_snap()); } updating = true; @@ -467,8 +465,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() { if (dragging_selected && selected_point == point_idx) { point += drag_ofs; if (snap->is_pressed()) { - point.x = Math::snapped(point.x, blend_space->get_snap().x); - point.y = Math::snapped(point.y, blend_space->get_snap().y); + point = point.snapped(blend_space->get_snap()); } } point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); @@ -503,8 +500,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() { if (dragging_selected && selected_point == i) { point += drag_ofs; if (snap->is_pressed()) { - point.x = Math::snapped(point.x, blend_space->get_snap().x); - point.y = Math::snapped(point.y, blend_space->get_snap().y); + point = point.snapped(blend_space->get_snap()); } } point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); @@ -702,8 +698,7 @@ void AnimationNodeBlendSpace2DEditor::_update_edited_point_pos() { if (dragging_selected) { pos += drag_ofs; if (snap->is_pressed()) { - pos.x = Math::snapped(pos.x, blend_space->get_snap().x); - pos.y = Math::snapped(pos.y, blend_space->get_snap().y); + pos = pos.snapped(blend_space->get_snap()); } } updating = true; diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 18b4966f80..68b143358a 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -120,7 +120,7 @@ void AnimationPlayerEditor::_notification(int p_what) { Ref<Image> autoplay_img = autoplay_icon->get_image(); Ref<Image> reset_img = reset_icon->get_image(); Ref<Image> autoplay_reset_img; - Size2 icon_size = Size2(autoplay_img->get_width(), autoplay_img->get_height()); + Size2 icon_size = autoplay_img->get_size(); autoplay_reset_img.instantiate(); autoplay_reset_img->create(icon_size.x * 2, icon_size.y, false, autoplay_img->get_format()); autoplay_reset_img->blit_rect(autoplay_img, Rect2(Point2(), icon_size), Point2()); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 1758ba7048..66a1719ff3 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -2927,7 +2927,7 @@ void CanvasItemEditor::_draw_ruler_tool() { viewport->draw_string(font, text_pos, TS->format_number(vformat("%.1f px", length_vector.length())), HALIGN_LEFT, -1, font_size, font_color, outline_size, outline_color); if (draw_secondary_lines) { - const real_t horizontal_angle_rad = atan2(length_vector.y, length_vector.x); + const real_t horizontal_angle_rad = length_vector.angle(); const real_t vertical_angle_rad = Math_PI / 2.0 - horizontal_angle_rad; const int horizontal_angle = round(180 * horizontal_angle_rad / Math_PI); const int vertical_angle = round(180 * vertical_angle_rad / Math_PI); @@ -4898,8 +4898,7 @@ void CanvasItemEditor::_focus_selection(int p_op) { if (p_op == VIEW_CENTER_TO_SELECTION) { center = rect.get_center(); Vector2 offset = viewport->get_size() / 2 - editor->get_scene_root()->get_global_canvas_transform().xform(center); - view_offset.x -= Math::round(offset.x / zoom); - view_offset.y -= Math::round(offset.y / zoom); + view_offset -= (offset / zoom).round(); update_viewport(); } else { // VIEW_FRAME_TO_SELECTION diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp index 6f246c1661..fb9f8696fe 100644 --- a/editor/plugins/cpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp @@ -83,7 +83,7 @@ void CPUParticles2DEditorPlugin::_generate_emission_mask() { } img->convert(Image::FORMAT_RGBA8); ERR_FAIL_COND(img->get_format() != Image::FORMAT_RGBA8); - Size2i s = Size2(img->get_width(), img->get_height()); + Size2i s = img->get_size(); ERR_FAIL_COND(s.width == 0 || s.height == 0); Vector<Point2> valid_positions; diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp index dd91df747a..44c789b145 100644 --- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp @@ -158,7 +158,7 @@ void GPUParticles2DEditorPlugin::_generate_emission_mask() { } img->convert(Image::FORMAT_RGBA8); ERR_FAIL_COND(img->get_format() != Image::FORMAT_RGBA8); - Size2i s = Size2(img->get_width(), img->get_height()); + Size2i s = img->get_size(); ERR_FAIL_COND(s.width == 0 || s.height == 0); Vector<Point2> valid_positions; diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp index 903a3689b0..5ac58795d1 100644 --- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp @@ -362,6 +362,7 @@ void GPUParticles3DEditor::_generate_emission_points() { Ref<ImageTexture> tex; tex.instantiate(); + tex->create_from_image(image); Ref<ParticlesMaterial> material = node->get_process_material(); ERR_FAIL_COND(material.is_null()); @@ -390,6 +391,7 @@ void GPUParticles3DEditor::_generate_emission_points() { Ref<ImageTexture> tex2; tex2.instantiate(); + tex2->create_from_image(image2); material->set_emission_normal_texture(tex2); } else { diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp index 3207a989bd..16cafda899 100644 --- a/editor/plugins/item_list_editor_plugin.cpp +++ b/editor/plugins/item_list_editor_plugin.cpp @@ -244,6 +244,7 @@ void ItemListEditor::_node_removed(Node *p_node) { void ItemListEditor::_notification(int p_notification) { if (p_notification == NOTIFICATION_ENTER_TREE || p_notification == NOTIFICATION_THEME_CHANGED) { add_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); + clear_button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); del_button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); } else if (p_notification == NOTIFICATION_READY) { get_tree()->connect("node_removed", callable_mp(this, &ItemListEditor::_node_removed)); @@ -258,6 +259,12 @@ void ItemListEditor::_add_pressed() { item_plugins[selected_idx]->add_item(); } +void ItemListEditor::_clear_pressed() { + for (int i = item_plugins[selected_idx]->get_item_count() - 1; i >= 0; i--) { + item_plugins[selected_idx]->erase(i); + } +} + void ItemListEditor::_delete_pressed() { if (selected_idx == -1) { return; @@ -350,6 +357,11 @@ ItemListEditor::ItemListEditor() { hbc->add_spacer(); + clear_button = memnew(Button); + clear_button->set_text(TTR("Delete All")); + hbc->add_child(clear_button); + clear_button->connect("pressed", callable_mp(this, &ItemListEditor::_clear_pressed)); + del_button = memnew(Button); del_button->set_text(TTR("Delete")); hbc->add_child(del_button); diff --git a/editor/plugins/item_list_editor_plugin.h b/editor/plugins/item_list_editor_plugin.h index 8c77f3d952..8f61aef083 100644 --- a/editor/plugins/item_list_editor_plugin.h +++ b/editor/plugins/item_list_editor_plugin.h @@ -204,6 +204,7 @@ class ItemListEditor : public HBoxContainer { Tree *tree; Button *add_button; Button *del_button; + Button *clear_button; int selected_idx; @@ -213,6 +214,7 @@ class ItemListEditor : public HBoxContainer { void _add_pressed(); void _delete_pressed(); + void _clear_pressed(); void _node_removed(Node *p_node); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index e9b715d645..fdd4baaf7d 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -128,7 +128,7 @@ void ViewportRotationControl::_draw_axis(const Axis2D &p_axis) { const Color axis_color = axis_colors[direction]; const double alpha = focused ? 1.0 : ((p_axis.z_axis + 1.0) / 2.0) * 0.5 + 0.5; - const Color c = focused ? Color(0.9, 0.9, 0.9) : Color(axis_color.r, axis_color.g, axis_color.b, alpha); + const Color c = focused ? Color(0.9, 0.9, 0.9) : Color(axis_color, alpha); if (positive) { // Draw axis lines for the positive axes. @@ -854,8 +854,8 @@ void Node3DEditorViewport::_update_name() { } void Node3DEditorViewport::_compute_edit(const Point2 &p_point) { - _edit.click_ray = _get_ray(Vector2(p_point.x, p_point.y)); - _edit.click_ray_pos = _get_ray_pos(Vector2(p_point.x, p_point.y)); + _edit.click_ray = _get_ray(p_point); + _edit.click_ray_pos = _get_ray_pos(p_point); _edit.plane = TRANSFORM_VIEW; spatial_editor->update_transform_gizmo(); _edit.center = spatial_editor->get_gizmo_transform().origin; @@ -934,8 +934,8 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b return false; } - Vector3 ray_pos = _get_ray_pos(Vector2(p_screenpos.x, p_screenpos.y)); - Vector3 ray = _get_ray(Vector2(p_screenpos.x, p_screenpos.y)); + Vector3 ray_pos = _get_ray_pos(p_screenpos); + Vector3 ray = _get_ray(p_screenpos); Transform3D gt = spatial_editor->get_gizmo_transform(); @@ -998,7 +998,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b } else { //handle plane translate _edit.mode = TRANSFORM_TRANSLATE; - _compute_edit(Point2(p_screenpos.x, p_screenpos.y)); + _compute_edit(p_screenpos); _edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis + (is_plane_translate ? 3 : 0)); } return true; @@ -1036,7 +1036,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b } else { //handle rotate _edit.mode = TRANSFORM_ROTATE; - _compute_edit(Point2(p_screenpos.x, p_screenpos.y)); + _compute_edit(p_screenpos); _edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis); } return true; @@ -1102,7 +1102,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b } else { //handle scale _edit.mode = TRANSFORM_SCALE; - _compute_edit(Point2(p_screenpos.x, p_screenpos.y)); + _compute_edit(p_screenpos); _edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis + (is_plane_scale ? 3 : 0)); } return true; @@ -6280,7 +6280,7 @@ void Node3DEditor::update_grid() { // Gets a orthogonal or perspective position correctly (for the grid comparison) const Vector3 camera_position = get_editor_viewport(0)->camera->get_position(); - if (!grid_init_draw || (camera_position - grid_camera_last_update_position).length() >= 10.0f) { + if (!grid_init_draw || grid_camera_last_update_position.distance_squared_to(camera_position) >= 100.0f) { _finish_grid(); _init_grid(); grid_init_draw = true; diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index 13f7908170..bb0c270baa 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -510,7 +510,14 @@ void Path3DEditorPlugin::_close_curve() { if (c->get_point_count() < 2) { return; } - c->add_point(c->get_point_position(0), c->get_point_in(0), c->get_point_out(0)); + if (c->get_point_position(0) == c->get_point_position(c->get_point_count() - 1)) { + return; + } + UndoRedo *ur = editor->get_undo_redo(); + ur->create_action(TTR("Close Curve")); + ur->add_do_method(c.ptr(), "add_point", c->get_point_position(0), c->get_point_in(0), c->get_point_out(0), -1); + ur->add_undo_method(c.ptr(), "remove_point", c->get_point_count()); + ur->commit_action(); } void Path3DEditorPlugin::_handle_option_pressed(int p_option) { diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 782152b002..5afe9ed60c 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -449,7 +449,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { if (mb.is_valid()) { if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { if (mb->is_pressed()) { - uv_drag_from = snap_point(Vector2(mb->get_position().x, mb->get_position().y)); + uv_drag_from = snap_point(mb->get_position()); uv_drag = true; points_prev = node->get_uv(); @@ -463,7 +463,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { if (uv_move_current == UV_MODE_CREATE) { if (!uv_create) { points_prev.resize(0); - Vector2 tuv = mtx.affine_inverse().xform(snap_point(Vector2(mb->get_position().x, mb->get_position().y))); + Vector2 tuv = mtx.affine_inverse().xform(snap_point(mb->get_position())); points_prev.push_back(tuv); uv_create_to = tuv; point_drag_index = 0; @@ -483,7 +483,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { uv_edit_draw->update(); } else { - Vector2 tuv = mtx.affine_inverse().xform(snap_point(Vector2(mb->get_position().x, mb->get_position().y))); + Vector2 tuv = mtx.affine_inverse().xform(snap_point(mb->get_position())); // Close the polygon if selected point is near start. Threshold for closing scaled by zoom level if (points_prev.size() > 2 && tuv.distance_to(points_prev[0]) < (8 / uv_draw_zoom)) { @@ -527,7 +527,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { uv_create_bones_prev = node->call("_get_bones"); int internal_vertices = node->get_internal_vertex_count(); - Vector2 pos = mtx.affine_inverse().xform(snap_point(Vector2(mb->get_position().x, mb->get_position().y))); + Vector2 pos = mtx.affine_inverse().xform(snap_point(mb->get_position())); uv_create_poly_prev.push_back(pos); uv_create_uv_prev.push_back(pos); @@ -573,7 +573,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { for (int i = points_prev.size() - internal_vertices; i < points_prev.size(); i++) { Vector2 tuv = mtx.xform(uv_create_poly_prev[i]); - real_t dist = tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)); + real_t dist = tuv.distance_to(mb->get_position()); if (dist < 8 && dist < closest_dist) { closest = i; closest_dist = dist; @@ -626,7 +626,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { point_drag_index = -1; for (int i = 0; i < points_prev.size(); i++) { Vector2 tuv = mtx.xform(points_prev[i]); - if (tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)) < 8) { + if (tuv.distance_to(mb->get_position()) < 8) { uv_drag_from = tuv; point_drag_index = i; } @@ -643,7 +643,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { for (int i = 0; i < points_prev.size(); i++) { Vector2 tuv = mtx.xform(points_prev[i]); - real_t dist = tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)); + real_t dist = tuv.distance_to(mb->get_position()); if (dist < 8 && dist < closest_dist) { closest = i; closest_dist = dist; @@ -695,7 +695,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { polys.write[j] = mtx.xform(points_prev[idx]); } - if (Geometry2D::is_point_in_polygon(Vector2(mb->get_position().x, mb->get_position().y), polys)) { + if (Geometry2D::is_point_in_polygon(mb->get_position(), polys)) { erase_index = i; break; } @@ -779,7 +779,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { if (mm.is_valid()) { if ((mm->get_button_mask() & MOUSE_BUTTON_MASK_MIDDLE) || Input::get_singleton()->is_key_pressed(KEY_SPACE)) { - Vector2 drag(mm->get_relative().x, mm->get_relative().y); + Vector2 drag = mm->get_relative(); uv_hscroll->set_value(uv_hscroll->get_value() - drag.x); uv_vscroll->set_value(uv_vscroll->get_value() - drag.y); @@ -791,7 +791,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { switch (uv_move_current) { case UV_MODE_CREATE: { if (uv_create) { - uv_create_to = mtx.affine_inverse().xform(snap_point(Vector2(mm->get_position().x, mm->get_position().y))); + uv_create_to = mtx.affine_inverse().xform(snap_point(mm->get_position())); } } break; case UV_MODE_EDIT_POINT: { @@ -870,7 +870,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } break; case UV_MODE_PAINT_WEIGHT: case UV_MODE_CLEAR_WEIGHT: { - bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y); + bone_paint_pos = mm->get_position(); } break; default: { } @@ -905,10 +905,10 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { uv_edit_draw->update(); CanvasItemEditor::get_singleton()->update_viewport(); } else if (polygon_create.size()) { - uv_create_to = mtx.affine_inverse().xform(Vector2(mm->get_position().x, mm->get_position().y)); + uv_create_to = mtx.affine_inverse().xform(mm->get_position()); uv_edit_draw->update(); } else if (uv_mode == UV_MODE_PAINT_WEIGHT || uv_mode == UV_MODE_CLEAR_WEIGHT) { - bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y); + bone_paint_pos = mm->get_position(); uv_edit_draw->update(); } } diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 07e4d4ebf0..6922a48a50 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -2750,6 +2750,29 @@ void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co } } +void ScriptEditor::input(const Ref<InputEvent> &p_event) { + // This is implemented in `input()` rather than `unhandled_input()` to allow + // the shortcut to be used regardless of the click location. + // This feature can be disabled to avoid interfering with other uses of the additional + // mouse buttons, such as push-to-talk in a VoIP program. + if (EDITOR_GET("interface/editor/mouse_extra_buttons_navigate_history")) { + const Ref<InputEventMouseButton> mb = p_event; + + // Navigate the script history using additional mouse buttons present on some mice. + // This must be hardcoded as the editor shortcuts dialog doesn't allow assigning + // more than one shortcut per action. + if (mb.is_valid() && mb->is_pressed() && is_visible_in_tree()) { + if (mb->get_button_index() == MOUSE_BUTTON_XBUTTON1) { + _history_back(); + } + + if (mb->get_button_index() == MOUSE_BUTTON_XBUTTON2) { + _history_forward(); + } + } + } +} + void ScriptEditor::unhandled_key_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); @@ -3294,7 +3317,6 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method("_update_script_connections", &ScriptEditor::_update_script_connections); ClassDB::bind_method("_help_class_open", &ScriptEditor::_help_class_open); ClassDB::bind_method("_live_auto_reload_running_scripts", &ScriptEditor::_live_auto_reload_running_scripts); - ClassDB::bind_method("_update_members_overview", &ScriptEditor::_update_members_overview); ClassDB::bind_method("_update_recent_scripts", &ScriptEditor::_update_recent_scripts); @@ -3427,9 +3449,11 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { ED_SHORTCUT("script_editor/window_sort", TTR("Sort")); ED_SHORTCUT("script_editor/window_move_up", TTR("Move Up"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_UP); ED_SHORTCUT("script_editor/window_move_down", TTR("Move Down"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_DOWN); - ED_SHORTCUT("script_editor/next_script", TTR("Next Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_PERIOD); // these should be KEY_GREATER and KEY_LESS but those don't work + // FIXME: These should be `KEY_GREATER` and `KEY_LESS` but those don't work. + ED_SHORTCUT("script_editor/next_script", TTR("Next Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_PERIOD); ED_SHORTCUT("script_editor/prev_script", TTR("Previous Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_COMMA); - set_process_unhandled_key_input(true); + set_process_input(true); + set_process_unhandled_input(true); file_menu = memnew(MenuButton); file_menu->set_text(TTR("File")); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 7620605570..6d9b27e0be 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -418,6 +418,7 @@ class ScriptEditor : public PanelContainer { bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); + virtual void input(const Ref<InputEvent> &p_event) override; virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override; void _script_list_gui_input(const Ref<InputEvent> &ev); diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp index 0f889ce33d..eb5e527640 100644 --- a/editor/plugins/sprite_2d_editor_plugin.cpp +++ b/editor/plugins/sprite_2d_editor_plugin.cpp @@ -182,7 +182,7 @@ void Sprite2DEditor::_update_mesh_data() { if (node->is_region_enabled()) { rect = node->get_region_rect(); } else { - rect.size = Size2(image->get_width(), image->get_height()); + rect.size = image->get_size(); } Ref<BitMap> bm; @@ -209,7 +209,7 @@ void Sprite2DEditor::_update_mesh_data() { computed_uv.clear(); computed_indices.clear(); - Size2 img_size = Vector2(image->get_width(), image->get_height()); + Size2 img_size = image->get_size(); for (int i = 0; i < lines.size(); i++) { lines.write[i] = expand(lines[i], rect, epsilon); } diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp index 44db06bcfd..b9ec6bf5ab 100644 --- a/editor/plugins/texture_editor_plugin.cpp +++ b/editor/plugins/texture_editor_plugin.cpp @@ -58,6 +58,21 @@ void TexturePreview::_notification(int p_what) { } } +void TexturePreview::_update_metadata_label_text() { + Ref<Texture2D> texture = texture_display->get_texture(); + + String format; + if (Object::cast_to<ImageTexture>(*texture)) { + format = Image::get_format_name(Object::cast_to<ImageTexture>(*texture)->get_format()); + } else if (Object::cast_to<StreamTexture2D>(*texture)) { + format = Image::get_format_name(Object::cast_to<StreamTexture2D>(*texture)->get_format()); + } else { + format = texture->get_class(); + } + + metadata_label->set_text(itos(texture->get_width()) + "x" + itos(texture->get_height()) + " " + format); +} + TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) { checkerboard = memnew(TextureRect); checkerboard->set_stretch_mode(TextureRect::STRETCH_TILE); @@ -75,16 +90,8 @@ TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) { if (p_show_metadata) { metadata_label = memnew(Label); - String format; - if (Object::cast_to<ImageTexture>(*p_texture)) { - format = Image::get_format_name(Object::cast_to<ImageTexture>(*p_texture)->get_format()); - } else if (Object::cast_to<StreamTexture2D>(*p_texture)) { - format = Image::get_format_name(Object::cast_to<StreamTexture2D>(*p_texture)->get_format()); - } else { - format = p_texture->get_class(); - } - - metadata_label->set_text(itos(p_texture->get_width()) + "x" + itos(p_texture->get_height()) + " " + format); + _update_metadata_label_text(); + p_texture->connect("changed", callable_mp(this, &TexturePreview::_update_metadata_label_text)); // It's okay that these colors are static since the grid color is static too. metadata_label->add_theme_color_override("font_color", Color::named("white")); diff --git a/editor/plugins/texture_editor_plugin.h b/editor/plugins/texture_editor_plugin.h index 36a5513ea6..60349febd7 100644 --- a/editor/plugins/texture_editor_plugin.h +++ b/editor/plugins/texture_editor_plugin.h @@ -44,6 +44,8 @@ private: TextureRect *checkerboard = nullptr; Label *metadata_label = nullptr; + void _update_metadata_label_text(); + protected: void _notification(int p_what); diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index a0d841ccca..ce90d61616 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -50,7 +50,7 @@ void draw_margin_line(Control *edit_draw, Vector2 from, Vector2 to) { EditorNode::get_singleton()->get_theme_base()->get_theme_color(SNAME("mono_color"), SNAME("Editor")).inverted() * Color(1, 1, 1, 0.5), Math::round(2 * EDSCALE)); - while ((to - from).length_squared() > 200) { + while (from.distance_squared_to(to) > 200) { edit_draw->draw_line( from, from + line, @@ -321,12 +321,12 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { prev_margin = margins[3]; } if (edited_margin >= 0) { - drag_from = Vector2(mb->get_position().x, mb->get_position().y); + drag_from = mb->get_position(); drag = true; } } if (edited_margin < 0 && snap_mode == SNAP_AUTOSLICE) { - Vector2 point = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y)); + Vector2 point = mtx.affine_inverse().xform(mb->get_position()); for (const Rect2 &E : autoslice_cache) { if (E.has_point(point)) { rect = E; @@ -372,7 +372,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { } } } else if (edited_margin < 0) { - drag_from = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y)); + drag_from = mtx.affine_inverse().xform(mb->get_position()); if (snap_mode == SNAP_PIXEL) { drag_from = drag_from.snapped(Vector2(1, 1)); } else if (snap_mode == SNAP_GRID) { @@ -393,7 +393,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { for (int i = 0; i < 8; i++) { Vector2 tuv = endpoints[i]; - if (tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)) < handle_radius) { + if (tuv.distance_to(mb->get_position()) < handle_radius) { drag_index = i; } } @@ -674,8 +674,7 @@ void TextureRegionEditor::_zoom_on_position(float p_zoom, Point2 p_position) { draw_zoom = p_zoom; Point2 ofs = p_position; ofs = ofs / prev_zoom - ofs / draw_zoom; - draw_ofs.x = Math::round(draw_ofs.x + ofs.x); - draw_ofs.y = Math::round(draw_ofs.y + ofs.y); + draw_ofs = (draw_ofs + ofs).round(); edit_draw->update(); } diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp index 801ee0eac2..d26d0ec39d 100644 --- a/editor/plugins/theme_editor_preview.cpp +++ b/editor/plugins/theme_editor_preview.cpp @@ -126,8 +126,8 @@ void ThemeEditorPreview::_draw_picker_overlay() { highlight_label_rect.size.x += margin_left + margin_right; highlight_label_rect.size.y += margin_top + margin_bottom; - highlight_label_rect.position.x = CLAMP(highlight_label_rect.position.x, 0.0, picker_overlay->get_size().width); - highlight_label_rect.position.y = CLAMP(highlight_label_rect.position.y, 0.0, picker_overlay->get_size().height); + highlight_label_rect.position = highlight_label_rect.position.clamp(Vector2(), picker_overlay->get_size()); + picker_overlay->draw_style_box(theme_cache.preview_picker_label, highlight_label_rect); Point2 label_pos = highlight_label_rect.position; diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp index d75013cfcf..2a8a3216ed 100644 --- a/editor/plugins/tiles/atlas_merging_dialog.cpp +++ b/editor/plugins/tiles/atlas_merging_dialog.cpp @@ -94,12 +94,14 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla } // Copy the texture. - Rect2i src_rect = atlas_source->get_tile_texture_region(tile_id); - Rect2 dst_rect_wide = Rect2i(new_tile_rect_in_altas.position * new_texture_region_size, new_tile_rect_in_altas.size * new_texture_region_size); - if (dst_rect_wide.get_end().x > output_image->get_width() || dst_rect_wide.get_end().y > output_image->get_height()) { - output_image->crop(MAX(dst_rect_wide.get_end().x, output_image->get_width()), MAX(dst_rect_wide.get_end().y, output_image->get_height())); + for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(tile_id); frame++) { + Rect2i src_rect = atlas_source->get_tile_texture_region(tile_id, frame); + Rect2 dst_rect_wide = Rect2i(new_tile_rect_in_altas.position * new_texture_region_size, new_tile_rect_in_altas.size * new_texture_region_size); + if (dst_rect_wide.get_end().x > output_image->get_width() || dst_rect_wide.get_end().y > output_image->get_height()) { + output_image->crop(MAX(dst_rect_wide.get_end().x, output_image->get_width()), MAX(dst_rect_wide.get_end().y, output_image->get_height())); + } + output_image->blit_rect(atlas_source->get_texture()->get_image(), src_rect, dst_rect_wide.get_center() - src_rect.size / 2); } - output_image->blit_rect(atlas_source->get_texture()->get_image(), src_rect, dst_rect_wide.get_center() - src_rect.size / 2); } // Compute the atlas offset. diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index 8d76f5f1b4..e98bd74b62 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -256,11 +256,15 @@ void TileAtlasView::_draw_base_tiles() { for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) { Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i); - // Update the y to max value. - Vector2i offset_pos = (margins + (atlas_coords * texture_region_size) + tile_set_atlas_source->get_tile_texture_region(atlas_coords).size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0)); + for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) { + // Update the y to max value. + int animation_columns = tile_set_atlas_source->get_tile_animation_columns(atlas_coords); + Vector2i frame_coords = atlas_coords + (tile_set_atlas_source->get_tile_size_in_atlas(atlas_coords) + tile_set_atlas_source->get_tile_animation_separation(atlas_coords)) * ((animation_columns > 0) ? Vector2i(frame % animation_columns, frame / animation_columns) : Vector2i(frame, 0)); + Vector2i offset_pos = (margins + (frame_coords * texture_region_size) + tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame).size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0)); - // Draw the tile. - TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0); + // Draw the tile. + TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0, frame); + } } } } @@ -326,13 +330,18 @@ void TileAtlasView::_draw_base_tiles_shape_grid() { for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) { Vector2i tile_id = tile_set_atlas_source->get_tile_id(i); Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_effective_texture_offset(tile_id, 0); - Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id); - // Draw only if the tile shape fits in the texture region - Transform2D tile_xform; - tile_xform.set_origin(texture_region.get_center() + in_tile_base_offset); - tile_xform.set_scale(tile_shape_size); - tile_set->draw_tile_shape(base_tiles_shape_grid, tile_xform, grid_color); + for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(tile_id); frame++) { + Color color = grid_color; + if (frame > 0) { + color.a *= 0.3; + } + Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id); + Transform2D tile_xform; + tile_xform.set_origin(texture_region.get_center() + in_tile_base_offset); + tile_xform.set_scale(tile_shape_size); + tile_set->draw_tile_shape(base_tiles_shape_grid, tile_xform, color); + } } } @@ -360,7 +369,7 @@ void TileAtlasView::_draw_alternatives() { Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i); current_pos.x = 0; int y_increment = 0; - Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(atlas_coords); + Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(atlas_coords).size; int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(atlas_coords); for (int j = 1; j < alternatives_count; j++) { int alternative_id = tile_set_atlas_source->get_alternative_tile_id(atlas_coords, j); @@ -370,18 +379,18 @@ void TileAtlasView::_draw_alternatives() { // Update the y to max value. Vector2i offset_pos = current_pos; if (transposed) { - offset_pos = (current_pos + Vector2(texture_region.size.y, texture_region.size.x) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id)); - y_increment = MAX(y_increment, texture_region.size.x); + offset_pos = (current_pos + Vector2(texture_region_size.y, texture_region_size.x) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id)); + y_increment = MAX(y_increment, texture_region_size.x); } else { - offset_pos = (current_pos + texture_region.size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id)); - y_increment = MAX(y_increment, texture_region.size.y); + offset_pos = (current_pos + texture_region_size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id)); + y_increment = MAX(y_increment, texture_region_size.y); } // Draw the tile. TileMap::draw_tile(alternatives_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, alternative_id); // Increment the x position. - current_pos.x += transposed ? texture_region.size.y : texture_region.size.x; + current_pos.x += transposed ? texture_region_size.y : texture_region_size.x; } if (alternatives_count > 1) { current_pos.y += y_increment; diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index ea7ca787c8..216c5f7c7d 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -622,7 +622,7 @@ void GenericTilePolygonEditor::_notification(int p_what) { button_delete->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveDelete"), SNAME("EditorIcons"))); button_center_view->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CenterView"), SNAME("EditorIcons"))); button_pixel_snap->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Snap"), SNAME("EditorIcons"))); - button_advanced_menu->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("GuiTabMenu"), SNAME("EditorIcons"))); + button_advanced_menu->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); break; } } diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 9fdf5044d9..a248f23517 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -790,7 +790,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over // Get the tile modulation. Color modulate = tile_data->get_modulate(); Color self_modulate = tile_map->get_self_modulate(); - modulate = Color(modulate.r * self_modulate.r, modulate.g * self_modulate.g, modulate.b * self_modulate.b, modulate.a * self_modulate.a); + modulate *= self_modulate; // Draw the tile. p_overlay->draw_texture_rect_region(atlas_source->get_texture(), dest_rect, source_rect, modulate * Color(1.0, 1.0, 1.0, 0.5), transpose, tile_set->is_uv_clipping()); @@ -1041,7 +1041,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i TypedArray<Vector2i> to_check; if (source.source_id == TileSet::INVALID_SOURCE) { Rect2i rect = tile_map->get_used_rect(); - if (rect.size.x <= 0 || rect.size.y <= 0) { + if (rect.has_no_area()) { rect = Rect2i(p_coords, Vector2i(1, 1)); } for (int x = boundaries.position.x; x < boundaries.get_end().x; x++) { @@ -1456,13 +1456,25 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() { Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0); for (Set<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) { if (E->get().source_id == source_id && E->get().alternative_tile == 0) { - tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get().get_atlas_coords()), selection_color, false); + for (int frame = 0; frame < atlas->get_tile_animation_frames_count(E->get().get_atlas_coords()); frame++) { + Color color = selection_color; + if (frame > 0) { + color.a *= 0.3; + } + tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get().get_atlas_coords(), frame), color, false); + } } } // Draw the hovered tile. if (hovered_tile.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0 && !tile_set_dragging_selection) { - tile_atlas_control->draw_rect(atlas->get_tile_texture_region(hovered_tile.get_atlas_coords()), Color(1.0, 1.0, 1.0), false); + for (int frame = 0; frame < atlas->get_tile_animation_frames_count(hovered_tile.get_atlas_coords()); frame++) { + Color color = Color(1.0, 1.0, 1.0); + if (frame > 0) { + color.a *= 0.3; + } + tile_atlas_control->draw_rect(atlas->get_tile_texture_region(hovered_tile.get_atlas_coords(), frame), color, false); + } } // Draw the selection rect. diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index 9370708a3e..1f1408016b 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -87,7 +87,7 @@ void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_get_property_list p_list->push_back(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D")); p_list->push_back(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "")); p_list->push_back(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "")); - p_list->push_back(PropertyInfo(Variant::VECTOR2I, "tile_size", PROPERTY_HINT_NONE, "")); + p_list->push_back(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, "")); } void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_bind_methods() { @@ -135,45 +135,81 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na const Vector2i &coords = tiles.front()->get().tile; const int &alternative = tiles.front()->get().alternative; - if (alternative == 0 && p_name == "atlas_coords") { - Vector2i as_vector2i = Vector2i(p_value); - ERR_FAIL_COND_V(!tile_set_atlas_source->can_move_tile_in_atlas(coords, as_vector2i), false); + if (alternative == 0) { + Vector<String> components = String(p_name).split("/", true, 2); + if (p_name == "atlas_coords") { + Vector2i as_vector2i = Vector2i(p_value); + bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(as_vector2i, tile_set_atlas_source->get_tile_size_in_atlas(coords), tile_set_atlas_source->get_tile_animation_columns(coords), tile_set_atlas_source->get_tile_animation_separation(coords), tile_set_atlas_source->get_tile_animation_frames_count(coords), coords); + ERR_FAIL_COND_V(!has_room_for_tile, false); + + if (tiles_set_atlas_source_editor->selection.front()->get().tile == coords) { + tiles_set_atlas_source_editor->selection.clear(); + tiles_set_atlas_source_editor->selection.insert({ as_vector2i, 0 }); + tiles_set_atlas_source_editor->_update_tile_id_label(); + } - if (tiles_set_atlas_source_editor->selection.front()->get().tile == coords) { - tiles_set_atlas_source_editor->selection.clear(); - tiles_set_atlas_source_editor->selection.insert({ as_vector2i, 0 }); - tiles_set_atlas_source_editor->_update_tile_id_label(); + tile_set_atlas_source->move_tile_in_atlas(coords, as_vector2i); + tiles.clear(); + tiles.insert({ as_vector2i, 0 }); + emit_signal(SNAME("changed"), "atlas_coords"); + return true; + } else if (p_name == "size_in_atlas") { + Vector2i as_vector2i = Vector2i(p_value); + bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(coords, as_vector2i, tile_set_atlas_source->get_tile_animation_columns(coords), tile_set_atlas_source->get_tile_animation_separation(coords), tile_set_atlas_source->get_tile_animation_frames_count(coords), coords); + ERR_FAIL_COND_V(!has_room_for_tile, false); + tile_set_atlas_source->move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i); + emit_signal(SNAME("changed"), "size_in_atlas"); + return true; + } else if (p_name == "animation_columns") { + bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(coords, tile_set_atlas_source->get_tile_size_in_atlas(coords), p_value, tile_set_atlas_source->get_tile_animation_separation(coords), tile_set_atlas_source->get_tile_animation_frames_count(coords), coords); + ERR_FAIL_COND_V(!has_room_for_tile, false); + tile_set_atlas_source->set_tile_animation_columns(coords, p_value); + emit_signal(SNAME("changed"), "animation_columns"); + return true; + } else if (p_name == "animation_separation") { + bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(coords, tile_set_atlas_source->get_tile_size_in_atlas(coords), tile_set_atlas_source->get_tile_animation_columns(coords), p_value, tile_set_atlas_source->get_tile_animation_frames_count(coords), coords); + ERR_FAIL_COND_V(!has_room_for_tile, false); + tile_set_atlas_source->set_tile_animation_separation(coords, p_value); + emit_signal(SNAME("changed"), "animation_separation"); + return true; + } else if (p_name == "animation_speed") { + tile_set_atlas_source->set_tile_animation_speed(coords, p_value); + emit_signal(SNAME("changed"), "animation_speed"); + return true; + } else if (p_name == "animation_frames_count") { + bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(coords, tile_set_atlas_source->get_tile_size_in_atlas(coords), tile_set_atlas_source->get_tile_animation_columns(coords), tile_set_atlas_source->get_tile_animation_separation(coords), p_value, coords); + ERR_FAIL_COND_V(!has_room_for_tile, false); + tile_set_atlas_source->set_tile_animation_frames_count(coords, p_value); + notify_property_list_changed(); + emit_signal(SNAME("changed"), "animation_separation"); + return true; + } else if (components.size() == 2 && components[0].begins_with("animation_frame_") && components[0].trim_prefix("animation_frame_").is_valid_int()) { + int frame = components[0].trim_prefix("animation_frame_").to_int(); + ERR_FAIL_INDEX_V(frame, tile_set_atlas_source->get_tile_animation_frames_count(coords), false); + if (components[1] == "duration") { + tile_set_atlas_source->set_tile_animation_frame_duration(coords, frame, p_value); + return true; + } } + } else if (alternative > 0) { + if (p_name == "alternative_id") { + int as_int = int(p_value); + ERR_FAIL_COND_V(as_int < 0, false); + ERR_FAIL_COND_V_MSG(tile_set_atlas_source->has_alternative_tile(coords, as_int), false, vformat("Cannot change alternative tile ID. Another alternative exists with id %d for tile at coords %s.", as_int, coords)); + + if (tiles_set_atlas_source_editor->selection.front()->get().alternative == alternative) { + tiles_set_atlas_source_editor->selection.clear(); + tiles_set_atlas_source_editor->selection.insert({ coords, as_int }); + } - tile_set_atlas_source->move_tile_in_atlas(coords, as_vector2i); - tiles.clear(); - tiles.insert({ as_vector2i, 0 }); - emit_signal(SNAME("changed"), "atlas_coords"); - return true; - } else if (alternative == 0 && p_name == "size_in_atlas") { - Vector2i as_vector2i = Vector2i(p_value); - ERR_FAIL_COND_V(!tile_set_atlas_source->can_move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i), false); + int previous_alternative_tile = alternative; + tiles.clear(); + tiles.insert({ coords, as_int }); // tiles must be updated before. + tile_set_atlas_source->set_alternative_tile_id(coords, previous_alternative_tile, as_int); - tile_set_atlas_source->move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i); - emit_signal(SNAME("changed"), "size_in_atlas"); - return true; - } else if (alternative > 0 && p_name == "alternative_id") { - int as_int = int(p_value); - ERR_FAIL_COND_V(as_int < 0, false); - ERR_FAIL_COND_V_MSG(tile_set_atlas_source->has_alternative_tile(coords, as_int), false, vformat("Cannot change alternative tile ID. Another alternative exists with id %d for tile at coords %s.", as_int, coords)); - - if (tiles_set_atlas_source_editor->selection.front()->get().alternative == alternative) { - tiles_set_atlas_source_editor->selection.clear(); - tiles_set_atlas_source_editor->selection.insert({ coords, as_int }); + emit_signal(SNAME("changed"), "alternative_id"); + return true; } - - int previous_alternative_tile = alternative; - tiles.clear(); - tiles.insert({ coords, as_int }); // tiles must be updated before. - tile_set_atlas_source->set_alternative_tile_id(coords, previous_alternative_tile, as_int); - - emit_signal(SNAME("changed"), "alternative_id"); - return true; } } @@ -206,15 +242,41 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_get(const StringName &p_na const Vector2i &coords = tiles.front()->get().tile; const int &alternative = tiles.front()->get().alternative; - if (alternative == 0 && p_name == "atlas_coords") { - r_ret = coords; - return true; - } else if (alternative == 0 && p_name == "size_in_atlas") { - r_ret = tile_set_atlas_source->get_tile_size_in_atlas(coords); - return true; - } else if (alternative > 0 && p_name == "alternative_id") { - r_ret = alternative; - return true; + if (alternative == 0) { + Vector<String> components = String(p_name).split("/", true, 2); + if (p_name == "atlas_coords") { + r_ret = coords; + return true; + } else if (p_name == "size_in_atlas") { + r_ret = tile_set_atlas_source->get_tile_size_in_atlas(coords); + return true; + } else if (p_name == "animation_columns") { + r_ret = tile_set_atlas_source->get_tile_animation_columns(coords); + return true; + } else if (p_name == "animation_separation") { + r_ret = tile_set_atlas_source->get_tile_animation_separation(coords); + return true; + } else if (p_name == "animation_speed") { + r_ret = tile_set_atlas_source->get_tile_animation_speed(coords); + return true; + } else if (p_name == "animation_frames_count") { + r_ret = tile_set_atlas_source->get_tile_animation_frames_count(coords); + return true; + } else if (components.size() == 2 && components[0].begins_with("animation_frame_") && components[0].trim_prefix("animation_frame_").is_valid_int()) { + int frame = components[0].trim_prefix("animation_frame_").to_int(); + if (components[1] == "duration") { + if (frame < 0 || frame >= tile_set_atlas_source->get_tile_animation_frames_count(coords)) { + return false; + } + r_ret = tile_set_atlas_source->get_tile_animation_frame_duration(coords, frame); + return true; + } + } + } else if (alternative > 0) { + if (p_name == "alternative_id") { + r_ret = alternative; + return true; + } } } @@ -246,6 +308,20 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro if (tiles.front()->get().alternative == 0) { p_list->push_back(PropertyInfo(Variant::VECTOR2I, "atlas_coords", PROPERTY_HINT_NONE, "")); p_list->push_back(PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "")); + + // Animation. + p_list->push_back(PropertyInfo(Variant::NIL, "Animation", PROPERTY_HINT_NONE, "animation_", PROPERTY_USAGE_GROUP)); + p_list->push_back(PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "")); + p_list->push_back(PropertyInfo(Variant::VECTOR2I, "animation_separation", PROPERTY_HINT_NONE, "")); + p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, "")); + p_list->push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Frames,animation_frame_")); + if (tile_set_atlas_source->get_tile_animation_frames_count(tiles.front()->get().tile) == 1) { + p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_frame_0/duration", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); + } else { + for (int i = 0; i < tile_set_atlas_source->get_tile_animation_frames_count(tiles.front()->get().tile); i++) { + p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "")); + } + } } else { p_list->push_back(PropertyInfo(Variant::INT, "alternative_id", PROPERTY_HINT_NONE, "")); } @@ -846,15 +922,15 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven CursorShape cursor_shape = CURSOR_ARROW; bool can_grow[4]; for (int i = 0; i < 4; i++) { - can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]); + can_grow[i] = tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), tile_set_atlas_source->get_tile_animation_columns(selected.tile), tile_set_atlas_source->get_tile_animation_separation(selected.tile), tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile); can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1; } for (int i = 0; i < 4; i++) { - Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i]; + Vector2 pos = rect.position + rect.size * coords[i]; if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) { cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE; } - Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4]; + Vector2 next_pos = rect.position + rect.size * coords[(i + 1) % 4]; if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) { cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE; } @@ -869,7 +945,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven Rect2i new_rect = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs(); new_rect.size += Vector2i(1, 1); // Check if the new tile can fit in the new rect. - if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) { + if (tile_set_atlas_source->has_room_for_tile(new_rect.position, new_rect.size, tile_set_atlas_source->get_tile_animation_columns(drag_current_tile), tile_set_atlas_source->get_tile_animation_separation(drag_current_tile), tile_set_atlas_source->get_tile_animation_frames_count(drag_current_tile), drag_current_tile)) { // Move and resize the tile. tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size); drag_current_tile = new_rect.position; @@ -908,7 +984,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven Vector2 mouse_offset = (Vector2(tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile)) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size(); Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position() - mouse_offset); coords = coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1)); - if (drag_current_tile != coords && tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, coords)) { + if (drag_current_tile != coords && tile_set_atlas_source->has_room_for_tile(coords, tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile), tile_set_atlas_source->get_tile_animation_columns(drag_current_tile), tile_set_atlas_source->get_tile_animation_separation(drag_current_tile), tile_set_atlas_source->get_tile_animation_frames_count(drag_current_tile), drag_current_tile)) { tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, coords); selection.clear(); selection.insert({ coords, 0 }); @@ -948,7 +1024,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven new_rect.set_end(Vector2i(new_rect.get_end().x, MAX(new_base_tiles_coords.y, old_rect.position.y + 1))); } - if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) { + if (tile_set_atlas_source->has_room_for_tile(new_rect.position, new_rect.size, tile_set_atlas_source->get_tile_animation_columns(drag_current_tile), tile_set_atlas_source->get_tile_animation_separation(drag_current_tile), tile_set_atlas_source->get_tile_animation_frames_count(drag_current_tile), drag_current_tile)) { tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size); selection.clear(); selection.insert({ new_rect.position, 0 }); @@ -1056,11 +1132,11 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven CursorShape cursor_shape = CURSOR_ARROW; bool can_grow[4]; for (int i = 0; i < 4; i++) { - can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]); + can_grow[i] = tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), tile_set_atlas_source->get_tile_animation_columns(selected.tile), tile_set_atlas_source->get_tile_animation_separation(selected.tile), tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile); can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1; } for (int i = 0; i < 4; i++) { - Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i]; + Vector2 pos = rect.position + rect.size * coords[i]; if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) { drag_type = (DragType)((int)DRAG_TYPE_RESIZE_TOP_LEFT + i * 2); drag_start_mouse_pos = mouse_local_pos; @@ -1069,7 +1145,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile)); cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE; } - Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4]; + Vector2 next_pos = rect.position + rect.size * coords[(i + 1) % 4]; if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) { drag_type = (DragType)((int)DRAG_TYPE_RESIZE_TOP + i * 2); drag_start_mouse_pos = mouse_local_pos; @@ -1511,37 +1587,45 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { TileSelection selected = E->get(); if (selected.alternative == 0) { // Draw the rect. - Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile); - tile_atlas_control->draw_rect(region, selection_color, false); + for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(selected.tile); frame++) { + Color color = selection_color; + if (frame > 0) { + color.a *= 0.3; + } + Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile, frame); + tile_atlas_control->draw_rect(region, color, false); + } } } if (selection.size() == 1) { // Draw the resize handles (only when it's possible to expand). TileSelection selected = selection.front()->get(); - Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile); - Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom(); - Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile); - Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0); - Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) }; - Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) }; - bool can_grow[4]; - for (int i = 0; i < 4; i++) { - can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]); - can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1; - } - for (int i = 0; i < 4; i++) { - Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i]; - if (can_grow[i] && can_grow[(i + 3) % 4]) { - tile_atlas_control->draw_texture_rect(resize_handle, Rect2(pos, zoomed_size), false); - } else { - tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2(pos, zoomed_size), false); + if (selected.alternative == 0) { + Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile); + Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom(); + Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile); + Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0); + Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) }; + Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) }; + bool can_grow[4]; + for (int i = 0; i < 4; i++) { + can_grow[i] = tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), tile_set_atlas_source->get_tile_animation_columns(selected.tile), tile_set_atlas_source->get_tile_animation_separation(selected.tile), tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile); + can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1; } - Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4]; - if (can_grow[i]) { - tile_atlas_control->draw_texture_rect(resize_handle, Rect2((pos + next_pos) / 2.0, zoomed_size), false); - } else { - tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2((pos + next_pos) / 2.0, zoomed_size), false); + for (int i = 0; i < 4; i++) { + Vector2 pos = rect.position + rect.size * coords[i]; + if (can_grow[i] && can_grow[(i + 3) % 4]) { + tile_atlas_control->draw_texture_rect(resize_handle, Rect2(pos, zoomed_size), false); + } else { + tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2(pos, zoomed_size), false); + } + Vector2 next_pos = rect.position + rect.size * coords[(i + 1) % 4]; + if (can_grow[i]) { + tile_atlas_control->draw_texture_rect(resize_handle, Rect2((pos + next_pos) / 2.0, zoomed_size), false); + } else { + tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2((pos + next_pos) / 2.0, zoomed_size), false); + } } } } @@ -1550,7 +1634,9 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { if (drag_type == DRAG_TYPE_REMOVE_TILES) { // Draw the tiles to be removed. for (Set<Vector2i>::Element *E = drag_modified_tiles.front(); E; E = E->next()) { - tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(E->get()), Color(0.0, 0.0, 0.0), false); + for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(E->get()); frame++) { + tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(E->get(), frame), Color(0.0, 0.0, 0.0), false); + } } } else if (drag_type == DRAG_TYPE_RECT_SELECT || drag_type == DRAG_TYPE_REMOVE_TILES_USING_RECT) { // Draw tiles to be removed. @@ -1617,7 +1703,13 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() { Vector2i hovered_tile = tile_set_atlas_source->get_tile_at_coords(hovered_base_tile_coords); if (hovered_tile != TileSetSource::INVALID_ATLAS_COORDS) { // Draw existing hovered tile. - tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(hovered_tile), Color(1.0, 1.0, 1.0), false); + for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(hovered_tile); frame++) { + Color color = Color(1.0, 1.0, 1.0); + if (frame > 0) { + color.a *= 0.3; + } + tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(hovered_tile, frame), color, false); + } } else { // Draw empty tile, only in add/remove tiles mode. if (tools_button_group->get_pressed_button() == tool_setup_atlas_source_button) { @@ -1854,6 +1946,11 @@ void TileSetAtlasSourceEditor::_tile_set_atlas_source_changed() { tile_set_atlas_source_changed_needs_update = true; } +void TileSetAtlasSourceEditor::_tile_proxy_object_changed(String p_what) { + tile_set_atlas_source_changed_needs_update = false; // Avoid updating too many things. + _update_atlas_view(); +} + void TileSetAtlasSourceEditor::_atlas_source_proxy_object_changed(String p_what) { if (p_what == "texture" && !atlas_source_proxy_object->get("texture").is_null()) { confirm_auto_create_tiles->popup_centered(); @@ -2114,7 +2211,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { middle_vbox_container->add_child(tile_inspector_label); tile_proxy_object = memnew(AtlasTileProxyObject(this)); - tile_proxy_object->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view).unbind(1)); + tile_proxy_object->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_proxy_object_changed)); tile_inspector = memnew(EditorInspector); tile_inspector->set_undo_redo(undo_redo); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h index 501416c340..6448b55feb 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.h +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h @@ -264,6 +264,7 @@ private: AcceptDialog *confirm_auto_create_tiles; void _tile_set_atlas_source_changed(); + void _tile_proxy_object_changed(String p_what); void _atlas_source_proxy_object_changed(String p_what); void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value); diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp index 79b869b511..f51f4625a9 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.cpp +++ b/editor/plugins/tiles/tiles_editor_plugin.cpp @@ -146,7 +146,7 @@ void TilesEditor::synchronize_atlas_view(Object *p_current) { ERR_FAIL_COND(!tile_atlas_view); if (tile_atlas_view->is_visible_in_tree()) { - tile_atlas_view->set_transform(atlas_view_zoom, Vector2(atlas_view_scroll.x, atlas_view_scroll.y)); + tile_atlas_view->set_transform(atlas_view_zoom, atlas_view_scroll); } } diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index aaa085675c..27f30479cf 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -1945,7 +1945,7 @@ void VisualShaderEditor::_set_node_size(int p_type, int p_node, const Vector2 &p box_size.x -= 28 * EDSCALE; box_size.y -= text_box->get_offset(SIDE_TOP); box_size.y -= 28 * EDSCALE; - text_box->set_custom_minimum_size(Size2(box_size.x, box_size.y)); + text_box->set_custom_minimum_size(box_size); text_box->set_size(Size2(1, 1)); } } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 4bc0905163..5ce88949ef 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -848,7 +848,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { break; } Ref<MultiNodeEdit> mne = memnew(MultiNodeEdit); - for (const Map<Node *, Object *>::Element *E = EditorNode::get_singleton()->get_editor_selection()->get_selection().front(); E; E = E->next()) { + for (const Map<Node *, Object *>::Element *E = editor_selection->get_selection().front(); E; E = E->next()) { mne->add_node(root->get_path_to(E->key())); } @@ -2101,11 +2101,11 @@ void SceneTreeDock::_update_script_button() { if (!profile_allow_script_editing) { button_create_script->hide(); button_detach_script->hide(); - } else if (EditorNode::get_singleton()->get_editor_selection()->get_selection().size() == 0) { + } else if (editor_selection->get_selection().size() == 0) { button_create_script->hide(); button_detach_script->hide(); - } else if (EditorNode::get_singleton()->get_editor_selection()->get_selection().size() == 1) { - Node *n = EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list()[0]; + } else if (editor_selection->get_selection().size() == 1) { + Node *n = editor_selection->get_selected_node_list()[0]; if (n->get_script().is_null()) { button_create_script->show(); button_detach_script->hide(); @@ -2128,10 +2128,12 @@ void SceneTreeDock::_update_script_button() { } void SceneTreeDock::_selection_changed() { - int selection_size = EditorNode::get_singleton()->get_editor_selection()->get_selection().size(); + int selection_size = editor_selection->get_selection().size(); if (selection_size > 1) { //automatically turn on multi-edit _tool_selected(TOOL_MULTI_EDIT); + } else if (selection_size == 1) { + editor->push_item(editor_selection->get_selection().front()->key()); } else if (selection_size == 0) { editor->push_item(nullptr); } diff --git a/main/main.cpp b/main/main.cpp index fc24010e7b..d512c41e7a 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -2689,8 +2689,9 @@ void Main::cleanup(bool p_force) { rendering_server->global_variables_clear(); if (xr_server) { - // cleanup now before we pull the rug from underneath... - memdelete(xr_server); + // Now that we're unregistering properly in plugins we need to keep access to xr_server for a little longer + // We do however unset our primary interface + xr_server->set_primary_interface(Ref<XRInterface>()); } unregister_driver_types(); @@ -2706,6 +2707,10 @@ void Main::cleanup(bool p_force) { unregister_scene_types(); unregister_server_types(); + if (xr_server) { + memdelete(xr_server); + } + if (audio_server) { audio_server->finish(); memdelete(audio_server); diff --git a/methods.py b/methods.py index 50b413a0e6..0e71adb40d 100644 --- a/methods.py +++ b/methods.py @@ -56,6 +56,17 @@ def disable_warnings(self): self.Append(CXXFLAGS=["-w"]) +def force_optimization_on_debug(self): + # 'self' is the environment + if self["target"] != "debug": + return + + if self.msvc: + self.Append(CCFLAGS=["/O2"]) + else: + self.Append(CCFLAGS=["-O3"]) + + def add_module_version_string(self, s): self.module_version_string += "." + s diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index 0d2cd1f5a0..7b20fad28c 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -303,6 +303,7 @@ RigidBodyBullet::~RigidBodyBullet() { void RigidBodyBullet::init_kinematic_utilities() { kinematic_utilities = memnew(KinematicUtilities(this)); + reload_kinematic_shapes(); } void RigidBodyBullet::destroy_kinematic_utilities() { @@ -529,26 +530,23 @@ void RigidBodyBullet::set_mode(PhysicsServer3D::BodyMode p_mode) { can_integrate_forces = false; destroy_kinematic_utilities(); // The mode change is relevant to its mass + mode = p_mode; switch (p_mode) { case PhysicsServer3D::BODY_MODE_KINEMATIC: - mode = PhysicsServer3D::BODY_MODE_KINEMATIC; reload_axis_lock(); _internal_set_mass(0); init_kinematic_utilities(); break; case PhysicsServer3D::BODY_MODE_STATIC: - mode = PhysicsServer3D::BODY_MODE_STATIC; reload_axis_lock(); _internal_set_mass(0); break; case PhysicsServer3D::BODY_MODE_DYNAMIC: - mode = PhysicsServer3D::BODY_MODE_DYNAMIC; reload_axis_lock(); _internal_set_mass(0 == mass ? 1 : mass); scratch_space_override_modificator(); break; - case PhysicsServer3D::MODE_DYNAMIC_LOCKED: - mode = PhysicsServer3D::MODE_DYNAMIC_LOCKED; + case PhysicsServer3D::MODE_DYNAMIC_LINEAR: reload_axis_lock(); _internal_set_mass(0 == mass ? 1 : mass); scratch_space_override_modificator(); @@ -721,7 +719,7 @@ bool RigidBodyBullet::is_axis_locked(PhysicsServer3D::BodyAxis p_axis) const { void RigidBodyBullet::reload_axis_lock() { btBody->setLinearFactor(btVector3(btScalar(!is_axis_locked(PhysicsServer3D::BODY_AXIS_LINEAR_X)), btScalar(!is_axis_locked(PhysicsServer3D::BODY_AXIS_LINEAR_Y)), btScalar(!is_axis_locked(PhysicsServer3D::BODY_AXIS_LINEAR_Z)))); - if (PhysicsServer3D::MODE_DYNAMIC_LOCKED == mode) { + if (PhysicsServer3D::MODE_DYNAMIC_LINEAR == mode) { /// When character angular is always locked btBody->setAngularFactor(btVector3(0., 0., 0.)); } else { @@ -1016,7 +1014,7 @@ void RigidBodyBullet::_internal_set_mass(real_t p_mass) { // Rigidbody is dynamic if and only if mass is non Zero, otherwise static const bool isDynamic = p_mass != 0.f; if (isDynamic) { - if (PhysicsServer3D::BODY_MODE_DYNAMIC != mode && PhysicsServer3D::MODE_DYNAMIC_LOCKED != mode) { + if (PhysicsServer3D::BODY_MODE_DYNAMIC != mode && PhysicsServer3D::MODE_DYNAMIC_LINEAR != mode) { return; } diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index a9a811c445..0cfd658bd5 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -947,7 +947,6 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform3D &p if (!p_body->get_kinematic_utilities()) { p_body->init_kinematic_utilities(); - p_body->reload_kinematic_shapes(); } btVector3 initial_recover_motion(0, 0, 0); @@ -1089,7 +1088,6 @@ int SpaceBullet::test_ray_separation(RigidBodyBullet *p_body, const Transform3D if (!p_body->get_kinematic_utilities()) { p_body->init_kinematic_utilities(); - p_body->reload_kinematic_shapes(); } btVector3 recover_motion(0, 0, 0); diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp index cbe41a1310..229e1e2724 100644 --- a/modules/csg/csg.cpp +++ b/modules/csg/csg.cpp @@ -37,16 +37,16 @@ // Static helper functions. inline static bool is_snapable(const Vector3 &p_point1, const Vector3 &p_point2, real_t p_distance) { - return (p_point1 - p_point2).length_squared() < p_distance * p_distance; + return p_point2.distance_squared_to(p_point1) < p_distance * p_distance; } -inline static Vector2 interpolate_segment_uv(const Vector2 p_segement_points[2], const Vector2 p_uvs[2], const Vector2 &p_interpolation_point) { - float segment_length = (p_segement_points[1] - p_segement_points[0]).length(); - if (p_segement_points[0].is_equal_approx(p_segement_points[1])) { +inline static Vector2 interpolate_segment_uv(const Vector2 p_segment_points[2], const Vector2 p_uvs[2], const Vector2 &p_interpolation_point) { + if (p_segment_points[0].is_equal_approx(p_segment_points[1])) { return p_uvs[0]; } - float distance = (p_interpolation_point - p_segement_points[0]).length(); + float segment_length = p_segment_points[0].distance_to(p_segment_points[1]); + float distance = p_segment_points[0].distance_to(p_interpolation_point); float fraction = distance / segment_length; return p_uvs[0].lerp(p_uvs[1], fraction); @@ -162,7 +162,7 @@ inline static bool is_triangle_degenerate(const Vector2 p_vertices[3], real_t p_ return det < p_vertex_snap2; } -inline static bool are_segements_parallel(const Vector2 p_segment1_points[2], const Vector2 p_segment2_points[2], float p_vertex_snap2) { +inline static bool are_segments_parallel(const Vector2 p_segment1_points[2], const Vector2 p_segment2_points[2], float p_vertex_snap2) { Vector2 segment1 = p_segment1_points[1] - p_segment1_points[0]; Vector2 segment2 = p_segment2_points[1] - p_segment2_points[0]; real_t segment1_length2 = segment1.dot(segment1); @@ -596,7 +596,7 @@ bool CSGBrushOperation::MeshMerge::_bvh_inside(FaceBVH *facebvhptr, int p_max_de _add_distance(intersectionsA, intersectionsB, current_face.from_b, 0); } } else if (ray_intersects_triangle(face_center, face_normal, current_points, CMP_EPSILON, intersection_point)) { - real_t distance = (intersection_point - face_center).length(); + real_t distance = face_center.distance_to(intersection_point); _add_distance(intersectionsA, intersectionsB, current_face.from_b, distance); } } @@ -781,7 +781,7 @@ void CSGBrushOperation::MeshMerge::add_face(const Vector3 p_points[], const Vect int CSGBrushOperation::Build2DFaces::_get_point_idx(const Vector2 &p_point) { for (int vertex_idx = 0; vertex_idx < vertices.size(); ++vertex_idx) { - if ((p_point - vertices[vertex_idx].point).length_squared() < vertex_snap2) { + if (vertices[vertex_idx].point.distance_squared_to(p_point) < vertex_snap2) { return vertex_idx; } } @@ -911,7 +911,7 @@ void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector<int> &p_segment_ vertices[outer_edge_idx[1]].point, vertices[p_segment_indices[closest_idx]].point }; - if (are_segements_parallel(edge1, edge2, vertex_snap2)) { + if (are_segments_parallel(edge1, edge2, vertex_snap2)) { if (!degenerate_points.find(outer_edge_idx[0])) { degenerate_points.push_back(outer_edge_idx[0]); } @@ -961,7 +961,7 @@ void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector<int> &p_segment_ // Check if point is existing face vertex. bool existing = false; for (int i = 0; i < 3; ++i) { - if ((point_2D - face_vertices[i].point).length_squared() < vertex_snap2) { + if (face_vertices[i].point.distance_squared_to(point_2D) < vertex_snap2) { existing = true; break; } @@ -978,7 +978,7 @@ void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector<int> &p_segment_ }; Vector2 closest_point = Geometry2D::get_closest_point_to_segment(point_2D, edge_points); - if ((closest_point - point_2D).length_squared() < vertex_snap2) { + if (point_2D.distance_squared_to(closest_point) < vertex_snap2) { int opposite_vertex_idx = face.vertex_idx[(face_edge_idx + 2) % 3]; // If new vertex snaps to degenerate vertex, just delete this face. @@ -1041,7 +1041,7 @@ void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_s bool on_edge = false; for (int edge_point_idx = 0; edge_point_idx < 2; ++edge_point_idx) { intersection_point = Geometry2D::get_closest_point_to_segment(p_segment_points[edge_point_idx], edge_points); - if ((intersection_point - p_segment_points[edge_point_idx]).length_squared() < vertex_snap2) { + if (p_segment_points[edge_point_idx].distance_squared_to(intersection_point) < vertex_snap2) { on_edge = true; break; } @@ -1050,13 +1050,13 @@ void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_s // Else check if the segment intersects the edge. if (on_edge || Geometry2D::segment_intersects_segment(p_segment_points[0], p_segment_points[1], edge_points[0], edge_points[1], &intersection_point)) { // Check if intersection point is an edge point. - if ((intersection_point - edge_points[0]).length_squared() < vertex_snap2 || - (intersection_point - edge_points[1]).length_squared() < vertex_snap2) { + if ((edge_points[0].distance_squared_to(intersection_point) < vertex_snap2) || + (edge_points[1].distance_squared_to(intersection_point) < vertex_snap2)) { continue; } // Check if edge exists, by checking if the intersecting segment is parallel to the edge. - if (are_segements_parallel(p_segment_points, edge_points, vertex_snap2)) { + if (are_segments_parallel(p_segment_points, edge_points, vertex_snap2)) { continue; } @@ -1078,7 +1078,7 @@ void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_s // If opposite point is on the segment, add its index to segment indices too. Vector2 closest_point = Geometry2D::get_closest_point_to_segment(vertices[opposite_vertex_idx].point, p_segment_points); - if ((closest_point - vertices[opposite_vertex_idx].point).length_squared() < vertex_snap2) { + if (vertices[opposite_vertex_idx].point.distance_squared_to(closest_point) < vertex_snap2) { _add_vertex_idx_sorted(r_segment_indices, opposite_vertex_idx); } @@ -1132,7 +1132,7 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) { // Check if point is existing face vertex. for (int i = 0; i < 3; ++i) { - if ((p_point - face_vertices[i].point).length_squared() < vertex_snap2) { + if (face_vertices[i].point.distance_squared_to(p_point) < vertex_snap2) { return face.vertex_idx[i]; } } @@ -1150,7 +1150,7 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) { }; Vector2 closest_point = Geometry2D::get_closest_point_to_segment(p_point, edge_points); - if ((closest_point - p_point).length_squared() < vertex_snap2) { + if (p_point.distance_squared_to(closest_point) < vertex_snap2) { on_edge = true; // Add the point as a new vertex. @@ -1172,8 +1172,8 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) { Vector2 split_edge1[2] = { vertices[new_vertex_idx].point, edge_points[0] }; Vector2 split_edge2[2] = { vertices[new_vertex_idx].point, edge_points[1] }; Vector2 new_edge[2] = { vertices[new_vertex_idx].point, vertices[opposite_vertex_idx].point }; - if (are_segements_parallel(split_edge1, new_edge, vertex_snap2) && - are_segements_parallel(split_edge2, new_edge, vertex_snap2)) { + if (are_segments_parallel(split_edge1, new_edge, vertex_snap2) && + are_segments_parallel(split_edge2, new_edge, vertex_snap2)) { break; } diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 452fb32d9d..e4297a593e 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -1732,6 +1732,7 @@ CSGBrush *CSGPolygon3D::_build_brush() { int extrusion_face_count = shape_sides * 2; int end_count = 0; int shape_face_count = shape_faces.size() / 3; + real_t curve_length = 1.0; switch (mode) { case MODE_DEPTH: extrusions = 1; @@ -1744,7 +1745,12 @@ CSGBrush *CSGPolygon3D::_build_brush() { } break; case MODE_PATH: { - extrusions = Math::ceil(1.0 * curve->get_point_count() / path_interval); + curve_length = curve->get_baked_length(); + if (path_interval_type == PATH_INTERVAL_DISTANCE) { + extrusions = MAX(1, Math::ceil(curve_length / path_interval)) + 1; + } else { + extrusions = Math::ceil(1.0 * curve->get_point_count() / path_interval); + } if (!path_joined) { end_count = 2; extrusions -= 1; @@ -1767,212 +1773,245 @@ CSGBrush *CSGPolygon3D::_build_brush() { smooth.resize(face_count); materials.resize(face_count); invert.resize(face_count); + int faces_removed = 0; - Vector3 *facesw = faces.ptrw(); - Vector2 *uvsw = uvs.ptrw(); - bool *smoothw = smooth.ptrw(); - Ref<Material> *materialsw = materials.ptrw(); - bool *invertw = invert.ptrw(); - - int face = 0; - Transform3D base_xform; - Transform3D current_xform; - Transform3D previous_xform; - double u_step = 1.0 / extrusions; - double v_step = 1.0 / shape_sides; - double spin_step = Math::deg2rad(spin_degrees / spin_sides); - double extrusion_step = 1.0 / extrusions; - if (mode == MODE_PATH) { - if (path_joined) { - extrusion_step = 1.0 / (extrusions - 1); - } - extrusion_step *= curve->get_baked_length(); - } + { + Vector3 *facesw = faces.ptrw(); + Vector2 *uvsw = uvs.ptrw(); + bool *smoothw = smooth.ptrw(); + Ref<Material> *materialsw = materials.ptrw(); + bool *invertw = invert.ptrw(); - if (mode == MODE_PATH) { - if (!path_local) { - base_xform = path->get_global_transform(); + int face = 0; + Transform3D base_xform; + Transform3D current_xform; + Transform3D previous_xform; + Transform3D previous_previous_xform; + double u_step = 1.0 / extrusions; + if (path_u_distance > 0.0) { + u_step *= curve_length / path_u_distance; + } + double v_step = 1.0 / shape_sides; + double spin_step = Math::deg2rad(spin_degrees / spin_sides); + double extrusion_step = 1.0 / extrusions; + if (mode == MODE_PATH) { + if (path_joined) { + extrusion_step = 1.0 / (extrusions - 1); + } + extrusion_step *= curve_length; } - Vector3 current_point = curve->interpolate_baked(0); - Vector3 next_point = curve->interpolate_baked(extrusion_step); - Vector3 current_up = Vector3(0, 1, 0); - Vector3 direction = next_point - current_point; + if (mode == MODE_PATH) { + if (!path_local) { + base_xform = path->get_global_transform(); + } - if (path_joined) { - Vector3 last_point = curve->interpolate_baked(curve->get_baked_length()); - direction = next_point - last_point; - } + Vector3 current_point = curve->interpolate_baked(0); + Vector3 next_point = curve->interpolate_baked(extrusion_step); + Vector3 current_up = Vector3(0, 1, 0); + Vector3 direction = next_point - current_point; - switch (path_rotation) { - case PATH_ROTATION_POLYGON: - direction = Vector3(0, 0, -1); - break; - case PATH_ROTATION_PATH: - break; - case PATH_ROTATION_PATH_FOLLOW: - current_up = curve->interpolate_baked_up_vector(0); - break; + if (path_joined) { + Vector3 last_point = curve->interpolate_baked(curve->get_baked_length()); + direction = next_point - last_point; + } + + switch (path_rotation) { + case PATH_ROTATION_POLYGON: + direction = Vector3(0, 0, -1); + break; + case PATH_ROTATION_PATH: + break; + case PATH_ROTATION_PATH_FOLLOW: + current_up = curve->interpolate_baked_up_vector(0); + break; + } + + Transform3D facing = Transform3D().looking_at(direction, current_up); + current_xform = base_xform.translated(current_point) * facing; } - Transform3D facing = Transform3D().looking_at(direction, current_up); - current_xform = base_xform.translated(current_point) * facing; - } + // Create the mesh. + if (end_count > 0) { + // Add front end face. + for (int face_idx = 0; face_idx < shape_face_count; face_idx++) { + for (int face_vertex_idx = 0; face_vertex_idx < 3; face_vertex_idx++) { + // We need to reverse the rotation of the shape face vertices. + int index = shape_faces[face_idx * 3 + 2 - face_vertex_idx]; + Point2 p = shape_polygon[index]; + Point2 uv = (p - shape_rect.position) / shape_rect.size; + + // Use the left side of the bottom half of the y-inverted texture. + uv.x = uv.x / 2; + uv.y = 1 - (uv.y / 2); + + facesw[face * 3 + face_vertex_idx] = current_xform.xform(Vector3(p.x, p.y, 0)); + uvsw[face * 3 + face_vertex_idx] = uv; + } - // Create the mesh. - if (end_count > 0) { - // Add front end face. - for (int face_idx = 0; face_idx < shape_face_count; face_idx++) { - for (int face_vertex_idx = 0; face_vertex_idx < 3; face_vertex_idx++) { - // We need to reverse the rotation of the shape face vertices. - int index = shape_faces[face_idx * 3 + 2 - face_vertex_idx]; - Point2 p = shape_polygon[index]; - Point2 uv = (p - shape_rect.position) / shape_rect.size; - - // Use the left side of the bottom half of the y-inverted texture. - uv.x = uv.x / 2; - uv.y = 1 - (uv.y / 2); - - facesw[face * 3 + face_vertex_idx] = current_xform.xform(Vector3(p.x, p.y, 0)); - uvsw[face * 3 + face_vertex_idx] = uv; + smoothw[face] = false; + materialsw[face] = material; + invertw[face] = invert_faces; + face++; } - - smoothw[face] = false; - materialsw[face] = material; - invertw[face] = invert_faces; - face++; } - } - // Add extrusion faces. - for (int x0 = 0; x0 < extrusions; x0++) { - previous_xform = current_xform; - - switch (mode) { - case MODE_DEPTH: { - current_xform.translate(Vector3(0, 0, -depth)); - } break; - case MODE_SPIN: { - current_xform.rotate(Vector3(0, 1, 0), spin_step); - } break; - case MODE_PATH: { - double previous_offset = x0 * extrusion_step; - double current_offset = (x0 + 1) * extrusion_step; - double next_offset = (x0 + 2) * extrusion_step; - if (x0 == extrusions - 1) { - if (path_joined) { - current_offset = 0; - next_offset = extrusion_step; + real_t angle_simplify_dot = Math::cos(Math::deg2rad(path_simplify_angle)); + Vector3 previous_simplify_dir = Vector3(0, 0, 0); + int faces_combined = 0; + + // Add extrusion faces. + for (int x0 = 0; x0 < extrusions; x0++) { + previous_previous_xform = previous_xform; + previous_xform = current_xform; + + switch (mode) { + case MODE_DEPTH: { + current_xform.translate(Vector3(0, 0, -depth)); + } break; + case MODE_SPIN: { + current_xform.rotate(Vector3(0, 1, 0), spin_step); + } break; + case MODE_PATH: { + double previous_offset = x0 * extrusion_step; + double current_offset = (x0 + 1) * extrusion_step; + double next_offset = (x0 + 2) * extrusion_step; + if (x0 == extrusions - 1) { + if (path_joined) { + current_offset = 0; + next_offset = extrusion_step; + } else { + next_offset = current_offset; + } + } + + Vector3 previous_point = curve->interpolate_baked(previous_offset); + Vector3 current_point = curve->interpolate_baked(current_offset); + Vector3 next_point = curve->interpolate_baked(next_offset); + Vector3 current_up = Vector3(0, 1, 0); + Vector3 direction = next_point - previous_point; + Vector3 current_dir = (current_point - previous_point).normalized(); + + // If the angles are similar, remove the previous face and replace it with this one. + if (path_simplify_angle > 0.0 && x0 > 0 && previous_simplify_dir.dot(current_dir) > angle_simplify_dot) { + faces_combined += 1; + previous_xform = previous_previous_xform; + face -= extrusion_face_count; + faces_removed += extrusion_face_count; } else { - next_offset = current_offset; + faces_combined = 0; + previous_simplify_dir = current_dir; } - } - Vector3 previous_point = curve->interpolate_baked(previous_offset); - Vector3 current_point = curve->interpolate_baked(current_offset); - Vector3 next_point = curve->interpolate_baked(next_offset); - Vector3 current_up = Vector3(0, 1, 0); - Vector3 direction = next_point - previous_point; + switch (path_rotation) { + case PATH_ROTATION_POLYGON: + direction = Vector3(0, 0, -1); + break; + case PATH_ROTATION_PATH: + break; + case PATH_ROTATION_PATH_FOLLOW: + current_up = curve->interpolate_baked_up_vector(current_offset); + break; + } - switch (path_rotation) { - case PATH_ROTATION_POLYGON: - direction = Vector3(0, 0, -1); - break; - case PATH_ROTATION_PATH: - break; - case PATH_ROTATION_PATH_FOLLOW: - current_up = curve->interpolate_baked_up_vector(current_offset); - break; - } + Transform3D facing = Transform3D().looking_at(direction, current_up); + current_xform = base_xform.translated(current_point) * facing; + } break; + } - Transform3D facing = Transform3D().looking_at(direction, current_up); - current_xform = base_xform.translated(current_point) * facing; - } break; - } + double u0 = (x0 - faces_combined) * u_step; + double u1 = ((x0 + 1) * u_step); + if (mode == MODE_PATH && !path_continuous_u) { + u0 = 0.0; + u1 = 1.0; + } - double u0 = x0 * u_step; - double u1 = ((x0 + 1) * u_step); - if (mode == MODE_PATH && !path_continuous_u) { - u0 = 0.0; - u1 = 1.0; - } + for (int y0 = 0; y0 < shape_sides; y0++) { + int y1 = (y0 + 1) % shape_sides; + // Use the top half of the texture. + double v0 = (y0 * v_step) / 2; + double v1 = ((y0 + 1) * v_step) / 2; - for (int y0 = 0; y0 < shape_sides; y0++) { - int y1 = (y0 + 1) % shape_sides; - // Use the top half of the texture. - double v0 = (y0 * v_step) / 2; - double v1 = ((y0 + 1) * v_step) / 2; - - Vector3 v[4] = { - previous_xform.xform(Vector3(shape_polygon[y0].x, shape_polygon[y0].y, 0)), - current_xform.xform(Vector3(shape_polygon[y0].x, shape_polygon[y0].y, 0)), - current_xform.xform(Vector3(shape_polygon[y1].x, shape_polygon[y1].y, 0)), - previous_xform.xform(Vector3(shape_polygon[y1].x, shape_polygon[y1].y, 0)), - }; - - Vector2 u[4] = { - Vector2(u0, v0), - Vector2(u1, v0), - Vector2(u1, v1), - Vector2(u0, v1), - }; - - // Face 1 - facesw[face * 3 + 0] = v[0]; - facesw[face * 3 + 1] = v[1]; - facesw[face * 3 + 2] = v[2]; - - uvsw[face * 3 + 0] = u[0]; - uvsw[face * 3 + 1] = u[1]; - uvsw[face * 3 + 2] = u[2]; - - smoothw[face] = smooth_faces; - invertw[face] = invert_faces; - materialsw[face] = material; - - face++; - - // Face 2 - facesw[face * 3 + 0] = v[2]; - facesw[face * 3 + 1] = v[3]; - facesw[face * 3 + 2] = v[0]; - - uvsw[face * 3 + 0] = u[2]; - uvsw[face * 3 + 1] = u[3]; - uvsw[face * 3 + 2] = u[0]; - - smoothw[face] = smooth_faces; - invertw[face] = invert_faces; - materialsw[face] = material; - - face++; - } - } + Vector3 v[4] = { + previous_xform.xform(Vector3(shape_polygon[y0].x, shape_polygon[y0].y, 0)), + current_xform.xform(Vector3(shape_polygon[y0].x, shape_polygon[y0].y, 0)), + current_xform.xform(Vector3(shape_polygon[y1].x, shape_polygon[y1].y, 0)), + previous_xform.xform(Vector3(shape_polygon[y1].x, shape_polygon[y1].y, 0)), + }; - if (end_count > 1) { - // Add back end face. - for (int face_idx = 0; face_idx < shape_face_count; face_idx++) { - for (int face_vertex_idx = 0; face_vertex_idx < 3; face_vertex_idx++) { - int index = shape_faces[face_idx * 3 + face_vertex_idx]; - Point2 p = shape_polygon[index]; - Point2 uv = (p - shape_rect.position) / shape_rect.size; + Vector2 u[4] = { + Vector2(u0, v0), + Vector2(u1, v0), + Vector2(u1, v1), + Vector2(u0, v1), + }; - // Use the x-inverted ride side of the bottom half of the y-inverted texture. - uv.x = 1 - uv.x / 2; - uv.y = 1 - (uv.y / 2); + // Face 1 + facesw[face * 3 + 0] = v[0]; + facesw[face * 3 + 1] = v[1]; + facesw[face * 3 + 2] = v[2]; - facesw[face * 3 + face_vertex_idx] = current_xform.xform(Vector3(p.x, p.y, 0)); - uvsw[face * 3 + face_vertex_idx] = uv; + uvsw[face * 3 + 0] = u[0]; + uvsw[face * 3 + 1] = u[1]; + uvsw[face * 3 + 2] = u[2]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_faces; + materialsw[face] = material; + + face++; + + // Face 2 + facesw[face * 3 + 0] = v[2]; + facesw[face * 3 + 1] = v[3]; + facesw[face * 3 + 2] = v[0]; + + uvsw[face * 3 + 0] = u[2]; + uvsw[face * 3 + 1] = u[3]; + uvsw[face * 3 + 2] = u[0]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_faces; + materialsw[face] = material; + + face++; } + } + + if (end_count > 1) { + // Add back end face. + for (int face_idx = 0; face_idx < shape_face_count; face_idx++) { + for (int face_vertex_idx = 0; face_vertex_idx < 3; face_vertex_idx++) { + int index = shape_faces[face_idx * 3 + face_vertex_idx]; + Point2 p = shape_polygon[index]; + Point2 uv = (p - shape_rect.position) / shape_rect.size; - smoothw[face] = false; - materialsw[face] = material; - invertw[face] = invert_faces; - face++; + // Use the x-inverted ride side of the bottom half of the y-inverted texture. + uv.x = 1 - uv.x / 2; + uv.y = 1 - (uv.y / 2); + + facesw[face * 3 + face_vertex_idx] = current_xform.xform(Vector3(p.x, p.y, 0)); + uvsw[face * 3 + face_vertex_idx] = uv; + } + + smoothw[face] = false; + materialsw[face] = material; + invertw[face] = invert_faces; + face++; + } } + + face_count -= faces_removed; + ERR_FAIL_COND_V_MSG(face != face_count, brush, "Bug: Failed to create the CSGPolygon mesh correctly."); } - ERR_FAIL_COND_V_MSG(face != face_count, brush, "Bug: Failed to create the CSGPolygon mesh correctly."); + if (faces_removed > 0) { + faces.resize(face_count * 3); + uvs.resize(face_count * 3); + smooth.resize(face_count); + materials.resize(face_count); + invert.resize(face_count); + } brush->build_from_faces(faces, uvs, smooth, materials, invert); @@ -2031,9 +2070,15 @@ void CSGPolygon3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_path_node", "path"), &CSGPolygon3D::set_path_node); ClassDB::bind_method(D_METHOD("get_path_node"), &CSGPolygon3D::get_path_node); + ClassDB::bind_method(D_METHOD("set_path_interval_type", "interval_type"), &CSGPolygon3D::set_path_interval_type); + ClassDB::bind_method(D_METHOD("get_path_interval_type"), &CSGPolygon3D::get_path_interval_type); + ClassDB::bind_method(D_METHOD("set_path_interval", "interval"), &CSGPolygon3D::set_path_interval); ClassDB::bind_method(D_METHOD("get_path_interval"), &CSGPolygon3D::get_path_interval); + ClassDB::bind_method(D_METHOD("set_path_simplify_angle", "degrees"), &CSGPolygon3D::set_path_simplify_angle); + ClassDB::bind_method(D_METHOD("get_path_simplify_angle"), &CSGPolygon3D::get_path_simplify_angle); + ClassDB::bind_method(D_METHOD("set_path_rotation", "path_rotation"), &CSGPolygon3D::set_path_rotation); ClassDB::bind_method(D_METHOD("get_path_rotation"), &CSGPolygon3D::get_path_rotation); @@ -2043,6 +2088,9 @@ void CSGPolygon3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_path_continuous_u", "enable"), &CSGPolygon3D::set_path_continuous_u); ClassDB::bind_method(D_METHOD("is_path_continuous_u"), &CSGPolygon3D::is_path_continuous_u); + ClassDB::bind_method(D_METHOD("set_path_u_distance", "distance"), &CSGPolygon3D::set_path_u_distance); + ClassDB::bind_method(D_METHOD("get_path_u_distance"), &CSGPolygon3D::get_path_u_distance); + ClassDB::bind_method(D_METHOD("set_path_joined", "enable"), &CSGPolygon3D::set_path_joined); ClassDB::bind_method(D_METHOD("is_path_joined"), &CSGPolygon3D::is_path_joined); @@ -2061,10 +2109,13 @@ void CSGPolygon3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "spin_degrees", PROPERTY_HINT_RANGE, "1,360,0.1"), "set_spin_degrees", "get_spin_degrees"); ADD_PROPERTY(PropertyInfo(Variant::INT, "spin_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_spin_sides", "get_spin_sides"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Path3D"), "set_path_node", "get_path_node"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_interval", PROPERTY_HINT_RANGE, "0.1,1.0,0.05,exp"), "set_path_interval", "get_path_interval"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "path_interval_type", PROPERTY_HINT_ENUM, "Distance,Subdivide"), "set_path_interval_type", "get_path_interval_type"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_interval", PROPERTY_HINT_RANGE, "0.01,1.0,0.01,exp,or_greater"), "set_path_interval", "get_path_interval"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_simplify_angle", PROPERTY_HINT_RANGE, "0.0,180.0,0.1,exp"), "set_path_simplify_angle", "get_path_simplify_angle"); ADD_PROPERTY(PropertyInfo(Variant::INT, "path_rotation", PROPERTY_HINT_ENUM, "Polygon,Path,PathFollow"), "set_path_rotation", "get_path_rotation"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_local"), "set_path_local", "is_path_local"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_continuous_u"), "set_path_continuous_u", "is_path_continuous_u"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_u_distance", PROPERTY_HINT_RANGE, "0.0,10.0,0.01,or_greater"), "set_path_u_distance", "get_path_u_distance"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_joined"), "set_path_joined", "is_path_joined"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material"); @@ -2076,6 +2127,9 @@ void CSGPolygon3D::_bind_methods() { BIND_ENUM_CONSTANT(PATH_ROTATION_POLYGON); BIND_ENUM_CONSTANT(PATH_ROTATION_PATH); BIND_ENUM_CONSTANT(PATH_ROTATION_PATH_FOLLOW); + + BIND_ENUM_CONSTANT(PATH_INTERVAL_DISTANCE); + BIND_ENUM_CONSTANT(PATH_INTERVAL_SUBDIVIDE); } void CSGPolygon3D::set_polygon(const Vector<Vector2> &p_polygon) { @@ -2119,6 +2173,16 @@ bool CSGPolygon3D::is_path_continuous_u() const { return path_continuous_u; } +void CSGPolygon3D::set_path_u_distance(real_t p_path_u_distance) { + path_u_distance = p_path_u_distance; + _make_dirty(); + update_gizmos(); +} + +real_t CSGPolygon3D::get_path_u_distance() const { + return path_u_distance; +} + void CSGPolygon3D::set_spin_degrees(const float p_spin_degrees) { ERR_FAIL_COND(p_spin_degrees < 0.01 || p_spin_degrees > 360); spin_degrees = p_spin_degrees; @@ -2151,8 +2215,17 @@ NodePath CSGPolygon3D::get_path_node() const { return path_node; } +void CSGPolygon3D::set_path_interval_type(PathIntervalType p_interval_type) { + path_interval_type = p_interval_type; + _make_dirty(); + update_gizmos(); +} + +CSGPolygon3D::PathIntervalType CSGPolygon3D::get_path_interval_type() const { + return path_interval_type; +} + void CSGPolygon3D::set_path_interval(float p_interval) { - ERR_FAIL_COND_MSG(p_interval <= 0 || p_interval > 1, "Path interval must be greater than 0 and less than or equal to 1.0."); path_interval = p_interval; _make_dirty(); update_gizmos(); @@ -2162,6 +2235,16 @@ float CSGPolygon3D::get_path_interval() const { return path_interval; } +void CSGPolygon3D::set_path_simplify_angle(float p_angle) { + path_simplify_angle = p_angle; + _make_dirty(); + update_gizmos(); +} + +float CSGPolygon3D::get_path_simplify_angle() const { + return path_simplify_angle; +} + void CSGPolygon3D::set_path_rotation(PathRotation p_rotation) { path_rotation = p_rotation; _make_dirty(); @@ -2229,10 +2312,13 @@ CSGPolygon3D::CSGPolygon3D() { spin_degrees = 360; spin_sides = 8; smooth_faces = false; + path_interval_type = PATH_INTERVAL_DISTANCE; path_interval = 1.0; + path_simplify_angle = 0.0; path_rotation = PATH_ROTATION_PATH_FOLLOW; path_local = false; path_continuous_u = true; + path_u_distance = 1.0; path_joined = false; path = nullptr; } diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h index 5cf371665e..c85cce776b 100644 --- a/modules/csg/csg_shape.h +++ b/modules/csg/csg_shape.h @@ -336,6 +336,11 @@ public: MODE_PATH }; + enum PathIntervalType { + PATH_INTERVAL_DISTANCE, + PATH_INTERVAL_SUBDIVIDE + }; + enum PathRotation { PATH_ROTATION_POLYGON, PATH_ROTATION_PATH, @@ -356,7 +361,9 @@ private: int spin_sides; NodePath path_node; + PathIntervalType path_interval_type; float path_interval; + float path_simplify_angle; PathRotation path_rotation; bool path_local; @@ -364,6 +371,7 @@ private: bool smooth_faces; bool path_continuous_u; + real_t path_u_distance; bool path_joined; bool _is_editable_3d_polygon() const; @@ -396,9 +404,15 @@ public: void set_path_node(const NodePath &p_path); NodePath get_path_node() const; + void set_path_interval_type(PathIntervalType p_interval_type); + PathIntervalType get_path_interval_type() const; + void set_path_interval(float p_interval); float get_path_interval() const; + void set_path_simplify_angle(float p_angle); + float get_path_simplify_angle() const; + void set_path_rotation(PathRotation p_rotation); PathRotation get_path_rotation() const; @@ -408,6 +422,9 @@ public: void set_path_continuous_u(bool p_enable); bool is_path_continuous_u() const; + void set_path_u_distance(real_t p_path_u_distance); + real_t get_path_u_distance() const; + void set_path_joined(bool p_enable); bool is_path_joined() const; @@ -422,5 +439,6 @@ public: VARIANT_ENUM_CAST(CSGPolygon3D::Mode) VARIANT_ENUM_CAST(CSGPolygon3D::PathRotation) +VARIANT_ENUM_CAST(CSGPolygon3D::PathIntervalType) #endif // CSG_SHAPE_H diff --git a/modules/csg/doc_classes/CSGPolygon3D.xml b/modules/csg/doc_classes/CSGPolygon3D.xml index 5d56e56de9..ecbb7962d1 100644 --- a/modules/csg/doc_classes/CSGPolygon3D.xml +++ b/modules/csg/doc_classes/CSGPolygon3D.xml @@ -24,6 +24,9 @@ <member name="path_interval" type="float" setter="set_path_interval" getter="get_path_interval"> When [member mode] is [constant MODE_PATH], the path interval or ratio of path points to extrusions. </member> + <member name="path_interval_type" type="int" setter="set_path_interval_type" getter="get_path_interval_type" enum="CSGPolygon3D.PathIntervalType"> + When [member mode] is [constant MODE_PATH], this will determine if the interval should be by distance ([constant PATH_INTERVAL_DISTANCE]) or subdivision fractions ([constant PATH_INTERVAL_SUBDIVIDE]). + </member> <member name="path_joined" type="bool" setter="set_path_joined" getter="is_path_joined"> When [member mode] is [constant MODE_PATH], if [code]true[/code] the ends of the path are joined, by adding an extrusion between the last and first points of the path. </member> @@ -36,6 +39,12 @@ <member name="path_rotation" type="int" setter="set_path_rotation" getter="get_path_rotation" enum="CSGPolygon3D.PathRotation"> When [member mode] is [constant MODE_PATH], the [enum PathRotation] method used to rotate the [member polygon] as it is extruded. </member> + <member name="path_simplify_angle" type="float" setter="set_path_simplify_angle" getter="get_path_simplify_angle"> + When [member mode] is [constant MODE_PATH], extrusions that are less than this angle, will be merged together to reduce polygon count. + </member> + <member name="path_u_distance" type="float" setter="set_path_u_distance" getter="get_path_u_distance"> + When [member mode] is [constant MODE_PATH], this is the distance along the path, in meters, the texture coordinates will tile. When set to 0, texture coordinates will match geometry exactly with no tiling. + </member> <member name="polygon" type="PackedVector2Array" setter="set_polygon" getter="get_polygon" default="PackedVector2Array(0, 0, 0, 1, 1, 1, 1, 0)"> The point array that defines the 2D polygon that is extruded. </member> @@ -70,5 +79,11 @@ <constant name="PATH_ROTATION_PATH_FOLLOW" value="2" enum="PathRotation"> The [member polygon] shape follows the path and its rotations around the path axis. </constant> + <constant name="PATH_INTERVAL_DISTANCE" value="0" enum="PathIntervalType"> + When [member mode] is set to [constant MODE_PATH], [member path_interval] will determine the distance, in meters, each interval of the path will extrude. + </constant> + <constant name="PATH_INTERVAL_SUBDIVIDE" value="1" enum="PathIntervalType"> + When [member mode] is set to [constant MODE_PATH], [member path_interval] will subdivide the polygons along the path. + </constant> </constants> </class> diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml index 456b390dbb..f9659c092a 100644 --- a/modules/enet/doc_classes/ENetMultiplayerPeer.xml +++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml @@ -77,10 +77,8 @@ <member name="host" type="ENetConnection" setter="" getter="get_host"> The underlying [ENetConnection] created after [method create_client] and [method create_server]. </member> - <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" /> <member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true"> Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server. </member> - <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="TransferMode" default="2" /> </members> </class> diff --git a/modules/enet/doc_classes/ENetPacketPeer.xml b/modules/enet/doc_classes/ENetPacketPeer.xml index 8f0693fb01..4116ba17f2 100644 --- a/modules/enet/doc_classes/ENetPacketPeer.xml +++ b/modules/enet/doc_classes/ENetPacketPeer.xml @@ -6,6 +6,7 @@ <description> A PacketPeer implementation representing a peer of an [ENetConnection]. This class cannot be instantiated directly but can be retrieved during [method ENetConnection.service] or via [method ENetConnection.get_peers]. + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> <link title="API documentation on the ENet website">http://enet.bespin.org/usergroup0.html</link> diff --git a/modules/enet/enet_multiplayer_peer.cpp b/modules/enet/enet_multiplayer_peer.cpp index afd31207f6..52eb46f070 100644 --- a/modules/enet/enet_multiplayer_peer.cpp +++ b/modules/enet/enet_multiplayer_peer.cpp @@ -33,22 +33,6 @@ #include "core/io/marshalls.h" #include "core/os/os.h" -void ENetMultiplayerPeer::set_transfer_channel(int p_channel) { - transfer_channel = p_channel; -} - -int ENetMultiplayerPeer::get_transfer_channel() const { - return transfer_channel; -} - -void ENetMultiplayerPeer::set_transfer_mode(Multiplayer::TransferMode p_mode) { - transfer_mode = p_mode; -} - -Multiplayer::TransferMode ENetMultiplayerPeer::get_transfer_mode() const { - return transfer_mode; -} - void ENetMultiplayerPeer::set_target_peer(int p_peer) { target_peer = p_peer; } @@ -62,6 +46,7 @@ int ENetMultiplayerPeer::get_packet_peer() const { Error ENetMultiplayerPeer::create_server(int p_port, int p_max_clients, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth) { ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "The multiplayer instance is already active."); + set_refuse_new_connections(false); Ref<ENetConnection> host; host.instantiate(); Error err = host->create_host_bound(bind_ip, p_port, p_max_clients, 0, p_max_channels > 0 ? p_max_channels + SYSCH_MAX : 0, p_out_bandwidth); @@ -70,7 +55,6 @@ Error ENetMultiplayerPeer::create_server(int p_port, int p_max_clients, int p_ma } active_mode = MODE_SERVER; - refuse_connections = false; unique_id = 1; connection_status = CONNECTION_CONNECTED; hosts[0] = host; @@ -79,6 +63,7 @@ Error ENetMultiplayerPeer::create_server(int p_port, int p_max_clients, int p_ma Error ENetMultiplayerPeer::create_client(const String &p_address, int p_port, int p_channel_count, int p_in_bandwidth, int p_out_bandwidth, int p_local_port) { ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "The multiplayer instance is already active."); + set_refuse_new_connections(false); Ref<ENetConnection> host; host.instantiate(); Error err; @@ -102,7 +87,6 @@ Error ENetMultiplayerPeer::create_client(const String &p_address, int p_port, in // Need to wait for CONNECT event. connection_status = CONNECTION_CONNECTING; active_mode = MODE_CLIENT; - refuse_connections = false; peers[1] = peer; hosts[0] = host; @@ -113,7 +97,6 @@ Error ENetMultiplayerPeer::create_mesh(int p_id) { ERR_FAIL_COND_V_MSG(p_id <= 0, ERR_INVALID_PARAMETER, "The unique ID must be greater then 0"); ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "The multiplayer instance is already active."); active_mode = MODE_MESH; - refuse_connections = false; unique_id = p_id; connection_status = CONNECTION_CONNECTED; return OK; @@ -145,7 +128,7 @@ bool ENetMultiplayerPeer::_poll_server() { } switch (ret) { case ENetConnection::EVENT_CONNECT: { - if (refuse_connections) { + if (is_refusing_new_connections()) { event.peer->reset(); return false; } @@ -173,7 +156,7 @@ bool ENetMultiplayerPeer::_poll_server() { emit_signal(SNAME("peer_disconnected"), id); peers.erase(id); - if (!server_relay) { + if (server_relay) { _notify_peers(id, false); } return false; @@ -423,6 +406,7 @@ void ENetMultiplayerPeer::close_connection(uint32_t wait_usec) { hosts.clear(); unique_id = 0; connection_status = CONNECTION_DISCONNECTED; + set_refuse_new_connections(false); } int ENetMultiplayerPeer::get_available_packet_count() const { @@ -451,10 +435,11 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size int packet_flags = 0; int channel = SYSCH_RELIABLE; + int transfer_channel = get_transfer_channel(); if (transfer_channel > 0) { channel = SYSCH_MAX + transfer_channel - 1; } else { - switch (transfer_mode) { + switch (get_transfer_mode()) { case Multiplayer::TRANSFER_MODE_UNRELIABLE: { packet_flags = ENET_PACKET_FLAG_UNSEQUENCED; channel = SYSCH_UNRELIABLE; @@ -545,19 +530,15 @@ int ENetMultiplayerPeer::get_unique_id() const { return unique_id; } -void ENetMultiplayerPeer::set_refuse_new_connections(bool p_enable) { - refuse_connections = p_enable; +void ENetMultiplayerPeer::set_refuse_new_connections(bool p_enabled) { #ifdef GODOT_ENET if (_is_active()) { for (KeyValue<int, Ref<ENetConnection>> &E : hosts) { - E.value->refuse_new_connections(p_enable); + E.value->refuse_new_connections(p_enabled); } } #endif -} - -bool ENetMultiplayerPeer::is_refusing_new_connections() const { - return refuse_connections; + MultiplayerPeer::set_refuse_new_connections(p_enabled); } void ENetMultiplayerPeer::set_server_relay_enabled(bool p_enabled) { diff --git a/modules/enet/enet_multiplayer_peer.h b/modules/enet/enet_multiplayer_peer.h index b5316b8292..7a60e2359c 100644 --- a/modules/enet/enet_multiplayer_peer.h +++ b/modules/enet/enet_multiplayer_peer.h @@ -65,10 +65,7 @@ private: uint32_t unique_id = 0; int target_peer = 0; - int transfer_channel = 0; - Multiplayer::TransferMode transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE; - bool refuse_connections = false; bool server_relay = true; ConnectionStatus connection_status = CONNECTION_DISCONNECTED; @@ -101,40 +98,31 @@ protected: static void _bind_methods(); public: - virtual void set_transfer_channel(int p_channel) override; - virtual int get_transfer_channel() const override; - - virtual void set_transfer_mode(Multiplayer::TransferMode p_mode) override; - virtual Multiplayer::TransferMode get_transfer_mode() const override; virtual void set_target_peer(int p_peer) override; - virtual int get_packet_peer() const override; - Error create_server(int p_port, int p_max_clients = 32, int p_max_channels = 0, int p_in_bandwidth = 0, int p_out_bandwidth = 0); - Error create_client(const String &p_address, int p_port, int p_channel_count = 0, int p_in_bandwidth = 0, int p_out_bandwidth = 0, int p_local_port = 0); - Error create_mesh(int p_id); - Error add_mesh_peer(int p_id, Ref<ENetConnection> p_host); - - void close_connection(uint32_t wait_usec = 100); - - void disconnect_peer(int p_peer, bool now = false); - virtual void poll() override; - virtual bool is_server() const override; + // Overriden so we can instrument the DTLSServer when needed. + virtual void set_refuse_new_connections(bool p_enabled) override; - virtual int get_available_packet_count() const override; - virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet - virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; + virtual ConnectionStatus get_connection_status() const override; + + virtual int get_unique_id() const override; virtual int get_max_packet_size() const override; + virtual int get_available_packet_count() const override; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; - virtual ConnectionStatus get_connection_status() const override; + Error create_server(int p_port, int p_max_clients = 32, int p_max_channels = 0, int p_in_bandwidth = 0, int p_out_bandwidth = 0); + Error create_client(const String &p_address, int p_port, int p_channel_count = 0, int p_in_bandwidth = 0, int p_out_bandwidth = 0, int p_local_port = 0); + Error create_mesh(int p_id); + Error add_mesh_peer(int p_id, Ref<ENetConnection> p_host); - virtual void set_refuse_new_connections(bool p_enable) override; - virtual bool is_refusing_new_connections() const override; + void close_connection(uint32_t wait_usec = 100); - virtual int get_unique_id() const override; + void disconnect_peer(int p_peer, bool now = false); void set_bind_ip(const IPAddress &p_ip); void set_server_relay_enabled(bool p_enabled); diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub index 21ee39f3ed..94bda04d12 100644 --- a/modules/gdnative/SCsub +++ b/modules/gdnative/SCsub @@ -16,7 +16,6 @@ env_gdnative.Prepend(CPPPATH=["#modules/gdnative/include/"]) Export("env_gdnative") -SConscript("net/SCsub") SConscript("pluginscript/SCsub") SConscript("videodecoder/SCsub") SConscript("text/SCsub") diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py index fa985501b5..026a84a70f 100644 --- a/modules/gdnative/config.py +++ b/modules/gdnative/config.py @@ -10,14 +10,9 @@ def get_doc_classes(): return [ "GDNative", "GDNativeLibrary", - "MultiplayerPeerGDNative", "NativeScript", - "PacketPeerGDNative", "PluginScript", - "StreamPeerGDNative", "VideoStreamGDNative", - "WebRTCPeerConnectionGDNative", - "WebRTCDataChannelGDNative", ] diff --git a/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml b/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml deleted file mode 100644 index 40f3121525..0000000000 --- a/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="MultiplayerPeerGDNative" inherits="MultiplayerPeer" version="4.0"> - <brief_description> - </brief_description> - <description> - </description> - <tutorials> - </tutorials> -</class> diff --git a/modules/gdnative/doc_classes/PacketPeerGDNative.xml b/modules/gdnative/doc_classes/PacketPeerGDNative.xml deleted file mode 100644 index 32863f8422..0000000000 --- a/modules/gdnative/doc_classes/PacketPeerGDNative.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="PacketPeerGDNative" inherits="PacketPeer" version="4.0"> - <brief_description> - </brief_description> - <description> - </description> - <tutorials> - </tutorials> -</class> diff --git a/modules/gdnative/doc_classes/StreamPeerGDNative.xml b/modules/gdnative/doc_classes/StreamPeerGDNative.xml deleted file mode 100644 index a505de2106..0000000000 --- a/modules/gdnative/doc_classes/StreamPeerGDNative.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="StreamPeerGDNative" inherits="StreamPeer" version="4.0"> - <brief_description> - </brief_description> - <description> - </description> - <tutorials> - </tutorials> -</class> diff --git a/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml b/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml deleted file mode 100644 index ddf354763c..0000000000 --- a/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="WebRTCDataChannelGDNative" inherits="WebRTCDataChannel" version="4.0"> - <brief_description> - </brief_description> - <description> - </description> - <tutorials> - </tutorials> -</class> diff --git a/modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml b/modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml deleted file mode 100644 index 821779a0ff..0000000000 --- a/modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> -<class name="WebRTCPeerConnectionGDNative" inherits="WebRTCPeerConnection" version="4.0"> - <brief_description> - </brief_description> - <description> - </description> - <tutorials> - </tutorials> -</class> diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index 66d2dc267d..627883886c 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -5105,97 +5105,6 @@ ] }, { - "name": "net", - "type": "NET", - "version": { - "major": 4, - "minor": 0 - }, - "next": null, - "api": [ - { - "name": "godot_net_bind_stream_peer", - "return_type": "void", - "arguments": [ - [ - "godot_object *", - "p_obj" - ], - [ - "const godot_net_stream_peer *", - "p_interface" - ] - ] - }, - { - "name": "godot_net_bind_packet_peer", - "return_type": "void", - "arguments": [ - [ - "godot_object *", - "p_obj" - ], - [ - "const godot_net_packet_peer *", - "p_interface" - ] - ] - }, - { - "name": "godot_net_bind_multiplayer_peer", - "return_type": "void", - "arguments": [ - [ - "godot_object *", - "p_obj" - ], - [ - "const godot_net_multiplayer_peer *", - "p_interface" - ] - ] - }, - { - "name": "godot_net_set_webrtc_library", - "return_type": "godot_error", - "arguments": [ - [ - "const godot_net_webrtc_library *", - "p_library" - ] - ] - }, - { - "name": "godot_net_bind_webrtc_peer_connection", - "return_type": "void", - "arguments": [ - [ - "godot_object *", - "p_obj" - ], - [ - "const godot_net_webrtc_peer_connection *", - "p_interface" - ] - ] - }, - { - "name": "godot_net_bind_webrtc_data_channel", - "return_type": "void", - "arguments": [ - [ - "godot_object *", - "p_obj" - ], - [ - "const godot_net_webrtc_data_channel *", - "p_interface" - ] - ] - } - ] - }, - { "name": "text", "type": "TEXT", "version": { diff --git a/modules/gdnative/gdnative_builders.py b/modules/gdnative/gdnative_builders.py index 181fd71b82..4986b173cf 100644 --- a/modules/gdnative/gdnative_builders.py +++ b/modules/gdnative/gdnative_builders.py @@ -20,7 +20,6 @@ def _build_gdnative_api_struct_header(api): "#include <gdnative/gdnative.h>", "#include <android/godot_android.h>", "#include <nativescript/godot_nativescript.h>", - "#include <net/godot_net.h>", "#include <pluginscript/godot_pluginscript.h>", "#include <videodecoder/godot_videodecoder.h>", "#include <text/godot_text.h>", diff --git a/modules/gdnative/include/net/godot_net.h b/modules/gdnative/include/net/godot_net.h deleted file mode 100644 index 3fb7b9e1cc..0000000000 --- a/modules/gdnative/include/net/godot_net.h +++ /dev/null @@ -1,122 +0,0 @@ -/*************************************************************************/ -/* godot_net.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef GODOT_NATIVENET_H -#define GODOT_NATIVENET_H - -#include <gdnative/gdnative.h> - -#ifdef __cplusplus -extern "C" { -#endif - -// For future versions of the API we should only add new functions at the end of the structure and use the -// version info to detect whether a call is available - -// Use these to populate version in your plugin -#define GODOT_NET_API_MAJOR 3 -#define GODOT_NET_API_MINOR 1 - -typedef struct { - godot_gdnative_api_version version; /* version of our API */ - godot_object *data; /* User reference */ - - /* This is StreamPeer */ - godot_error (*get_data)(void *user, uint8_t *p_buffer, int p_bytes); - godot_error (*get_partial_data)(void *user, uint8_t *p_buffer, int p_bytes, int *r_received); - godot_error (*put_data)(void *user, const uint8_t *p_data, int p_bytes); - godot_error (*put_partial_data)(void *user, const uint8_t *p_data, int p_bytes, int *r_sent); - - int (*get_available_bytes)(const void *user); - - void *next; /* For extension? */ -} godot_net_stream_peer; - -/* Binds a StreamPeerGDNative to the provided interface */ -void godot_net_bind_stream_peer(godot_object *p_obj, const godot_net_stream_peer *p_interface); - -typedef struct { - godot_gdnative_api_version version; /* version of our API */ - - godot_object *data; /* User reference */ - - /* This is PacketPeer */ - godot_error (*get_packet)(void *, const uint8_t **, int *); - godot_error (*put_packet)(void *, const uint8_t *, int); - godot_int (*get_available_packet_count)(const void *); - godot_int (*get_max_packet_size)(const void *); - - void *next; /* For extension? */ -} godot_net_packet_peer; - -/* Binds a PacketPeerGDNative to the provided interface */ -void GDAPI godot_net_bind_packet_peer(godot_object *p_obj, const godot_net_packet_peer *); - -typedef struct { - godot_gdnative_api_version version; /* version of our API */ - - godot_object *data; /* User reference */ - - /* This is PacketPeer */ - godot_error (*get_packet)(void *, const uint8_t **, int *); - godot_error (*put_packet)(void *, const uint8_t *, int); - godot_int (*get_available_packet_count)(const void *); - godot_int (*get_max_packet_size)(const void *); - - /* This is MultiplayerPeer */ - void (*set_transfer_channel)(void *, godot_int); - godot_int (*get_transfer_channel)(void *); - void (*set_transfer_mode)(void *, godot_int); - godot_int (*get_transfer_mode)(const void *); - // 0 = broadcast, 1 = server, <0 = all but abs(value) - void (*set_target_peer)(void *, godot_int); - godot_int (*get_packet_peer)(const void *); - godot_bool (*is_server)(const void *); - void (*poll)(void *); - // Must be > 0, 1 is for server - int32_t (*get_unique_id)(const void *); - void (*set_refuse_new_connections)(void *, godot_bool); - godot_bool (*is_refusing_new_connections)(const void *); - godot_int (*get_connection_status)(const void *); - - void *next; /* For extension? Or maybe not... */ -} godot_net_multiplayer_peer; - -/* Binds a MultiplayerPeerGDNative to the provided interface */ -void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_multiplayer_peer *); - -#ifdef __cplusplus -} -#endif - -// WebRTC Bindings -#include "net/godot_webrtc.h" - -#endif /* GODOT_NATIVENET_H */ diff --git a/modules/gdnative/include/net/godot_webrtc.h b/modules/gdnative/include/net/godot_webrtc.h deleted file mode 100644 index 52006e56ec..0000000000 --- a/modules/gdnative/include/net/godot_webrtc.h +++ /dev/null @@ -1,123 +0,0 @@ -/*************************************************************************/ -/* godot_webrtc.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef GODOT_NATIVEWEBRTC_H -#define GODOT_NATIVEWEBRTC_H - -#include <gdnative/gdnative.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#define GODOT_NET_WEBRTC_API_MAJOR 4 -#define GODOT_NET_WEBRTC_API_MINOR 0 - -/* Library Interface (used to set default GDNative WebRTC implementation */ -typedef struct { - godot_gdnative_api_version version; /* version of our API */ - - /* Called when the library is unset as default interface via godot_net_set_webrtc_library */ - void (*unregistered)(); - - /* Used by WebRTCPeerConnection create when GDNative is the default implementation. */ - /* Takes a pointer to WebRTCPeerConnectionGDNative, should bind and return OK, failure if binding was unsuccessful. */ - godot_error (*create_peer_connection)(godot_object *); - - void *next; /* For extension */ -} godot_net_webrtc_library; - -/* WebRTCPeerConnection interface */ -typedef struct { - godot_gdnative_api_version version; /* version of our API */ - - godot_object *data; /* User reference */ - - /* This is WebRTCPeerConnection */ - godot_int (*get_connection_state)(const void *); - - godot_error (*initialize)(void *, const godot_dictionary *); - godot_object *(*create_data_channel)(void *, const char *p_channel_name, const godot_dictionary *); - godot_error (*create_offer)(void *); - godot_error (*create_answer)(void *); /* unused for now, should be done automatically on set_local_description */ - godot_error (*set_remote_description)(void *, const char *, const char *); - godot_error (*set_local_description)(void *, const char *, const char *); - godot_error (*add_ice_candidate)(void *, const char *, int, const char *); - godot_error (*poll)(void *); - void (*close)(void *); - - void *next; /* For extension? */ -} godot_net_webrtc_peer_connection; - -/* WebRTCDataChannel interface */ -typedef struct { - godot_gdnative_api_version version; /* version of our API */ - - godot_object *data; /* User reference */ - - /* This is PacketPeer */ - godot_error (*get_packet)(void *, const uint8_t **, int *); - godot_error (*put_packet)(void *, const uint8_t *, int); - godot_int (*get_available_packet_count)(const void *); - godot_int (*get_max_packet_size)(const void *); - - /* This is WebRTCDataChannel */ - void (*set_write_mode)(void *, godot_int); - godot_int (*get_write_mode)(const void *); - bool (*was_string_packet)(const void *); - - godot_int (*get_ready_state)(const void *); - const char *(*get_label)(const void *); - bool (*is_ordered)(const void *); - int (*get_id)(const void *); - int (*get_max_packet_life_time)(const void *); - int (*get_max_retransmits)(const void *); - const char *(*get_protocol)(const void *); - bool (*is_negotiated)(const void *); - int (*get_buffered_amount)(const void *); - - godot_error (*poll)(void *); - void (*close)(void *); - - void *next; /* For extension? */ -} godot_net_webrtc_data_channel; - -/* Set the default GDNative library */ -godot_error GDAPI godot_net_set_webrtc_library(const godot_net_webrtc_library *); -/* Binds a WebRTCPeerConnectionGDNative to the provided interface */ -void GDAPI godot_net_bind_webrtc_peer_connection(godot_object *p_obj, const godot_net_webrtc_peer_connection *); -/* Binds a WebRTCDataChannelGDNative to the provided interface */ -void GDAPI godot_net_bind_webrtc_data_channel(godot_object *p_obj, const godot_net_webrtc_data_channel *); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/modules/gdnative/net/SCsub b/modules/gdnative/net/SCsub deleted file mode 100644 index b76500c003..0000000000 --- a/modules/gdnative/net/SCsub +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python - -Import("env") -Import("env_gdnative") - -env_net = env_gdnative.Clone() - -has_webrtc = env_net["module_webrtc_enabled"] -if has_webrtc: - env_net.Append(CPPDEFINES=["WEBRTC_GDNATIVE_ENABLED"]) - -env_net.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.cpp b/modules/gdnative/net/multiplayer_peer_gdnative.cpp deleted file mode 100644 index 575d5f5060..0000000000 --- a/modules/gdnative/net/multiplayer_peer_gdnative.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/*************************************************************************/ -/* multiplayer_peer_gdnative.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "multiplayer_peer_gdnative.h" - -MultiplayerPeerGDNative::MultiplayerPeerGDNative() { - interface = nullptr; -} - -MultiplayerPeerGDNative::~MultiplayerPeerGDNative() { -} - -void MultiplayerPeerGDNative::set_native_multiplayer_peer(const godot_net_multiplayer_peer *p_interface) { - interface = p_interface; -} - -Error MultiplayerPeerGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)interface->get_packet(interface->data, r_buffer, &r_buffer_size); -} - -Error MultiplayerPeerGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size); -} - -int MultiplayerPeerGDNative::get_max_packet_size() const { - ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->get_max_packet_size(interface->data); -} - -int MultiplayerPeerGDNative::get_available_packet_count() const { - ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->get_available_packet_count(interface->data); -} - -/* MultiplayerPeer */ -void MultiplayerPeerGDNative::set_transfer_channel(int p_channel) { - ERR_FAIL_COND(interface == nullptr); - return interface->set_transfer_channel(interface->data, p_channel); -} - -int MultiplayerPeerGDNative::get_transfer_channel() const { - ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->get_transfer_channel(interface->data); -} - -void MultiplayerPeerGDNative::set_transfer_mode(Multiplayer::TransferMode p_mode) { - ERR_FAIL_COND(interface == nullptr); - interface->set_transfer_mode(interface->data, (godot_int)p_mode); -} - -Multiplayer::TransferMode MultiplayerPeerGDNative::get_transfer_mode() const { - ERR_FAIL_COND_V(interface == nullptr, Multiplayer::TRANSFER_MODE_UNRELIABLE); - return (Multiplayer::TransferMode)interface->get_transfer_mode(interface->data); -} - -void MultiplayerPeerGDNative::set_target_peer(int p_peer_id) { - ERR_FAIL_COND(interface == nullptr); - interface->set_target_peer(interface->data, p_peer_id); -} - -int MultiplayerPeerGDNative::get_packet_peer() const { - ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->get_packet_peer(interface->data); -} - -bool MultiplayerPeerGDNative::is_server() const { - ERR_FAIL_COND_V(interface == nullptr, false); - return interface->is_server(interface->data); -} - -void MultiplayerPeerGDNative::poll() { - ERR_FAIL_COND(interface == nullptr); - interface->poll(interface->data); -} - -int MultiplayerPeerGDNative::get_unique_id() const { - ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->get_unique_id(interface->data); -} - -void MultiplayerPeerGDNative::set_refuse_new_connections(bool p_enable) { - ERR_FAIL_COND(interface == nullptr); - interface->set_refuse_new_connections(interface->data, p_enable); -} - -bool MultiplayerPeerGDNative::is_refusing_new_connections() const { - ERR_FAIL_COND_V(interface == nullptr, true); - return interface->is_refusing_new_connections(interface->data); -} - -MultiplayerPeer::ConnectionStatus MultiplayerPeerGDNative::get_connection_status() const { - ERR_FAIL_COND_V(interface == nullptr, CONNECTION_DISCONNECTED); - return (ConnectionStatus)interface->get_connection_status(interface->data); -} - -void MultiplayerPeerGDNative::_bind_methods() { - ADD_PROPERTY_DEFAULT("transfer_channel", 0); - ADD_PROPERTY_DEFAULT("transfer_mode", Multiplayer::TRANSFER_MODE_UNRELIABLE); - ADD_PROPERTY_DEFAULT("refuse_new_connections", true); -} - -extern "C" { - -void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_multiplayer_peer *p_impl) { - ((MultiplayerPeerGDNative *)p_obj)->set_native_multiplayer_peer(p_impl); -} -} diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.h b/modules/gdnative/net/multiplayer_peer_gdnative.h deleted file mode 100644 index 33e424d284..0000000000 --- a/modules/gdnative/net/multiplayer_peer_gdnative.h +++ /dev/null @@ -1,79 +0,0 @@ -/*************************************************************************/ -/* multiplayer_peer_gdnative.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef MULTIPLAYER_PEER_GDNATIVE_H -#define MULTIPLAYER_PEER_GDNATIVE_H - -#include "core/multiplayer/multiplayer_peer.h" -#include "modules/gdnative/gdnative.h" -#include "modules/gdnative/include/net/godot_net.h" - -class MultiplayerPeerGDNative : public MultiplayerPeer { - GDCLASS(MultiplayerPeerGDNative, MultiplayerPeer); - -protected: - static void _bind_methods(); - const godot_net_multiplayer_peer *interface; - -public: - MultiplayerPeerGDNative(); - ~MultiplayerPeerGDNative(); - - /* Sets the interface implementation from GDNative */ - void set_native_multiplayer_peer(const godot_net_multiplayer_peer *p_impl); - - /* Specific to PacketPeer */ - virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; - virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; - virtual int get_max_packet_size() const override; - virtual int get_available_packet_count() const override; - - /* Specific to MultiplayerPeer */ - virtual void set_transfer_channel(int p_channel) override; - virtual int get_transfer_channel() const override; - virtual void set_transfer_mode(Multiplayer::TransferMode p_mode) override; - virtual Multiplayer::TransferMode get_transfer_mode() const override; - virtual void set_target_peer(int p_peer_id) override; - - virtual int get_packet_peer() const override; - - virtual bool is_server() const override; - - virtual void poll() override; - - virtual int get_unique_id() const override; - - virtual void set_refuse_new_connections(bool p_enable) override; - virtual bool is_refusing_new_connections() const override; - - virtual ConnectionStatus get_connection_status() const override; -}; - -#endif // MULTIPLAYER_PEER_GDNATIVE_H diff --git a/modules/gdnative/net/packet_peer_gdnative.cpp b/modules/gdnative/net/packet_peer_gdnative.cpp deleted file mode 100644 index 3bcdfed8ff..0000000000 --- a/modules/gdnative/net/packet_peer_gdnative.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/*************************************************************************/ -/* packet_peer_gdnative.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "packet_peer_gdnative.h" - -PacketPeerGDNative::PacketPeerGDNative() { - interface = nullptr; -} - -PacketPeerGDNative::~PacketPeerGDNative() { -} - -void PacketPeerGDNative::set_native_packet_peer(const godot_net_packet_peer *p_impl) { - interface = p_impl; -} - -void PacketPeerGDNative::_bind_methods() { -} - -Error PacketPeerGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)interface->get_packet(interface->data, r_buffer, &r_buffer_size); -} - -Error PacketPeerGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size); -} - -int PacketPeerGDNative::get_max_packet_size() const { - ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->get_max_packet_size(interface->data); -} - -int PacketPeerGDNative::get_available_packet_count() const { - ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->get_available_packet_count(interface->data); -} - -extern "C" { - -void GDAPI godot_net_bind_packet_peer(godot_object *p_obj, const godot_net_packet_peer *p_impl) { - ((PacketPeerGDNative *)p_obj)->set_native_packet_peer(p_impl); -} -} diff --git a/modules/gdnative/net/packet_peer_gdnative.h b/modules/gdnative/net/packet_peer_gdnative.h deleted file mode 100644 index 29013f9367..0000000000 --- a/modules/gdnative/net/packet_peer_gdnative.h +++ /dev/null @@ -1,59 +0,0 @@ -/*************************************************************************/ -/* packet_peer_gdnative.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef PACKET_PEER_GDNATIVE_H -#define PACKET_PEER_GDNATIVE_H - -#include "core/io/packet_peer.h" -#include "modules/gdnative/gdnative.h" -#include "modules/gdnative/include/net/godot_net.h" - -class PacketPeerGDNative : public PacketPeer { - GDCLASS(PacketPeerGDNative, PacketPeer); - -protected: - static void _bind_methods(); - const godot_net_packet_peer *interface; - -public: - PacketPeerGDNative(); - ~PacketPeerGDNative(); - - /* Sets the interface implementation from GDNative */ - void set_native_packet_peer(const godot_net_packet_peer *p_impl); - - /* Specific to PacketPeer */ - virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; - virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; - virtual int get_max_packet_size() const override; - virtual int get_available_packet_count() const override; -}; - -#endif // PACKET_PEER_GDNATIVE_H diff --git a/modules/gdnative/net/register_types.cpp b/modules/gdnative/net/register_types.cpp deleted file mode 100644 index 46c383e5ae..0000000000 --- a/modules/gdnative/net/register_types.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/*************************************************************************/ -/* register_types.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "register_types.h" -#include "multiplayer_peer_gdnative.h" -#include "packet_peer_gdnative.h" -#include "stream_peer_gdnative.h" - -void register_net_types() { - GDREGISTER_CLASS(MultiplayerPeerGDNative); - GDREGISTER_CLASS(PacketPeerGDNative); - GDREGISTER_CLASS(StreamPeerGDNative); -} - -void unregister_net_types() { -} diff --git a/modules/gdnative/net/register_types.h b/modules/gdnative/net/register_types.h deleted file mode 100644 index c99c6f6fbf..0000000000 --- a/modules/gdnative/net/register_types.h +++ /dev/null @@ -1,37 +0,0 @@ -/*************************************************************************/ -/* register_types.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef NET_REGISTER_TYPES_H -#define NET_REGISTER_TYPES_H - -void register_net_types(); -void unregister_net_types(); - -#endif // NET_REGISTER_TYPES_H diff --git a/modules/gdnative/net/stream_peer_gdnative.cpp b/modules/gdnative/net/stream_peer_gdnative.cpp deleted file mode 100644 index 72ab72323d..0000000000 --- a/modules/gdnative/net/stream_peer_gdnative.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/*************************************************************************/ -/* stream_peer_gdnative.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "stream_peer_gdnative.h" - -StreamPeerGDNative::StreamPeerGDNative() { - interface = nullptr; -} - -StreamPeerGDNative::~StreamPeerGDNative() { -} - -void StreamPeerGDNative::set_native_stream_peer(const godot_net_stream_peer *p_interface) { - interface = p_interface; -} - -void StreamPeerGDNative::_bind_methods() { -} - -Error StreamPeerGDNative::put_data(const uint8_t *p_data, int p_bytes) { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)(interface->put_data(interface->data, p_data, p_bytes)); -} - -Error StreamPeerGDNative::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)(interface->put_partial_data(interface->data, p_data, p_bytes, &r_sent)); -} - -Error StreamPeerGDNative::get_data(uint8_t *p_buffer, int p_bytes) { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)(interface->get_data(interface->data, p_buffer, p_bytes)); -} - -Error StreamPeerGDNative::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)(interface->get_partial_data(interface->data, p_buffer, p_bytes, &r_received)); -} - -int StreamPeerGDNative::get_available_bytes() const { - ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->get_available_bytes(interface->data); -} - -extern "C" { - -void GDAPI godot_net_bind_stream_peer(godot_object *p_obj, const godot_net_stream_peer *p_interface) { - ((StreamPeerGDNative *)p_obj)->set_native_stream_peer(p_interface); -} -} diff --git a/modules/gdnative/net/stream_peer_gdnative.h b/modules/gdnative/net/stream_peer_gdnative.h deleted file mode 100644 index dd5abceb83..0000000000 --- a/modules/gdnative/net/stream_peer_gdnative.h +++ /dev/null @@ -1,60 +0,0 @@ -/*************************************************************************/ -/* stream_peer_gdnative.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef STREAM_PEER_GDNATIVE_H -#define STREAM_PEER_GDNATIVE_H - -#include "core/io/stream_peer.h" -#include "modules/gdnative/gdnative.h" -#include "modules/gdnative/include/net/godot_net.h" - -class StreamPeerGDNative : public StreamPeer { - GDCLASS(StreamPeerGDNative, StreamPeer); - -protected: - static void _bind_methods(); - const godot_net_stream_peer *interface; - -public: - StreamPeerGDNative(); - ~StreamPeerGDNative(); - - /* Sets the interface implementation from GDNative */ - void set_native_stream_peer(const godot_net_stream_peer *p_interface); - - /* Specific to StreamPeer */ - Error put_data(const uint8_t *p_data, int p_bytes) override; - Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override; - Error get_data(uint8_t *p_buffer, int p_bytes) override; - Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override; - int get_available_bytes() const override; -}; - -#endif // STREAM_PEER_GDNATIVE_H diff --git a/modules/gdnative/net/webrtc_gdnative.cpp b/modules/gdnative/net/webrtc_gdnative.cpp deleted file mode 100644 index 76ccbad009..0000000000 --- a/modules/gdnative/net/webrtc_gdnative.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/*************************************************************************/ -/* webrtc_gdnative.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "modules/gdnative/gdnative.h" -#include "modules/gdnative/include/net/godot_net.h" - -#ifdef WEBRTC_GDNATIVE_ENABLED -#include "modules/webrtc/webrtc_data_channel_gdnative.h" -#include "modules/webrtc/webrtc_peer_connection_gdnative.h" -#endif - -extern "C" { - -void GDAPI godot_net_bind_webrtc_peer_connection(godot_object *p_obj, const godot_net_webrtc_peer_connection *p_impl) { -#ifdef WEBRTC_GDNATIVE_ENABLED - ((WebRTCPeerConnectionGDNative *)p_obj)->set_native_webrtc_peer_connection(p_impl); -#endif -} - -void GDAPI godot_net_bind_webrtc_data_channel(godot_object *p_obj, const godot_net_webrtc_data_channel *p_impl) { -#ifdef WEBRTC_GDNATIVE_ENABLED - ((WebRTCDataChannelGDNative *)p_obj)->set_native_webrtc_data_channel(p_impl); -#endif -} - -godot_error GDAPI godot_net_set_webrtc_library(const godot_net_webrtc_library *p_lib) { -#ifdef WEBRTC_GDNATIVE_ENABLED - return (godot_error)WebRTCPeerConnectionGDNative::set_default_library(p_lib); -#else - return (godot_error)ERR_UNAVAILABLE; -#endif -} -} diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index e4c2b20224..a4ab5663ef 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -35,7 +35,6 @@ #include "gdnative.h" #include "nativescript/register_types.h" -#include "net/register_types.h" #include "pluginscript/register_types.h" #include "videodecoder/register_types.h" @@ -265,7 +264,6 @@ void register_gdnative_types() { GDNativeCallRegistry::singleton->register_native_call_type("standard_varcall", cb_standard_varcall); - register_net_types(); register_nativescript_types(); register_pluginscript_types(); register_videodecoder_types(); @@ -329,7 +327,6 @@ void unregister_gdnative_types() { unregister_videodecoder_types(); unregister_pluginscript_types(); unregister_nativescript_types(); - unregister_net_types(); memdelete(GDNativeCallRegistry::singleton); diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index bf21c8510a..6186d0edee 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -88,9 +88,9 @@ static String _get_var_type(const Variant *p_var) { Object *bobj = p_var->get_validated_object_with_check(was_freed); if (!bobj) { if (was_freed) { - basestr = "null instance"; - } else { basestr = "previously freed"; + } else { + basestr = "null instance"; } } else { basestr = bobj->get_class(); @@ -1233,7 +1233,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX); #ifdef DEBUG_ENABLED - if (src->get_type() == Variant::OBJECT && !src->operator ObjectID().is_ref_counted() && ObjectDB::get_instance(src->operator ObjectID()) == nullptr) { + if (src->operator Object *() && !src->get_validated_object()) { err_text = "Trying to cast a freed object."; OPCODE_BREAK; } @@ -1263,7 +1263,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(!nc); #ifdef DEBUG_ENABLED - if (src->get_type() == Variant::OBJECT && !src->operator ObjectID().is_ref_counted() && ObjectDB::get_instance(src->operator ObjectID()) == nullptr) { + if (src->operator Object *() && !src->get_validated_object()) { err_text = "Trying to cast a freed object."; OPCODE_BREAK; } @@ -1295,7 +1295,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(!base_type); #ifdef DEBUG_ENABLED - if (src->get_type() == Variant::OBJECT && !src->operator ObjectID().is_ref_counted() && ObjectDB::get_instance(src->operator ObjectID()) == nullptr) { + if (src->operator Object *() && !src->get_validated_object()) { err_text = "Trying to cast a freed object."; OPCODE_BREAK; } @@ -2138,7 +2138,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a retvalue = gdfs; - Error err = sig.connect(Callable(gdfs.ptr(), "_signal_callback"), varray(gdfs), Object::CONNECT_ONESHOT); + Error err = sig.connect(callable_bind(Callable(gdfs.ptr(), "_signal_callback"), retvalue), Object::CONNECT_ONESHOT); if (err != OK) { err_text = "Error connecting to signal: " + sig.get_name() + " during await."; OPCODE_BREAK; diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp index b6c48468f5..bd5a9f01b2 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.cpp +++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp @@ -284,6 +284,23 @@ void GDScriptLanguageProtocol::notify_client(const String &p_method, const Varia peer->res_queue.push_back(msg.utf8()); } +void GDScriptLanguageProtocol::request_client(const String &p_method, const Variant &p_params, int p_client_id) { + if (p_client_id == -1) { + ERR_FAIL_COND_MSG(latest_client_id == -1, + "GDScript LSP: Can't notify client as none was connected."); + p_client_id = latest_client_id; + } + ERR_FAIL_COND(!clients.has(p_client_id)); + Ref<LSPeer> peer = clients.get(p_client_id); + ERR_FAIL_COND(peer == nullptr); + + Dictionary message = make_request(p_method, p_params, next_server_id); + next_server_id++; + String msg = Variant(message).to_json_string(); + msg = format_output(msg); + peer->res_queue.push_back(msg.utf8()); +} + bool GDScriptLanguageProtocol::is_smart_resolve_enabled() const { return bool(_EDITOR_GET("network/language_server/enable_smart_resolve")); } diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h index 5a2dd55c46..899446fb42 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.h +++ b/modules/gdscript/language_server/gdscript_language_protocol.h @@ -79,6 +79,8 @@ private: int latest_client_id = 0; int next_client_id = 0; + int next_server_id = 0; + Ref<GDScriptTextDocument> text_document; Ref<GDScriptWorkspace> workspace; @@ -107,6 +109,7 @@ public: void stop(); void notify_client(const String &p_method, const Variant &p_params = Variant(), int p_client_id = -1); + void request_client(const String &p_method, const Variant &p_params = Variant(), int p_client_id = -1); bool is_smart_resolve_enabled() const; bool is_goto_native_symbols_enabled() const; diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp index 1512b4bb89..86b3a3a326 100644 --- a/modules/gdscript/language_server/gdscript_workspace.cpp +++ b/modules/gdscript/language_server/gdscript_workspace.cpp @@ -42,6 +42,7 @@ #include "scene/resources/packed_scene.h" void GDScriptWorkspace::_bind_methods() { + ClassDB::bind_method(D_METHOD("apply_new_signal"), &GDScriptWorkspace::apply_new_signal); ClassDB::bind_method(D_METHOD("didDeleteFiles"), &GDScriptWorkspace::did_delete_files); ClassDB::bind_method(D_METHOD("symbol"), &GDScriptWorkspace::symbol); ClassDB::bind_method(D_METHOD("parse_script", "path", "content"), &GDScriptWorkspace::parse_script); @@ -52,6 +53,54 @@ void GDScriptWorkspace::_bind_methods() { ClassDB::bind_method(D_METHOD("generate_script_api", "path"), &GDScriptWorkspace::generate_script_api); } +void GDScriptWorkspace::apply_new_signal(Object *obj, String function, PackedStringArray args) { + String function_signature = "func " + function; + Ref<Script> script = obj->get_script(); + + String source = script->get_source_code(); + + if (source.find(function_signature) != -1) { + return; + } + + int first_class = source.find("\nclass "); + int start_line = 0; + if (first_class != -1) { + start_line = source.substr(0, first_class).split("\n").size(); + } else { + start_line = source.split("\n").size(); + } + + String function_body = "\n\n" + function_signature + "("; + for (int i = 0; i < args.size(); ++i) { + function_body += args[i]; + if (i < args.size() - 1) { + function_body += ", "; + } + } + function_body += ")"; + if (EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints")) { + function_body += " -> void"; + } + function_body += ":\n\tpass # Replace with function body.\n"; + + lsp::TextEdit text_edit; + + if (first_class != -1) { + function_body += "\n\n"; + } + text_edit.range.end.line = text_edit.range.start.line = start_line; + + text_edit.newText = function_body; + + String uri = get_file_uri(script->get_path()); + + lsp::ApplyWorkspaceEditParams params; + params.edit.add_edit(uri, text_edit); + + GDScriptLanguageProtocol::get_singleton()->request_client("workspace/applyEdit", params.to_json()); +} + void GDScriptWorkspace::did_delete_files(const Dictionary &p_params) { Array files = p_params["files"]; for (int i = 0; i < files.size(); ++i) { @@ -360,6 +409,9 @@ Error GDScriptWorkspace::initialize() { } } + EditorNode *editor_node = EditorNode::get_singleton(); + editor_node->connect("script_add_function_request", callable_mp(this, &GDScriptWorkspace::apply_new_signal)); + return OK; } diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h index 9496677449..e5cd4d9824 100644 --- a/modules/gdscript/language_server/gdscript_workspace.h +++ b/modules/gdscript/language_server/gdscript_workspace.h @@ -62,6 +62,8 @@ protected: void list_script_files(const String &p_root_dir, List<String> &r_files); + void apply_new_signal(Object *obj, String function, PackedStringArray args); + public: String root; String root_uri; diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index 9ac6c6bd4e..662382d279 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -263,6 +263,16 @@ struct WorkspaceEdit { */ Map<String, Vector<TextEdit>> changes; + _FORCE_INLINE_ void add_edit(const String &uri, const TextEdit &edit) { + if (changes.has(uri)) { + changes[uri].push_back(edit); + } else { + Vector<TextEdit> edits; + edits.push_back(edit); + changes[uri] = edits; + } + } + _FORCE_INLINE_ Dictionary to_json() const { Dictionary dict; @@ -1322,6 +1332,18 @@ struct DocumentSymbol { } }; +struct ApplyWorkspaceEditParams { + WorkspaceEdit edit; + + Dictionary to_json() { + Dictionary dict; + + dict["edit"] = edit.to_json(); + + return dict; + } +}; + struct NativeSymbolInspectParams { String native_class; String symbol_name; diff --git a/modules/gltf/doc_classes/GLTFLight.xml b/modules/gltf/doc_classes/GLTFLight.xml index 91df7d8014..b4f03cd1ed 100644 --- a/modules/gltf/doc_classes/GLTFLight.xml +++ b/modules/gltf/doc_classes/GLTFLight.xml @@ -7,17 +7,25 @@ <tutorials> </tutorials> <members> - <member name="color" type="Color" setter="set_color" getter="get_color" default="Color(0, 0, 0, 1)"> + <member name="color" type="Color" setter="set_color" getter="get_color" default="Color(1, 1, 1, 1)"> + The [Color] of the light. Defaults to white. A black color causes the light to have no effect. </member> <member name="inner_cone_angle" type="float" setter="set_inner_cone_angle" getter="get_inner_cone_angle" default="0.0"> + The inner angle of the cone in a spotlight. Must be less than or equal to the outer cone angle. + Within this angle, the light is at full brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. When creating a Godot [SpotLight3D], the ratio between the inner and outer cone angles is used to calculate the attenuation of the light. </member> - <member name="intensity" type="float" setter="set_intensity" getter="get_intensity" default="0.0"> + <member name="intensity" type="float" setter="set_intensity" getter="get_intensity" default="1.0"> + The intensity of the light. This is expressed in candelas (lumens per steradian) for point and spot lights, and lux (lumens per m²) for directional lights. When creating a Godot light, this value is converted to a unitless multiplier. </member> <member name="light_type" type="String" setter="set_light_type" getter="get_light_type" default=""""> + The type of the light. The values accepted by Godot are "point", "spot", and "directional", which correspond to Godot's [OmniLight3D], [SpotLight3D], and [DirectionalLight3D] respectively. </member> - <member name="outer_cone_angle" type="float" setter="set_outer_cone_angle" getter="get_outer_cone_angle" default="0.0"> + <member name="outer_cone_angle" type="float" setter="set_outer_cone_angle" getter="get_outer_cone_angle" default="0.785398"> + The outer angle of the cone in a spotlight. Must be greater than or equal to the inner angle. + At this angle, the light drops off to zero brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. If this angle is a half turn, then the spotlight emits in all directions. When creating a Godot [SpotLight3D], the outer cone angle is used as the angle of the spotlight. </member> - <member name="range" type="float" setter="set_range" getter="get_range" default="0.0"> + <member name="range" type="float" setter="set_range" getter="get_range" default="inf"> + The range of the light, beyond which the light has no effect. GLTF lights with no range defined behave like physical lights (which have infinite range). When creating a Godot light, the range is clamped to 4096. </member> </members> </class> diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index df2856ec7c..a92eb88edb 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -3757,10 +3757,7 @@ void GLTFDocument::spec_gloss_to_metal_base_color(const Color &p_specular_factor r_base_color.g = Math::lerp(base_color_from_diffuse.g, base_color_from_specular.g, r_metallic * r_metallic); r_base_color.b = Math::lerp(base_color_from_diffuse.b, base_color_from_specular.b, r_metallic * r_metallic); r_base_color.a = p_diffuse.a; - r_base_color.r = CLAMP(r_base_color.r, 0.0f, 1.0f); - r_base_color.g = CLAMP(r_base_color.g, 0.0f, 1.0f); - r_base_color.b = CLAMP(r_base_color.b, 0.0f, 1.0f); - r_base_color.a = CLAMP(r_base_color.a, 0.0f, 1.0f); + r_base_color = r_base_color.clamp(); } GLTFNodeIndex GLTFDocument::_find_highest_node(Ref<GLTFState> state, const Vector<GLTFNodeIndex> &subset) { @@ -5074,7 +5071,7 @@ Node3D *GLTFDocument::_generate_light(Ref<GLTFState> state, Node *scene_parent, const float range = CLAMP(l->range, 0, 4096); // Doubling the range will double the effective brightness, so we need double attenuation (half brightness). // We want to have double intensity give double brightness, so we need half the attenuation. - const float attenuation = range / intensity; + const float attenuation = range / (intensity * 2048); if (l->light_type == "point") { OmniLight3D *light = memnew(OmniLight3D); light->set_param(OmniLight3D::PARAM_ATTENUATION, attenuation); @@ -5150,13 +5147,13 @@ GLTFLightIndex GLTFDocument::_convert_light(Ref<GLTFState> state, Light3D *p_lig OmniLight3D *light = cast_to<OmniLight3D>(p_light); l->range = light->get_param(OmniLight3D::PARAM_RANGE); float attenuation = p_light->get_param(OmniLight3D::PARAM_ATTENUATION); - l->intensity = l->range / attenuation; + l->intensity = l->range / (attenuation * 2048); } else if (cast_to<SpotLight3D>(p_light)) { l->light_type = "spot"; SpotLight3D *light = cast_to<SpotLight3D>(p_light); l->range = light->get_param(SpotLight3D::PARAM_RANGE); float attenuation = light->get_param(SpotLight3D::PARAM_ATTENUATION); - l->intensity = l->range / attenuation; + l->intensity = l->range / (attenuation * 2048); l->outer_cone_angle = Math::deg2rad(light->get_param(SpotLight3D::PARAM_SPOT_ANGLE)); // This equation is the inverse of the import equation (which has a desmos link). diff --git a/modules/gltf/gltf_light.h b/modules/gltf/gltf_light.h index 079fb18151..62a20d2f16 100644 --- a/modules/gltf/gltf_light.h +++ b/modules/gltf/gltf_light.h @@ -42,12 +42,12 @@ protected: static void _bind_methods(); private: - Color color; - float intensity = 0.0f; + Color color = Color(1.0f, 1.0f, 1.0f); + float intensity = 1.0f; String light_type; - float range = 0.0f; + float range = INFINITY; float inner_cone_angle = 0.0f; - float outer_cone_angle = 0.0f; + float outer_cone_angle = Math_TAU / 8.0f; public: Color get_color(); diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index 487e6deac0..116c0e00f9 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -377,10 +377,7 @@ int GridMap::get_cell_item_orientation(const Vector3i &p_position) const { } Vector3i GridMap::world_to_map(const Vector3 &p_world_position) const { - Vector3 map_position = p_world_position / cell_size; - map_position.x = floor(map_position.x); - map_position.y = floor(map_position.y); - map_position.z = floor(map_position.z); + Vector3 map_position = (p_world_position / cell_size).floor(); return Vector3i(map_position); } diff --git a/modules/mobile_vr/register_types.cpp b/modules/mobile_vr/register_types.cpp index 47d1fe482c..233c16531a 100644 --- a/modules/mobile_vr/register_types.cpp +++ b/modules/mobile_vr/register_types.cpp @@ -32,15 +32,30 @@ #include "mobile_vr_interface.h" +Ref<MobileVRInterface> mobile_vr; + void register_mobile_vr_types() { GDREGISTER_CLASS(MobileVRInterface); if (XRServer::get_singleton()) { - Ref<MobileVRInterface> mobile_vr; mobile_vr.instantiate(); XRServer::get_singleton()->add_interface(mobile_vr); } } void unregister_mobile_vr_types() { + if (mobile_vr.is_valid()) { + // uninitialise our interface if it is initialised + if (mobile_vr->is_initialized()) { + mobile_vr->uninitialize(); + } + + // unregister our interface from the XR server + if (XRServer::get_singleton()) { + XRServer::get_singleton()->remove_interface(mobile_vr); + } + + // and release + mobile_vr.unref(); + } } diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 6cc7ddb424..af7c54dd5b 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -3511,10 +3511,10 @@ int CSharpScript::get_member_line(const StringName &p_member) const { } Multiplayer::RPCMode CSharpScript::_member_get_rpc_mode(IMonoClassMember *p_member) const { - if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute))) { + if (p_member->has_attribute(CACHED_CLASS(AnyAttribute))) { return Multiplayer::RPC_MODE_ANY; } - if (p_member->has_attribute(CACHED_CLASS(PuppetAttribute))) { + if (p_member->has_attribute(CACHED_CLASS(AuthorityAttribute))) { return Multiplayer::RPC_MODE_AUTHORITY; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs index 2dedba2be3..1da91ea867 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs @@ -3,8 +3,8 @@ using System; namespace Godot { [AttributeUsage(AttributeTargets.Method)] - public class RemoteAttribute : Attribute { } + public class AnyAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] - public class PuppetAttribute : Attribute { } + public class AuthorityAttribute : Attribute { } } diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index 8b215a66c2..34e845a589 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -140,8 +140,8 @@ void CachedData::clear_godot_api_cache() { field_ExportAttribute_hintString = nullptr; class_SignalAttribute = nullptr; class_ToolAttribute = nullptr; - class_RemoteAttribute = nullptr; - class_PuppetAttribute = nullptr; + class_AnyAttribute = nullptr; + class_AuthorityAttribute = nullptr; class_GodotMethodAttribute = nullptr; field_GodotMethodAttribute_methodName = nullptr; class_ScriptPathAttribute = nullptr; @@ -265,8 +265,8 @@ void update_godot_api_cache() { CACHE_FIELD_AND_CHECK(ExportAttribute, hintString, CACHED_CLASS(ExportAttribute)->get_field("hintString")); CACHE_CLASS_AND_CHECK(SignalAttribute, GODOT_API_CLASS(SignalAttribute)); CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute)); - CACHE_CLASS_AND_CHECK(RemoteAttribute, GODOT_API_CLASS(RemoteAttribute)); - CACHE_CLASS_AND_CHECK(PuppetAttribute, GODOT_API_CLASS(PuppetAttribute)); + CACHE_CLASS_AND_CHECK(AnyAttribute, GODOT_API_CLASS(AnyAttribute)); + CACHE_CLASS_AND_CHECK(AuthorityAttribute, GODOT_API_CLASS(AuthorityAttribute)); CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute)); CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName")); CACHE_CLASS_AND_CHECK(ScriptPathAttribute, GODOT_API_CLASS(ScriptPathAttribute)); diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index fd28bbda14..e60a4d5279 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -111,8 +111,8 @@ struct CachedData { GDMonoField *field_ExportAttribute_hintString; GDMonoClass *class_SignalAttribute; GDMonoClass *class_ToolAttribute; - GDMonoClass *class_RemoteAttribute; - GDMonoClass *class_PuppetAttribute; + GDMonoClass *class_AnyAttribute; + GDMonoClass *class_AuthorityAttribute; GDMonoClass *class_GodotMethodAttribute; GDMonoField *field_GodotMethodAttribute_methodName; GDMonoClass *class_ScriptPathAttribute; diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index 3150ca0bc8..962bf79150 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -664,7 +664,7 @@ void NavMap::sync() { } else { other1 = other_edge_p1.lerp(other_edge_p2, (1.0 - projected_p1_ratio) / (projected_p2_ratio - projected_p1_ratio)); } - if ((self1 - other1).length() > edge_connection_margin) { + if (other1.distance_to(self1) > edge_connection_margin) { continue; } @@ -675,7 +675,7 @@ void NavMap::sync() { } else { other2 = other_edge_p1.lerp(other_edge_p2, (0.0 - projected_p1_ratio) / (projected_p2_ratio - projected_p1_ratio)); } - if ((self2 - other2).length() > edge_connection_margin) { + if (other2.distance_to(self2) > edge_connection_margin) { continue; } diff --git a/modules/raycast/SCsub b/modules/raycast/SCsub index 1fdc8fe1b3..4820cf7608 100644 --- a/modules/raycast/SCsub +++ b/modules/raycast/SCsub @@ -79,6 +79,7 @@ if env["builtin_embree"]: env.Append(LIBS=["psapi"]) env_thirdparty = env_raycast.Clone() + env_thirdparty.force_optimization_on_debug() env_thirdparty.disable_warnings() env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 2336153c0d..c93f353cea 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -131,20 +131,6 @@ hb_position_t TextServerAdvanced::hb_bmp_get_glyph_h_kerning(hb_font_t *p_font, return bm_font->face->kerning_map[Vector2i(p_left_glyph, p_right_glyph)].x * 64; } -hb_position_t TextServerAdvanced::hb_bmp_get_glyph_v_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data) { - const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); - - if (!bm_font->face) { - return 0; - } - - if (!bm_font->face->kerning_map.has(Vector2i(p_left_glyph, p_right_glyph))) { - return 0; - } - - return bm_font->face->kerning_map[Vector2i(p_left_glyph, p_right_glyph)].y * 64; -} - hb_bool_t TextServerAdvanced::hb_bmp_get_glyph_v_origin(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_position_t *r_x, hb_position_t *r_y, void *p_user_data) { const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data); @@ -205,7 +191,6 @@ void TextServerAdvanced::hb_bmp_create_font_funcs() { hb_font_funcs_set_glyph_v_advance_func(funcs, hb_bmp_get_glyph_v_advance, nullptr, nullptr); hb_font_funcs_set_glyph_v_origin_func(funcs, hb_bmp_get_glyph_v_origin, nullptr, nullptr); hb_font_funcs_set_glyph_h_kerning_func(funcs, hb_bmp_get_glyph_h_kerning, nullptr, nullptr); - hb_font_funcs_set_glyph_v_kerning_func(funcs, hb_bmp_get_glyph_v_kerning, nullptr, nullptr); hb_font_funcs_set_glyph_extents_func(funcs, hb_bmp_get_glyph_extents, nullptr, nullptr); hb_font_funcs_make_immutable(funcs); @@ -3588,6 +3573,7 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, re shaped_text_shape(p_shaped_line); } + sd->text_trimmed = false; sd->overrun_trim_data.ellipsis_glyph_buf.clear(); bool add_ellipsis = (p_trim_flags & OVERRUN_ADD_ELLIPSIS) == OVERRUN_ADD_ELLIPSIS; diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 5989035800..fc0e7a09a7 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -269,7 +269,6 @@ class TextServerAdvanced : public TextServer { static hb_position_t hb_bmp_get_glyph_h_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data); static hb_position_t hb_bmp_get_glyph_v_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data); static hb_position_t hb_bmp_get_glyph_h_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data); - static hb_position_t hb_bmp_get_glyph_v_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data); static hb_bool_t hb_bmp_get_glyph_v_origin(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_position_t *r_x, hb_position_t *r_y, void *p_user_data); static hb_bool_t hb_bmp_get_glyph_extents(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_glyph_extents_t *r_extents, void *p_user_data); static hb_bool_t hb_bmp_get_font_h_extents(hb_font_t *p_font, void *p_font_data, hb_font_extents_t *r_metrics, void *p_user_data); diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 8a1bd93c65..1323aa80ce 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -2683,6 +2683,7 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, re shaped_text_shape(p_shaped_line); } + sd->text_trimmed = false; sd->overrun_trim_data.ellipsis_glyph_buf.clear(); bool add_ellipsis = (p_trim_flags & OVERRUN_ADD_ELLIPSIS) == OVERRUN_ADD_ELLIPSIS; diff --git a/modules/webrtc/SCsub b/modules/webrtc/SCsub index 31b8a73bf2..e6b9959840 100644 --- a/modules/webrtc/SCsub +++ b/modules/webrtc/SCsub @@ -4,11 +4,6 @@ Import("env") Import("env_modules") env_webrtc = env_modules.Clone() -use_gdnative = env_webrtc["module_gdnative_enabled"] - -if use_gdnative: # GDNative is retained in Javascript for export compatibility - env_webrtc.Append(CPPDEFINES=["WEBRTC_GDNATIVE_ENABLED"]) - env_webrtc.Prepend(CPPPATH=["#modules/gdnative/include/"]) if env["platform"] == "javascript": # Our JavaScript/C++ interface. diff --git a/modules/webrtc/config.py b/modules/webrtc/config.py index 3281415f38..4ad918833a 100644 --- a/modules/webrtc/config.py +++ b/modules/webrtc/config.py @@ -11,6 +11,8 @@ def get_doc_classes(): "WebRTCPeerConnection", "WebRTCDataChannel", "WebRTCMultiplayerPeer", + "WebRTCPeerConnectionExtension", + "WebRTCDataChannelExtension", ] diff --git a/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml b/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml new file mode 100644 index 0000000000..26a4391b83 --- /dev/null +++ b/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="WebRTCDataChannelExtension" inherits="WebRTCDataChannel" version="4.0"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="_close" qualifiers="virtual"> + <return type="void" /> + <description> + </description> + </method> + <method name="_get_available_packet_count" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_buffered_amount" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_id" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_label" qualifiers="virtual const"> + <return type="String" /> + <description> + </description> + </method> + <method name="_get_max_packet_life_time" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_max_packet_size" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_max_retransmits" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_packet" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="r_buffer" type="const void*" /> + <argument index="1" name="r_buffer_size" type="int32_t*" /> + <description> + </description> + </method> + <method name="_get_protocol" qualifiers="virtual const"> + <return type="String" /> + <description> + </description> + </method> + <method name="_get_ready_state" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_write_mode" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_is_negotiated" qualifiers="virtual const"> + <return type="bool" /> + <description> + </description> + </method> + <method name="_is_ordered" qualifiers="virtual const"> + <return type="bool" /> + <description> + </description> + </method> + <method name="_poll" qualifiers="virtual"> + <return type="int" /> + <description> + </description> + </method> + <method name="_put_packet" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="p_buffer" type="const void*" /> + <argument index="1" name="p_buffer_size" type="int" /> + <description> + </description> + </method> + <method name="_set_write_mode" qualifiers="virtual"> + <return type="void" /> + <argument index="0" name="p_write_mode" type="int" /> + <description> + </description> + </method> + <method name="_was_string_packet" qualifiers="virtual const"> + <return type="bool" /> + <description> + </description> + </method> + </methods> +</class> diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml index 9040d510c0..a8360a4d45 100644 --- a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml +++ b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml @@ -7,6 +7,7 @@ This class constructs a full mesh of [WebRTCPeerConnection] (one connection for each peer) that can be used as a [member MultiplayerAPI.multiplayer_peer]. You can add each [WebRTCPeerConnection] via [method add_peer] or remove them via [method remove_peer]. Peers must be added in [constant WebRTCPeerConnection.STATE_NEW] state to allow it to create the appropriate channels. This class will not create offers nor set descriptions, it will only poll them, and notify connections and disconnections. [signal MultiplayerPeer.connection_succeeded] and [signal MultiplayerPeer.server_disconnected] will not be emitted unless [code]server_compatibility[/code] is [code]true[/code] in [method initialize]. Beside that data transfer works like in a [MultiplayerPeer]. + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> </tutorials> @@ -67,8 +68,4 @@ </description> </method> </methods> - <members> - <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" /> - <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="TransferMode" default="2" /> - </members> </class> diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml new file mode 100644 index 0000000000..d296fcd6e7 --- /dev/null +++ b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="WebRTCPeerConnectionExtension" inherits="WebRTCPeerConnection" version="4.0"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="_add_ice_candidate" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="p_sdp_mid_name" type="String" /> + <argument index="1" name="p_sdp_mline_index" type="int" /> + <argument index="2" name="p_sdp_name" type="String" /> + <description> + </description> + </method> + <method name="_close" qualifiers="virtual"> + <return type="void" /> + <description> + </description> + </method> + <method name="_create_data_channel" qualifiers="virtual"> + <return type="Object" /> + <argument index="0" name="p_label" type="String" /> + <argument index="1" name="p_config" type="Dictionary" /> + <description> + </description> + </method> + <method name="_create_offer" qualifiers="virtual"> + <return type="int" /> + <description> + </description> + </method> + <method name="_get_connection_state" qualifiers="virtual const"> + <return type="int" /> + <description> + </description> + </method> + <method name="_initialize" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="p_config" type="Dictionary" /> + <description> + </description> + </method> + <method name="_poll" qualifiers="virtual"> + <return type="int" /> + <description> + </description> + </method> + <method name="_set_local_description" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="p_type" type="String" /> + <argument index="1" name="p_sdp" type="String" /> + <description> + </description> + </method> + <method name="_set_remote_description" qualifiers="virtual"> + <return type="int" /> + <argument index="0" name="p_type" type="String" /> + <argument index="1" name="p_sdp" type="String" /> + <description> + </description> + </method> + <method name="make_default"> + <return type="void" /> + <description> + </description> + </method> + </methods> +</class> diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp index 63ecc03a4c..8110e4a048 100644 --- a/modules/webrtc/register_types.cpp +++ b/modules/webrtc/register_types.cpp @@ -31,17 +31,11 @@ #include "register_types.h" #include "core/config/project_settings.h" #include "webrtc_data_channel.h" +#include "webrtc_multiplayer_peer.h" #include "webrtc_peer_connection.h" -#ifdef JAVASCRIPT_ENABLED -#include "emscripten.h" -#include "webrtc_peer_connection_js.h" -#endif -#ifdef WEBRTC_GDNATIVE_ENABLED -#include "webrtc_data_channel_gdnative.h" -#include "webrtc_peer_connection_gdnative.h" -#endif -#include "webrtc_multiplayer_peer.h" +#include "webrtc_data_channel_extension.h" +#include "webrtc_peer_connection_extension.h" void register_webrtc_types() { #define _SET_HINT(NAME, _VAL_, _MAX_) \ @@ -50,18 +44,12 @@ void register_webrtc_types() { _SET_HINT(WRTC_IN_BUF, 64, 4096); -#ifdef JAVASCRIPT_ENABLED - WebRTCPeerConnectionJS::make_default(); -#elif defined(WEBRTC_GDNATIVE_ENABLED) - WebRTCPeerConnectionGDNative::make_default(); -#endif - ClassDB::register_custom_instance_class<WebRTCPeerConnection>(); -#ifdef WEBRTC_GDNATIVE_ENABLED - GDREGISTER_CLASS(WebRTCPeerConnectionGDNative); - GDREGISTER_CLASS(WebRTCDataChannelGDNative); -#endif + GDREGISTER_CLASS(WebRTCPeerConnectionExtension); + GDREGISTER_VIRTUAL_CLASS(WebRTCDataChannel); + GDREGISTER_CLASS(WebRTCDataChannelExtension); + GDREGISTER_CLASS(WebRTCMultiplayerPeer); } diff --git a/modules/webrtc/webrtc_data_channel_extension.cpp b/modules/webrtc/webrtc_data_channel_extension.cpp new file mode 100644 index 0000000000..ae346f6d8e --- /dev/null +++ b/modules/webrtc/webrtc_data_channel_extension.cpp @@ -0,0 +1,215 @@ +/*************************************************************************/ +/* webrtc_data_channel_extension.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "webrtc_data_channel_extension.h" + +void WebRTCDataChannelExtension::_bind_methods() { + ADD_PROPERTY_DEFAULT("write_mode", WRITE_MODE_BINARY); + + GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size"); + GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size"); + GDVIRTUAL_BIND(_get_available_packet_count); + GDVIRTUAL_BIND(_get_max_packet_size); + + GDVIRTUAL_BIND(_poll); + GDVIRTUAL_BIND(_close); + + GDVIRTUAL_BIND(_set_write_mode, "p_write_mode"); + GDVIRTUAL_BIND(_get_write_mode); + + GDVIRTUAL_BIND(_was_string_packet); + GDVIRTUAL_BIND(_get_ready_state); + GDVIRTUAL_BIND(_get_label); + GDVIRTUAL_BIND(_is_ordered); + GDVIRTUAL_BIND(_get_id); + GDVIRTUAL_BIND(_get_max_packet_life_time); + GDVIRTUAL_BIND(_get_max_retransmits); + GDVIRTUAL_BIND(_get_protocol); + GDVIRTUAL_BIND(_is_negotiated); + GDVIRTUAL_BIND(_get_buffered_amount); +} + +int WebRTCDataChannelExtension::get_available_packet_count() const { + int count; + if (GDVIRTUAL_CALL(_get_available_packet_count, count)) { + return count; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_available_packet_count is unimplemented!"); + return -1; +} + +Error WebRTCDataChannelExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { + int err; + if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_packet_native is unimplemented!"); + return FAILED; +} + +Error WebRTCDataChannelExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) { + int err; + if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_put_packet_native is unimplemented!"); + return FAILED; +} + +int WebRTCDataChannelExtension::get_max_packet_size() const { + int size; + if (GDVIRTUAL_CALL(_get_max_packet_size, size)) { + return size; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_max_packet_size is unimplemented!"); + return 0; +} + +Error WebRTCDataChannelExtension::poll() { + int err; + if (GDVIRTUAL_CALL(_poll, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_poll is unimplemented!"); + return ERR_UNCONFIGURED; +} + +void WebRTCDataChannelExtension::close() { + if (GDVIRTUAL_CALL(_close)) { + return; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_close is unimplemented!"); +} + +void WebRTCDataChannelExtension::set_write_mode(WriteMode p_mode) { + if (GDVIRTUAL_CALL(_set_write_mode, p_mode)) { + return; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_set_write_mode is unimplemented!"); +} + +WebRTCDataChannel::WriteMode WebRTCDataChannelExtension::get_write_mode() const { + int mode; + if (GDVIRTUAL_CALL(_get_write_mode, mode)) { + return (WriteMode)mode; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_write_mode is unimplemented!"); + return WRITE_MODE_BINARY; +} + +bool WebRTCDataChannelExtension::was_string_packet() const { + bool was_string; + if (GDVIRTUAL_CALL(_was_string_packet, was_string)) { + return was_string; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_was_string_packet is unimplemented!"); + return false; +} + +WebRTCDataChannel::ChannelState WebRTCDataChannelExtension::get_ready_state() const { + int state; + if (GDVIRTUAL_CALL(_get_ready_state, state)) { + return (ChannelState)state; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_ready_state is unimplemented!"); + return STATE_CLOSED; +} + +String WebRTCDataChannelExtension::get_label() const { + String label; + if (GDVIRTUAL_CALL(_get_label, label)) { + return label; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_label is unimplemented!"); + return label; +} + +bool WebRTCDataChannelExtension::is_ordered() const { + bool ordered; + if (GDVIRTUAL_CALL(_is_ordered, ordered)) { + return ordered; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_is_ordered is unimplemented!"); + return false; +} + +int WebRTCDataChannelExtension::get_id() const { + int id; + if (GDVIRTUAL_CALL(_get_id, id)) { + return id; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_id is unimplemented!"); + return -1; +} + +int WebRTCDataChannelExtension::get_max_packet_life_time() const { + int lifetime; + if (GDVIRTUAL_CALL(_get_max_packet_life_time, lifetime)) { + return lifetime; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_max_packet_life_time is unimplemented!"); + return -1; +} + +int WebRTCDataChannelExtension::get_max_retransmits() const { + int retransmits; + if (GDVIRTUAL_CALL(_get_max_retransmits, retransmits)) { + return retransmits; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_max_retransmits is unimplemented!"); + return -1; +} + +String WebRTCDataChannelExtension::get_protocol() const { + String protocol; + if (GDVIRTUAL_CALL(_get_protocol, protocol)) { + return protocol; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_protocol is unimplemented!"); + return protocol; +} + +bool WebRTCDataChannelExtension::is_negotiated() const { + bool negotiated; + if (GDVIRTUAL_CALL(_is_negotiated, negotiated)) { + return negotiated; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_is_negotiated is unimplemented!"); + return false; +} + +int WebRTCDataChannelExtension::get_buffered_amount() const { + int amount; + if (GDVIRTUAL_CALL(_get_buffered_amount, amount)) { + return amount; + } + WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_buffered_amount is unimplemented!"); + return -1; +} diff --git a/modules/webrtc/webrtc_data_channel_gdnative.h b/modules/webrtc/webrtc_data_channel_extension.h index 5c80edd48c..eec96b4c62 100644 --- a/modules/webrtc/webrtc_data_channel_gdnative.h +++ b/modules/webrtc/webrtc_data_channel_extension.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* webrtc_data_channel_gdnative.h */ +/* webrtc_data_channel_extension.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,26 +28,22 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef WEBRTC_DATA_CHANNEL_GDNATIVE_H -#define WEBRTC_DATA_CHANNEL_GDNATIVE_H +#ifndef WEBRTC_DATA_CHANNEL_EXTENSION_H +#define WEBRTC_DATA_CHANNEL_EXTENSION_H -#ifdef WEBRTC_GDNATIVE_ENABLED - -#include "modules/gdnative/include/net/godot_net.h" #include "webrtc_data_channel.h" -class WebRTCDataChannelGDNative : public WebRTCDataChannel { - GDCLASS(WebRTCDataChannelGDNative, WebRTCDataChannel); +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" +#include "core/variant/native_ptr.h" + +class WebRTCDataChannelExtension : public WebRTCDataChannel { + GDCLASS(WebRTCDataChannelExtension, WebRTCDataChannel); protected: static void _bind_methods(); -private: - const godot_net_webrtc_data_channel *interface; - public: - void set_native_webrtc_data_channel(const godot_net_webrtc_data_channel *p_impl); - virtual void set_write_mode(WriteMode mode) override; virtual WriteMode get_write_mode() const override; virtual bool was_string_packet() const override; @@ -72,10 +68,31 @@ public: virtual int get_max_packet_size() const override; - WebRTCDataChannelGDNative(); - ~WebRTCDataChannelGDNative(); -}; + /** GDExtension **/ + GDVIRTUAL0RC(int, _get_available_packet_count); + GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>); + GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int); + GDVIRTUAL0RC(int, _get_max_packet_size); -#endif // WEBRTC_GDNATIVE_ENABLED + GDVIRTUAL0R(int, _poll); + GDVIRTUAL0(_close); + + GDVIRTUAL1(_set_write_mode, int); + GDVIRTUAL0RC(int, _get_write_mode); + + GDVIRTUAL0RC(bool, _was_string_packet); + + GDVIRTUAL0RC(int, _get_ready_state); + GDVIRTUAL0RC(String, _get_label); + GDVIRTUAL0RC(bool, _is_ordered); + GDVIRTUAL0RC(int, _get_id); + GDVIRTUAL0RC(int, _get_max_packet_life_time); + GDVIRTUAL0RC(int, _get_max_retransmits); + GDVIRTUAL0RC(String, _get_protocol); + GDVIRTUAL0RC(bool, _is_negotiated); + GDVIRTUAL0RC(int, _get_buffered_amount); + + WebRTCDataChannelExtension() {} +}; -#endif // WEBRTC_DATA_CHANNEL_GDNATIVE_H +#endif // WEBRTC_DATA_CHANNEL_EXTENSION_H diff --git a/modules/webrtc/webrtc_data_channel_gdnative.cpp b/modules/webrtc/webrtc_data_channel_gdnative.cpp deleted file mode 100644 index 10a3367557..0000000000 --- a/modules/webrtc/webrtc_data_channel_gdnative.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/*************************************************************************/ -/* webrtc_data_channel_gdnative.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifdef WEBRTC_GDNATIVE_ENABLED - -#include "webrtc_data_channel_gdnative.h" - -#include "core/io/resource_loader.h" -#include "modules/gdnative/nativescript/nativescript.h" - -void WebRTCDataChannelGDNative::_bind_methods() { - ADD_PROPERTY_DEFAULT("write_mode", WRITE_MODE_BINARY); -} - -WebRTCDataChannelGDNative::WebRTCDataChannelGDNative() { - interface = nullptr; -} - -WebRTCDataChannelGDNative::~WebRTCDataChannelGDNative() { -} - -Error WebRTCDataChannelGDNative::poll() { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)interface->poll(interface->data); -} - -void WebRTCDataChannelGDNative::close() { - ERR_FAIL_COND(interface == nullptr); - interface->close(interface->data); -} - -void WebRTCDataChannelGDNative::set_write_mode(WriteMode p_mode) { - ERR_FAIL_COND(interface == nullptr); - interface->set_write_mode(interface->data, p_mode); -} - -WebRTCDataChannel::WriteMode WebRTCDataChannelGDNative::get_write_mode() const { - ERR_FAIL_COND_V(interface == nullptr, WRITE_MODE_BINARY); - return (WriteMode)interface->get_write_mode(interface->data); -} - -bool WebRTCDataChannelGDNative::was_string_packet() const { - ERR_FAIL_COND_V(interface == nullptr, false); - return interface->was_string_packet(interface->data); -} - -WebRTCDataChannel::ChannelState WebRTCDataChannelGDNative::get_ready_state() const { - ERR_FAIL_COND_V(interface == nullptr, STATE_CLOSED); - return (ChannelState)interface->get_ready_state(interface->data); -} - -String WebRTCDataChannelGDNative::get_label() const { - ERR_FAIL_COND_V(interface == nullptr, ""); - return String(interface->get_label(interface->data)); -} - -bool WebRTCDataChannelGDNative::is_ordered() const { - ERR_FAIL_COND_V(interface == nullptr, false); - return interface->is_ordered(interface->data); -} - -int WebRTCDataChannelGDNative::get_id() const { - ERR_FAIL_COND_V(interface == nullptr, -1); - return interface->get_id(interface->data); -} - -int WebRTCDataChannelGDNative::get_max_packet_life_time() const { - ERR_FAIL_COND_V(interface == nullptr, -1); - return interface->get_max_packet_life_time(interface->data); -} - -int WebRTCDataChannelGDNative::get_max_retransmits() const { - ERR_FAIL_COND_V(interface == nullptr, -1); - return interface->get_max_retransmits(interface->data); -} - -String WebRTCDataChannelGDNative::get_protocol() const { - ERR_FAIL_COND_V(interface == nullptr, ""); - return String(interface->get_protocol(interface->data)); -} - -bool WebRTCDataChannelGDNative::is_negotiated() const { - ERR_FAIL_COND_V(interface == nullptr, false); - return interface->is_negotiated(interface->data); -} - -int WebRTCDataChannelGDNative::get_buffered_amount() const { - ERR_FAIL_COND_V(interface == NULL, 0); - return interface->get_buffered_amount(interface->data); -} - -Error WebRTCDataChannelGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)interface->get_packet(interface->data, r_buffer, &r_buffer_size); -} - -Error WebRTCDataChannelGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size); -} - -int WebRTCDataChannelGDNative::get_max_packet_size() const { - ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->get_max_packet_size(interface->data); -} - -int WebRTCDataChannelGDNative::get_available_packet_count() const { - ERR_FAIL_COND_V(interface == nullptr, 0); - return interface->get_available_packet_count(interface->data); -} - -void WebRTCDataChannelGDNative::set_native_webrtc_data_channel(const godot_net_webrtc_data_channel *p_impl) { - interface = p_impl; -} - -#endif // WEBRTC_GDNATIVE_ENABLED diff --git a/modules/webrtc/webrtc_multiplayer_peer.cpp b/modules/webrtc/webrtc_multiplayer_peer.cpp index d60d694df1..48117f05f2 100644 --- a/modules/webrtc/webrtc_multiplayer_peer.cpp +++ b/modules/webrtc/webrtc_multiplayer_peer.cpp @@ -43,22 +43,6 @@ void WebRTCMultiplayerPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("close"), &WebRTCMultiplayerPeer::close); } -void WebRTCMultiplayerPeer::set_transfer_channel(int p_channel) { - transfer_channel = p_channel; -} - -int WebRTCMultiplayerPeer::get_transfer_channel() const { - return transfer_channel; -} - -void WebRTCMultiplayerPeer::set_transfer_mode(Multiplayer::TransferMode p_mode) { - transfer_mode = p_mode; -} - -Multiplayer::TransferMode WebRTCMultiplayerPeer::get_transfer_mode() const { - return transfer_mode; -} - void WebRTCMultiplayerPeer::set_target_peer(int p_peer_id) { target_peer = p_peer_id; } @@ -188,14 +172,6 @@ void WebRTCMultiplayerPeer::_find_next_peer() { next_packet_peer = 0; } -void WebRTCMultiplayerPeer::set_refuse_new_connections(bool p_enable) { - refuse_connections = p_enable; -} - -bool WebRTCMultiplayerPeer::is_refusing_new_connections() const { - return refuse_connections; -} - MultiplayerPeer::ConnectionStatus WebRTCMultiplayerPeer::get_connection_status() const { return connection_status; } @@ -279,7 +255,7 @@ Dictionary WebRTCMultiplayerPeer::get_peers() { Error WebRTCMultiplayerPeer::add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime) { ERR_FAIL_COND_V(p_peer_id < 0 || p_peer_id > ~(1 << 31), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_unreliable_lifetime < 0, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(refuse_connections, ERR_UNAUTHORIZED); + ERR_FAIL_COND_V(is_refusing_new_connections(), ERR_UNAUTHORIZED); // Peer must be valid, and in new state (to create data channels) ERR_FAIL_COND_V(!p_peer.is_valid(), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_peer->get_connection_state() != WebRTCPeerConnection::STATE_NEW, ERR_INVALID_PARAMETER); @@ -352,9 +328,9 @@ Error WebRTCMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_ Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, ERR_UNCONFIGURED); - int ch = transfer_channel; + int ch = get_transfer_channel(); if (ch == 0) { - switch (transfer_mode) { + switch (get_transfer_mode()) { case Multiplayer::TRANSFER_MODE_RELIABLE: ch = CH_RELIABLE; break; diff --git a/modules/webrtc/webrtc_multiplayer_peer.h b/modules/webrtc/webrtc_multiplayer_peer.h index 80a6491492..4a7e9ad7c8 100644 --- a/modules/webrtc/webrtc_multiplayer_peer.h +++ b/modules/webrtc/webrtc_multiplayer_peer.h @@ -65,10 +65,7 @@ private: uint32_t unique_id = 0; int target_peer = 0; int client_count = 0; - bool refuse_connections = false; ConnectionStatus connection_status = CONNECTION_DISCONNECTED; - int transfer_channel = 0; - Multiplayer::TransferMode transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE; int next_packet_peer = 0; bool server_compat = false; @@ -97,10 +94,6 @@ public: int get_max_packet_size() const override; // MultiplayerPeer - void set_transfer_channel(int p_channel) override; - int get_transfer_channel() const override; - void set_transfer_mode(Multiplayer::TransferMode p_mode) override; - Multiplayer::TransferMode get_transfer_mode() const override; void set_target_peer(int p_peer_id) override; int get_unique_id() const override; @@ -110,9 +103,6 @@ public: void poll() override; - void set_refuse_new_connections(bool p_enable) override; - bool is_refusing_new_connections() const override; - ConnectionStatus get_connection_status() const override; }; diff --git a/modules/webrtc/webrtc_peer_connection.cpp b/modules/webrtc/webrtc_peer_connection.cpp index 3e2938bf7d..ad28aa76c7 100644 --- a/modules/webrtc/webrtc_peer_connection.cpp +++ b/modules/webrtc/webrtc_peer_connection.cpp @@ -30,17 +30,29 @@ #include "webrtc_peer_connection.h" -WebRTCPeerConnection *(*WebRTCPeerConnection::_create)() = nullptr; +#ifdef JAVASCRIPT_ENABLED +#include "webrtc_peer_connection_js.h" +#else +#include "webrtc_peer_connection_extension.h" +#endif -Ref<WebRTCPeerConnection> WebRTCPeerConnection::create_ref() { - return create(); +StringName WebRTCPeerConnection::default_extension; + +void WebRTCPeerConnection::set_default_extension(const StringName &p_extension) { + default_extension = p_extension; } WebRTCPeerConnection *WebRTCPeerConnection::create() { - if (!_create) { - return nullptr; +#ifdef JAVASCRIPT_ENABLED + return memnew(WebRTCPeerConnectionJS); +#else + if (default_extension == String()) { + WARN_PRINT_ONCE("No default WebRTC extension configured."); + return memnew(WebRTCPeerConnectionExtension); } - return _create(); + Object *obj = ClassDB::instantiate(default_extension); + return Object::cast_to<WebRTCPeerConnectionExtension>(obj); +#endif } void WebRTCPeerConnection::_bind_methods() { diff --git a/modules/webrtc/webrtc_peer_connection.h b/modules/webrtc/webrtc_peer_connection.h index fcfb9ae9ae..e2ef3e55ad 100644 --- a/modules/webrtc/webrtc_peer_connection.h +++ b/modules/webrtc/webrtc_peer_connection.h @@ -47,11 +47,15 @@ public: STATE_CLOSED }; +private: + static StringName default_extension; + protected: static void _bind_methods(); - static WebRTCPeerConnection *(*_create)(); public: + static void set_default_extension(const StringName &p_name); + virtual ConnectionState get_connection_state() const = 0; virtual Error initialize(Dictionary p_config = Dictionary()) = 0; @@ -63,7 +67,6 @@ public: virtual Error poll() = 0; virtual void close() = 0; - static Ref<WebRTCPeerConnection> create_ref(); static WebRTCPeerConnection *create(); WebRTCPeerConnection(); diff --git a/modules/webrtc/webrtc_peer_connection_extension.cpp b/modules/webrtc/webrtc_peer_connection_extension.cpp new file mode 100644 index 0000000000..33288e66d6 --- /dev/null +++ b/modules/webrtc/webrtc_peer_connection_extension.cpp @@ -0,0 +1,131 @@ +/*************************************************************************/ +/* webrtc_peer_connection_extension.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "webrtc_peer_connection_extension.h" + +void WebRTCPeerConnectionExtension::_bind_methods() { + ClassDB::bind_method(D_METHOD("make_default"), &WebRTCPeerConnectionExtension::make_default); + + GDVIRTUAL_BIND(_get_connection_state); + GDVIRTUAL_BIND(_initialize, "p_config"); + GDVIRTUAL_BIND(_create_data_channel, "p_label", "p_config"); + GDVIRTUAL_BIND(_create_offer); + GDVIRTUAL_BIND(_set_remote_description, "p_type", "p_sdp"); + GDVIRTUAL_BIND(_set_local_description, "p_type", "p_sdp"); + GDVIRTUAL_BIND(_add_ice_candidate, "p_sdp_mid_name", "p_sdp_mline_index", "p_sdp_name"); + GDVIRTUAL_BIND(_poll); + GDVIRTUAL_BIND(_close); +} + +void WebRTCPeerConnectionExtension::make_default() { + ERR_FAIL_COND_MSG(!_get_extension(), vformat("Can't make %s the default without extending it.", get_class())); + WebRTCPeerConnection::set_default_extension(get_class()); +} + +WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionExtension::get_connection_state() const { + int state; + if (GDVIRTUAL_CALL(_get_connection_state, state)) { + return (ConnectionState)state; + } + WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_get_connection_state is unimplemented!"); + return STATE_DISCONNECTED; +} + +Error WebRTCPeerConnectionExtension::initialize(Dictionary p_config) { + int err; + if (GDVIRTUAL_CALL(_initialize, p_config, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_initialize is unimplemented!"); + return ERR_UNCONFIGURED; +} + +Ref<WebRTCDataChannel> WebRTCPeerConnectionExtension::create_data_channel(String p_label, Dictionary p_options) { + Object *ret = nullptr; + if (GDVIRTUAL_CALL(_create_data_channel, p_label, p_options, ret)) { + WebRTCDataChannel *ch = Object::cast_to<WebRTCDataChannel>(ret); + ERR_FAIL_COND_V_MSG(ret && !ch, nullptr, "Returned object must be an instance of WebRTCDataChannel."); + return ch; + } + WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_create_data_channel is unimplemented!"); + return nullptr; +} + +Error WebRTCPeerConnectionExtension::create_offer() { + int err; + if (GDVIRTUAL_CALL(_create_offer, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_create_offer is unimplemented!"); + return ERR_UNCONFIGURED; +} + +Error WebRTCPeerConnectionExtension::set_local_description(String p_type, String p_sdp) { + int err; + if (GDVIRTUAL_CALL(_set_local_description, p_type, p_sdp, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_set_local_description is unimplemented!"); + return ERR_UNCONFIGURED; +} + +Error WebRTCPeerConnectionExtension::set_remote_description(String p_type, String p_sdp) { + int err; + if (GDVIRTUAL_CALL(_set_remote_description, p_type, p_sdp, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_set_remote_description is unimplemented!"); + return ERR_UNCONFIGURED; +} + +Error WebRTCPeerConnectionExtension::add_ice_candidate(String p_sdp_mid_name, int p_sdp_mline_index, String p_sdp_name) { + int err; + if (GDVIRTUAL_CALL(_add_ice_candidate, p_sdp_mid_name, p_sdp_mline_index, p_sdp_name, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_add_ice_candidate is unimplemented!"); + return ERR_UNCONFIGURED; +} + +Error WebRTCPeerConnectionExtension::poll() { + int err; + if (GDVIRTUAL_CALL(_poll, err)) { + return (Error)err; + } + WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_poll is unimplemented!"); + return ERR_UNCONFIGURED; +} + +void WebRTCPeerConnectionExtension::close() { + if (GDVIRTUAL_CALL(_close)) { + return; + } + WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_close is unimplemented!"); +} diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.h b/modules/webrtc/webrtc_peer_connection_extension.h index 578af0202f..b3c2039fc1 100644 --- a/modules/webrtc/webrtc_peer_connection_gdnative.h +++ b/modules/webrtc/webrtc_peer_connection_extension.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* webrtc_peer_connection_gdnative.h */ +/* webrtc_peer_connection_extension.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,30 +28,23 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef WEBRTC_PEER_CONNECTION_GDNATIVE_H -#define WEBRTC_PEER_CONNECTION_GDNATIVE_H +#ifndef WEBRTC_PEER_CONNECTION_EXTENSION_H +#define WEBRTC_PEER_CONNECTION_EXTENSION_H -#ifdef WEBRTC_GDNATIVE_ENABLED - -#include "modules/gdnative/include/net/godot_net.h" #include "webrtc_peer_connection.h" -class WebRTCPeerConnectionGDNative : public WebRTCPeerConnection { - GDCLASS(WebRTCPeerConnectionGDNative, WebRTCPeerConnection); +#include "core/object/gdvirtual.gen.inc" +#include "core/object/script_language.h" +#include "core/variant/native_ptr.h" + +class WebRTCPeerConnectionExtension : public WebRTCPeerConnection { + GDCLASS(WebRTCPeerConnectionExtension, WebRTCPeerConnection); protected: static void _bind_methods(); - static WebRTCPeerConnection *_create(); - -private: - static const godot_net_webrtc_library *default_library; - const godot_net_webrtc_peer_connection *interface; public: - static Error set_default_library(const godot_net_webrtc_library *p_library); - static void make_default() { WebRTCPeerConnection::_create = WebRTCPeerConnectionGDNative::_create; } - - void set_native_webrtc_peer_connection(const godot_net_webrtc_peer_connection *p_impl); + void make_default(); virtual ConnectionState get_connection_state() const override; @@ -60,14 +53,22 @@ public: virtual Error create_offer() override; virtual Error set_remote_description(String type, String sdp) override; virtual Error set_local_description(String type, String sdp) override; - virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) override; + virtual Error add_ice_candidate(String p_sdp_mid_name, int p_sdp_mline_index, String p_sdp_name) override; virtual Error poll() override; virtual void close() override; - WebRTCPeerConnectionGDNative(); - ~WebRTCPeerConnectionGDNative(); -}; + /** GDExtension **/ + GDVIRTUAL0RC(int, _get_connection_state); + GDVIRTUAL1R(int, _initialize, Dictionary); + GDVIRTUAL2R(Object *, _create_data_channel, String, Dictionary); + GDVIRTUAL0R(int, _create_offer); + GDVIRTUAL2R(int, _set_remote_description, String, String); + GDVIRTUAL2R(int, _set_local_description, String, String); + GDVIRTUAL3R(int, _add_ice_candidate, String, int, String); + GDVIRTUAL0R(int, _poll); + GDVIRTUAL0(_close); -#endif // WEBRTC_GDNATIVE_ENABLED + WebRTCPeerConnectionExtension() {} +}; -#endif // WEBRTC_PEER_CONNECTION_GDNATIVE_H +#endif // WEBRTC_PEER_CONNECTION_EXTENSION_H diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.cpp b/modules/webrtc/webrtc_peer_connection_gdnative.cpp deleted file mode 100644 index dcf78dfb73..0000000000 --- a/modules/webrtc/webrtc_peer_connection_gdnative.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/*************************************************************************/ -/* webrtc_peer_connection_gdnative.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifdef WEBRTC_GDNATIVE_ENABLED - -#include "webrtc_peer_connection_gdnative.h" - -#include "core/io/resource_loader.h" -#include "modules/gdnative/nativescript/nativescript.h" -#include "webrtc_data_channel_gdnative.h" - -const godot_net_webrtc_library *WebRTCPeerConnectionGDNative::default_library = nullptr; - -Error WebRTCPeerConnectionGDNative::set_default_library(const godot_net_webrtc_library *p_lib) { - if (default_library) { - const godot_net_webrtc_library *old = default_library; - default_library = nullptr; - old->unregistered(); - } - default_library = p_lib; - return OK; // Maybe add version check and fail accordingly -} - -WebRTCPeerConnection *WebRTCPeerConnectionGDNative::_create() { - WebRTCPeerConnectionGDNative *obj = memnew(WebRTCPeerConnectionGDNative); - ERR_FAIL_COND_V_MSG(!default_library, obj, "Default GDNative WebRTC implementation not defined."); - - // Call GDNative constructor - Error err = (Error)default_library->create_peer_connection(obj); - ERR_FAIL_COND_V_MSG(err != OK, obj, "GDNative default library constructor returned an error."); - - return obj; -} - -void WebRTCPeerConnectionGDNative::_bind_methods() { -} - -WebRTCPeerConnectionGDNative::WebRTCPeerConnectionGDNative() { - interface = nullptr; -} - -WebRTCPeerConnectionGDNative::~WebRTCPeerConnectionGDNative() { -} - -Error WebRTCPeerConnectionGDNative::initialize(Dictionary p_config) { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)interface->initialize(interface->data, (const godot_dictionary *)&p_config); -} - -Ref<WebRTCDataChannel> WebRTCPeerConnectionGDNative::create_data_channel(String p_label, Dictionary p_options) { - ERR_FAIL_COND_V(interface == nullptr, nullptr); - return (WebRTCDataChannel *)interface->create_data_channel(interface->data, p_label.utf8().get_data(), (const godot_dictionary *)&p_options); -} - -Error WebRTCPeerConnectionGDNative::create_offer() { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)interface->create_offer(interface->data); -} - -Error WebRTCPeerConnectionGDNative::set_local_description(String p_type, String p_sdp) { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)interface->set_local_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data()); -} - -Error WebRTCPeerConnectionGDNative::set_remote_description(String p_type, String p_sdp) { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)interface->set_remote_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data()); -} - -Error WebRTCPeerConnectionGDNative::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)interface->add_ice_candidate(interface->data, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data()); -} - -Error WebRTCPeerConnectionGDNative::poll() { - ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); - return (Error)interface->poll(interface->data); -} - -void WebRTCPeerConnectionGDNative::close() { - ERR_FAIL_COND(interface == nullptr); - interface->close(interface->data); -} - -WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionGDNative::get_connection_state() const { - ERR_FAIL_COND_V(interface == nullptr, STATE_DISCONNECTED); - return (ConnectionState)interface->get_connection_state(interface->data); -} - -void WebRTCPeerConnectionGDNative::set_native_webrtc_peer_connection(const godot_net_webrtc_peer_connection *p_impl) { - interface = p_impl; -} - -#endif // WEBRTC_GDNATIVE_ENABLED diff --git a/modules/webrtc/webrtc_peer_connection_js.h b/modules/webrtc/webrtc_peer_connection_js.h index 0272e67f6f..d2beccaf03 100644 --- a/modules/webrtc/webrtc_peer_connection_js.h +++ b/modules/webrtc/webrtc_peer_connection_js.h @@ -63,9 +63,6 @@ private: static void _on_error(void *p_obj); public: - static WebRTCPeerConnection *_create() { return memnew(WebRTCPeerConnectionJS); } - static void make_default() { WebRTCPeerConnection::_create = WebRTCPeerConnectionJS::_create; } - virtual ConnectionState get_connection_state() const; virtual Error initialize(Dictionary configuration = Dictionary()); diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml index ed8f3ba867..4b515c12a1 100644 --- a/modules/websocket/doc_classes/WebSocketClient.xml +++ b/modules/websocket/doc_classes/WebSocketClient.xml @@ -8,6 +8,7 @@ This client can be optionally used as a multiplayer peer for the [MultiplayerAPI]. After starting the client ([method connect_to_url]), you will need to [method MultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]). You will receive appropriate signals when connecting, disconnecting, or when new data is available. + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> </tutorials> diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml index bf35acbd11..8d8ab220e2 100644 --- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml +++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml @@ -5,6 +5,7 @@ </brief_description> <description> Base class for WebSocket server and client, allowing them to be used as multiplayer peer for the [MultiplayerAPI]. + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> </tutorials> @@ -30,10 +31,6 @@ </description> </method> </methods> - <members> - <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" /> - <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="TransferMode" default="2" /> - </members> <signals> <signal name="peer_packet"> <argument index="0" name="peer_source" type="int" /> diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml index 2bb705b384..f901b089ea 100644 --- a/modules/websocket/doc_classes/WebSocketServer.xml +++ b/modules/websocket/doc_classes/WebSocketServer.xml @@ -7,6 +7,7 @@ This class implements a WebSocket server that can also support the high-level multiplayer API. After starting the server ([method listen]), you will need to [method MultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]). When clients connect, disconnect, or send data, you will receive the appropriate signal. [b]Note:[/b] Not available in HTML5 exports. + [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> </tutorials> diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp index 7464cf2bf5..3a27855a5e 100644 --- a/modules/websocket/websocket_multiplayer_peer.cpp +++ b/modules/websocket/websocket_multiplayer_peer.cpp @@ -105,23 +105,6 @@ Error WebSocketMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer // // MultiplayerPeer // -void WebSocketMultiplayerPeer::set_transfer_channel(int p_channel) { - // Websocket does not have channels. -} - -int WebSocketMultiplayerPeer::get_transfer_channel() const { - return 0; -} - -void WebSocketMultiplayerPeer::set_transfer_mode(Multiplayer::TransferMode p_mode) { - // Websocket uses TCP, reliable -} - -Multiplayer::TransferMode WebSocketMultiplayerPeer::get_transfer_mode() const { - // Websocket uses TCP, reliable - return Multiplayer::TRANSFER_MODE_RELIABLE; -} - void WebSocketMultiplayerPeer::set_target_peer(int p_target_peer) { _target_peer = p_target_peer; } @@ -137,14 +120,6 @@ int WebSocketMultiplayerPeer::get_unique_id() const { return _peer_id; } -void WebSocketMultiplayerPeer::set_refuse_new_connections(bool p_enable) { - _refusing = p_enable; -} - -bool WebSocketMultiplayerPeer::is_refusing_new_connections() const { - return _refusing; -} - void WebSocketMultiplayerPeer::_send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id) { ERR_FAIL_COND(!p_peer.is_valid()); ERR_FAIL_COND(!p_peer->is_connected_to_host()); diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h index d97a599fe9..380edf67ed 100644 --- a/modules/websocket/websocket_multiplayer_peer.h +++ b/modules/websocket/websocket_multiplayer_peer.h @@ -68,7 +68,6 @@ protected: bool _is_multiplayer = false; int _target_peer = 0; int _peer_id = 0; - int _refusing = false; static void _bind_methods(); @@ -78,15 +77,9 @@ protected: public: /* MultiplayerPeer */ - void set_transfer_channel(int p_channel) override; - int get_transfer_channel() const override; - void set_transfer_mode(Multiplayer::TransferMode p_mode) override; - Multiplayer::TransferMode get_transfer_mode() const override; void set_target_peer(int p_target_peer) override; int get_packet_peer() const override; int get_unique_id() const override; - void set_refuse_new_connections(bool p_enable) override; - bool is_refusing_new_connections() const override; /* PacketPeer */ virtual int get_available_packet_count() const override; diff --git a/modules/webxr/register_types.cpp b/modules/webxr/register_types.cpp index 078a6547cf..16b483c39e 100644 --- a/modules/webxr/register_types.cpp +++ b/modules/webxr/register_types.cpp @@ -33,15 +33,34 @@ #include "webxr_interface.h" #include "webxr_interface_js.h" +#ifdef JAVASCRIPT_ENABLED +Ref<WebXRInterfaceJS> webxr; +#endif + void register_webxr_types() { GDREGISTER_VIRTUAL_CLASS(WebXRInterface); #ifdef JAVASCRIPT_ENABLED - Ref<WebXRInterfaceJS> webxr; webxr.instantiate(); XRServer::get_singleton()->add_interface(webxr); #endif } void unregister_webxr_types() { +#ifdef JAVASCRIPT_ENABLED + if (webxr.is_valid()) { + // uninitialise our interface if it is initialised + if (webxr->is_initialized()) { + webxr->uninitialize(); + } + + // unregister our interface from the XR server + if (XRServer::get_singleton()) { + XRServer::get_singleton()->remove_interface(webxr); + } + + // and release + webxr.unref(); + } +#endif } diff --git a/platform/javascript/.eslintrc.js b/platform/javascript/.eslintrc.js index 0ff9d67d26..2c81f1f02d 100644 --- a/platform/javascript/.eslintrc.js +++ b/platform/javascript/.eslintrc.js @@ -39,5 +39,13 @@ module.exports = { // Closure compiler (exported properties) "quote-props": ["error", "consistent"], "dot-notation": "off", + // No comma dangle for functions (it's madness, and ES2017) + "comma-dangle": ["error", { + "arrays": "always-multiline", + "objects": "always-multiline", + "imports": "always-multiline", + "exports": "always-multiline", + "functions": "never" + }], } }; diff --git a/platform/javascript/js/libs/library_godot_audio.js b/platform/javascript/js/libs/library_godot_audio.js index f6010fd12a..6cbb0567f4 100644 --- a/platform/javascript/js/libs/library_godot_audio.js +++ b/platform/javascript/js/libs/library_godot_audio.js @@ -229,7 +229,7 @@ const GodotAudioWorklet = { 'godot-processor', { 'outputChannelCount': [channels], - }, + } ); return Promise.resolve(); }); diff --git a/platform/osx/export/export_plugin.cpp b/platform/osx/export/export_plugin.cpp index 54a3104482..2404c20153 100644 --- a/platform/osx/export/export_plugin.cpp +++ b/platform/osx/export/export_plugin.cpp @@ -95,6 +95,7 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::ARRAY, "codesign/entitlements/app_sandbox/helper_executables", PROPERTY_HINT_ARRAY_TYPE, itos(Variant::STRING) + "/" + itos(PROPERTY_HINT_GLOBAL_FILE) + ":"), Array())); r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray())); @@ -535,6 +536,8 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p err = ERR_CANT_CREATE; } + Array helpers = p_preset->get("codesign/entitlements/app_sandbox/helper_executables"); + // Create our folder structure. if (err == OK) { print_line("Creating " + tmp_app_path_name + "/Contents/MacOS"); @@ -546,6 +549,11 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks"); } + if ((err == OK) && helpers.size() > 0) { + print_line("Creating " + tmp_app_path_name + "/Contents/Helpers"); + err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Helpers"); + } + if (err == OK) { print_line("Creating " + tmp_app_path_name + "/Contents/Resources"); err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Resources"); @@ -688,6 +696,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p bool sign_enabled = p_preset->get("codesign/enable"); String ent_path = p_preset->get("codesign/entitlements/custom_file"); + String hlp_ent_path = EditorPaths::get_singleton()->get_cache_dir().plus_file(pkg_name + "_helper.entitlements"); if (sign_enabled && (ent_path == "")) { ent_path = EditorPaths::get_singleton()->get_cache_dir().plus_file(pkg_name + ".entitlements"); @@ -819,10 +828,43 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p } else { err = ERR_CANT_CREATE; } + + if ((err == OK) && helpers.size() > 0) { + ent_f = FileAccess::open(hlp_ent_path, FileAccess::WRITE); + if (ent_f) { + ent_f->store_line("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + ent_f->store_line("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"); + ent_f->store_line("<plist version=\"1.0\">"); + ent_f->store_line("<dict>"); + ent_f->store_line("<key>com.apple.security.app-sandbox</key>"); + ent_f->store_line("<true/>"); + ent_f->store_line("<key>com.apple.security.inherit</key>"); + ent_f->store_line("<true/>"); + ent_f->store_line("</dict>"); + ent_f->store_line("</plist>"); + + ent_f->close(); + memdelete(ent_f); + } else { + err = ERR_CANT_CREATE; + } + } + } + + if ((err == OK) && helpers.size() > 0) { + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + for (int i = 0; i < helpers.size(); i++) { + String hlp_path = helpers[i]; + err = da->copy(hlp_path, tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file()); + if (err == OK && sign_enabled) { + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), hlp_ent_path); + } + FileAccess::set_unix_permissions(tmp_app_path_name + "/Contents/Helpers/" + hlp_path.get_file(), 0755); + } } if (err == OK) { - DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); for (int i = 0; i < shared_objects.size(); i++) { String src_path = ProjectSettings::get_singleton()->globalize_path(shared_objects[i].path); if (da->dir_exists(src_path)) { @@ -842,7 +884,6 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), ent_path); } } - memdelete(da); } if (sign_enabled) { @@ -903,6 +944,9 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p err = _notarize(p_preset, p_path); } + // Clean up temporary entitlements files. + DirAccess::remove_file_or_error(hlp_ent_path); + // Clean up temporary .app dir. tmp_app_dir->change_dir(tmp_app_path_name); tmp_app_dir->erase_contents_recursive(); @@ -916,7 +960,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) { String dir = p_root_path.plus_file(p_folder); - DirAccess *da = DirAccess::open(dir); + DirAccessRef da = DirAccess::open(dir); da->list_dir_begin(); String f; while ((f = da->get_next()) != "") { @@ -967,7 +1011,7 @@ void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String } else if (da->current_is_dir()) { _zip_folder_recursive(p_zip, p_root_path, p_folder.plus_file(f), p_pkg_name); } else { - bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name)); + bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name)) || p_folder.ends_with("Helpers"); OS::Time time = OS::get_singleton()->get_time(); OS::Date date = OS::get_singleton()->get_date(); @@ -1012,7 +1056,6 @@ void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String } } da->list_dir_end(); - memdelete(da); } bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { diff --git a/platform/uwp/SCsub b/platform/uwp/SCsub index 71c402358f..8726d32d61 100644 --- a/platform/uwp/SCsub +++ b/platform/uwp/SCsub @@ -7,7 +7,7 @@ files = [ "#platform/windows/windows_terminal_logger.cpp", "joypad_uwp.cpp", "context_egl_uwp.cpp", - "app.cpp", + "app_uwp.cpp", "os_uwp.cpp", ] diff --git a/platform/uwp/app.cpp b/platform/uwp/app_uwp.cpp index 1da17ffc5d..50e33e6c49 100644 --- a/platform/uwp/app.cpp +++ b/platform/uwp/app_uwp.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* app.cpp */ +/* app_uwp.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -32,7 +32,7 @@ // This file demonstrates how to initialize EGL in a Windows Store app, using ICoreWindow. // -#include "app.h" +#include "app_uwp.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" diff --git a/platform/uwp/app.h b/platform/uwp/app_uwp.h index 0b02527dae..8d4a0b90c3 100644 --- a/platform/uwp/app.h +++ b/platform/uwp/app_uwp.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* app.h */ +/* app_uwp.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,7 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#pragma once +#ifndef APP_UWP_H +#define APP_UWP_H #include <string> @@ -111,3 +112,4 @@ namespace GodotUWP } /* clang-format on */ +#endif // APP_UWP_H diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index b836497627..bf26ec1f20 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -155,7 +155,7 @@ void CPUParticles2D::_update_mesh_texture() { Vector<Vector2> vertices; vertices.push_back(-tex_size * 0.5); vertices.push_back(-tex_size * 0.5 + Vector2(tex_size.x, 0)); - vertices.push_back(-tex_size * 0.5 + Vector2(tex_size.x, tex_size.y)); + vertices.push_back(-tex_size * 0.5 + tex_size); vertices.push_back(-tex_size * 0.5 + Vector2(0, tex_size.y)); Vector<Vector2> uvs; AtlasTexture *atlas_texure = Object::cast_to<AtlasTexture>(*texture); @@ -727,7 +727,7 @@ void CPUParticles2D::_particles_process(double p_delta) { p.hue_rot_rand = Math::randf(); p.anim_offset_rand = Math::randf(); - real_t angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); + real_t angle1_rad = direction.angle() + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); Vector2 rot = Vector2(Math::cos(angle1_rad), Math::sin(angle1_rad)); p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], Math::randf()); diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index 72ea6541e3..cbf0d50c4e 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -464,7 +464,7 @@ void NavigationRegion2D::_notification(int p_what) { draw_line(a, b, doors_color); // Draw a circle to illustrate the margins. - real_t angle = (b - a).angle(); + real_t angle = b.angle_to_point(a); draw_arc(a, radius, angle + Math_PI / 2.0, angle - Math_PI / 2.0 + Math_TAU, 10, doors_color); draw_arc(b, radius, angle - Math_PI / 2.0, angle + Math_PI / 2.0, 10, doors_color); } diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp index c4b2608812..48817679bc 100644 --- a/scene/2d/physical_bone_2d.cpp +++ b/scene/2d/physical_bone_2d.cpp @@ -150,27 +150,15 @@ void PhysicalBone2D::_start_physics_simulation() { return; } - // Reset to Bone2D position + // Reset to Bone2D position. _position_at_bone2d(); - // Apply the layers and masks + // Apply the layers and masks. PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); - // Apply the correct mode - RigidDynamicBody2D::Mode rigid_mode = get_mode(); - if (rigid_mode == RigidDynamicBody2D::MODE_STATIC) { - set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); - } else if (rigid_mode == RigidDynamicBody2D::MODE_DYNAMIC) { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); - } else if (rigid_mode == RigidDynamicBody2D::MODE_KINEMATIC) { - set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); - } else if (rigid_mode == RigidDynamicBody2D::MODE_DYNAMIC_LOCKED) { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED); - } else { - // Default to Dynamic. - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); - } + // Apply the correct mode. + _apply_body_mode(); _internal_simulate_physics = true; set_physics_process_internal(true); diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index c3dc9ab92b..c07a999588 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -34,8 +34,8 @@ #include "scene/scene_string_names.h" void PhysicsBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "test_only", "safe_margin"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08)); - ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "collision", "safe_margin"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08)); + ClassDB::bind_method(D_METHOD("move_and_collide", "linear_velocity", "test_only", "safe_margin"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08)); + ClassDB::bind_method(D_METHOD("test_move", "from", "linear_velocity", "collision", "safe_margin"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08)); ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody2D::get_collision_exceptions); ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody2D::add_collision_exception_with); @@ -57,7 +57,10 @@ PhysicsBody2D::~PhysicsBody2D() { Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_test_only, real_t p_margin) { PhysicsServer2D::MotionResult result; - if (move_and_collide(p_motion, result, p_margin, p_test_only)) { + // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky. + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + + if (move_and_collide(p_motion * delta, result, p_margin, p_test_only)) { // Create a new instance when the cached reference is invalid or still in use in script. if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) { motion_cache.instantiate(); @@ -133,7 +136,10 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion r = const_cast<PhysicsServer2D::MotionResult *>(&r_collision->result); } - return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_margin, r); + // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky. + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + + return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion * delta, p_margin, r); } TypedArray<PhysicsBody2D> PhysicsBody2D::get_collision_exceptions() { @@ -430,7 +436,7 @@ void RigidDynamicBody2D::_body_state_changed_callback(void *p_instance, PhysicsD void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { set_block_transform_notify(true); // don't want notify (would feedback loop) - if (mode != MODE_KINEMATIC) { + if (!freeze || freeze_mode != FREEZE_MODE_KINEMATIC) { set_global_transform(p_state->get_transform()); } @@ -524,29 +530,60 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) } } -void RigidDynamicBody2D::set_mode(Mode p_mode) { - mode = p_mode; - switch (p_mode) { - case MODE_DYNAMIC: { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); - } break; - case MODE_STATIC: { - set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); +void RigidDynamicBody2D::_apply_body_mode() { + if (freeze) { + switch (freeze_mode) { + case FREEZE_MODE_STATIC: { + set_body_mode(PhysicsServer2D::BODY_MODE_STATIC); + } break; + case FREEZE_MODE_KINEMATIC: { + set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); + } break; + } + } else if (lock_rotation) { + set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LINEAR); + } else { + set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC); + } +} - } break; - case MODE_KINEMATIC: { - set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC); +void RigidDynamicBody2D::set_lock_rotation_enabled(bool p_lock_rotation) { + if (p_lock_rotation == lock_rotation) { + return; + } - } break; - case MODE_DYNAMIC_LOCKED: { - set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED); + lock_rotation = p_lock_rotation; + _apply_body_mode(); +} - } break; +bool RigidDynamicBody2D::is_lock_rotation_enabled() const { + return lock_rotation; +} + +void RigidDynamicBody2D::set_freeze_enabled(bool p_freeze) { + if (p_freeze == freeze) { + return; + } + + freeze = p_freeze; + _apply_body_mode(); +} + +bool RigidDynamicBody2D::is_freeze_enabled() const { + return freeze; +} + +void RigidDynamicBody2D::set_freeze_mode(FreezeMode p_freeze_mode) { + if (p_freeze_mode == freeze_mode) { + return; } + + freeze_mode = p_freeze_mode; + _apply_body_mode(); } -RigidDynamicBody2D::Mode RigidDynamicBody2D::get_mode() const { - return mode; +RigidDynamicBody2D::FreezeMode RigidDynamicBody2D::get_freeze_mode() const { + return freeze_mode; } void RigidDynamicBody2D::set_mass(real_t p_mass) { @@ -844,17 +881,14 @@ TypedArray<String> RigidDynamicBody2D::get_configuration_warnings() const { TypedArray<String> warnings = CollisionObject2D::get_configuration_warnings(); - if ((get_mode() == MODE_DYNAMIC || get_mode() == MODE_DYNAMIC_LOCKED) && (ABS(t.elements[0].length() - 1.0) > 0.05 || ABS(t.elements[1].length() - 1.0) > 0.05)) { - warnings.push_back(TTR("Size changes to RigidDynamicBody2D (in dynamic modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); + if (ABS(t.elements[0].length() - 1.0) > 0.05 || ABS(t.elements[1].length() - 1.0) > 0.05) { + warnings.push_back(TTR("Size changes to RigidDynamicBody2D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } return warnings; } void RigidDynamicBody2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_mode", "mode"), &RigidDynamicBody2D::set_mode); - ClassDB::bind_method(D_METHOD("get_mode"), &RigidDynamicBody2D::get_mode); - ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidDynamicBody2D::set_mass); ClassDB::bind_method(D_METHOD("get_mass"), &RigidDynamicBody2D::get_mass); @@ -918,11 +952,19 @@ void RigidDynamicBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidDynamicBody2D::set_can_sleep); ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidDynamicBody2D::is_able_to_sleep); + ClassDB::bind_method(D_METHOD("set_lock_rotation_enabled", "lock_rotation"), &RigidDynamicBody2D::set_lock_rotation_enabled); + ClassDB::bind_method(D_METHOD("is_lock_rotation_enabled"), &RigidDynamicBody2D::is_lock_rotation_enabled); + + ClassDB::bind_method(D_METHOD("set_freeze_enabled", "freeze_mode"), &RigidDynamicBody2D::set_freeze_enabled); + ClassDB::bind_method(D_METHOD("is_freeze_enabled"), &RigidDynamicBody2D::is_freeze_enabled); + + ClassDB::bind_method(D_METHOD("set_freeze_mode", "freeze_mode"), &RigidDynamicBody2D::set_freeze_mode); + ClassDB::bind_method(D_METHOD("get_freeze_mode"), &RigidDynamicBody2D::get_freeze_mode); + ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidDynamicBody2D::get_colliding_bodies); GDVIRTUAL_BIND(_integrate_forces, "state"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Dynamic,Static,DynamicLocked,Kinematic"), "set_mode", "get_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inertia", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater,exp"), "set_inertia", "get_inertia"); ADD_PROPERTY(PropertyInfo(Variant::INT, "center_of_mass_mode", PROPERTY_HINT_ENUM, "Auto,Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_center_of_mass_mode", "get_center_of_mass_mode"); @@ -936,6 +978,9 @@ void RigidDynamicBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "contact_monitor"), "set_contact_monitor", "is_contact_monitor_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sleeping"), "set_sleeping", "is_sleeping"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "lock_rotation"), "set_lock_rotation_enabled", "is_lock_rotation_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "freeze"), "set_freeze_enabled", "is_freeze_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "freeze_mode", PROPERTY_HINT_ENUM, "Static,Kinematic"), "set_freeze_mode", "get_freeze_mode"); ADD_GROUP("Linear", "linear_"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); @@ -952,10 +997,8 @@ void RigidDynamicBody2D::_bind_methods() { ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("sleeping_state_changed")); - BIND_ENUM_CONSTANT(MODE_DYNAMIC); - BIND_ENUM_CONSTANT(MODE_STATIC); - BIND_ENUM_CONSTANT(MODE_DYNAMIC_LOCKED); - BIND_ENUM_CONSTANT(MODE_KINEMATIC); + BIND_ENUM_CONSTANT(FREEZE_MODE_STATIC); + BIND_ENUM_CONSTANT(FREEZE_MODE_KINEMATIC); BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO); BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM); @@ -1090,9 +1133,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo if (on_floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) { Transform2D gt = get_global_transform(); - if (result.travel.length() > margin) { - gt.elements[2] -= result.travel.slide(up_direction); - } else { + if (result.travel.length() <= margin + CMP_EPSILON) { gt.elements[2] -= result.travel; } set_global_transform(gt); @@ -1111,7 +1152,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo // Avoid to move forward on a wall if floor_block_on_wall is true. if (p_was_on_floor && !on_floor && !vel_dir_facing_up) { // If the movement is large the body can be prevented from reaching the walls. - if (result.travel.length() <= margin) { + if (result.travel.length() <= margin + CMP_EPSILON) { // Cancels the motion. Transform2D gt = get_global_transform(); gt.elements[2] -= result.travel; @@ -1240,13 +1281,16 @@ void CharacterBody2D::_move_and_slide_free(double p_delta) { } void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) { - if (Math::is_equal_approx(floor_snap_length, 0) || on_floor || !was_on_floor || vel_dir_facing_up) { + if (on_floor || !was_on_floor || vel_dir_facing_up) { return; } + // Snap by at least collision margin to keep floor state consistent. + real_t length = MAX(floor_snap_length, margin); + Transform2D gt = get_global_transform(); PhysicsServer2D::MotionResult result; - if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false, true)) { + if (move_and_collide(-up_direction * length, result, margin, true, false, true)) { bool apply = true; if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { on_floor = true; @@ -1274,12 +1318,15 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) } bool CharacterBody2D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) { - if (Math::is_equal_approx(floor_snap_length, 0) || up_direction == Vector2() || on_floor || !was_on_floor || vel_dir_facing_up) { + if (up_direction == Vector2() || on_floor || !was_on_floor || vel_dir_facing_up) { return false; } + // Snap by at least collision margin to keep floor state consistent. + real_t length = MAX(floor_snap_length, margin); + PhysicsServer2D::MotionResult result; - if (move_and_collide(up_direction * -floor_snap_length, result, margin, true, false, true)) { + if (move_and_collide(-up_direction * length, result, margin, true, false, true)) { if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { return true; } @@ -1568,7 +1615,7 @@ void CharacterBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_constant_speed"), "set_floor_constant_speed_enabled", "is_floor_constant_speed_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall"), "set_floor_block_on_wall_enabled", "is_floor_block_on_wall_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,1000,0.1"), "set_floor_snap_length", "get_floor_snap_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_floor_snap_length", "get_floor_snap_length"); ADD_GROUP("Moving platform", "moving_platform"); ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers"); ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers"); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 5d0d98a2df..e789ac4cb7 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -92,7 +92,7 @@ class AnimatableBody2D : public StaticBody2D { GDCLASS(AnimatableBody2D, StaticBody2D); private: - bool sync_to_physics = false; + bool sync_to_physics = true; Transform2D last_valid_transform; @@ -117,11 +117,9 @@ class RigidDynamicBody2D : public PhysicsBody2D { GDCLASS(RigidDynamicBody2D, PhysicsBody2D); public: - enum Mode { - MODE_DYNAMIC, - MODE_STATIC, - MODE_DYNAMIC_LOCKED, - MODE_KINEMATIC, + enum FreezeMode { + FREEZE_MODE_STATIC, + FREEZE_MODE_KINEMATIC, }; enum CenterOfMassMode { @@ -137,7 +135,9 @@ public: private: bool can_sleep = true; - Mode mode = MODE_DYNAMIC; + bool lock_rotation = false; + bool freeze = false; + FreezeMode freeze_mode = FREEZE_MODE_STATIC; real_t mass = 1.0; real_t inertia = 0.0; @@ -211,9 +211,17 @@ protected: GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState2D *) + void _apply_body_mode(); + public: - void set_mode(Mode p_mode); - Mode get_mode() const; + void set_lock_rotation_enabled(bool p_lock_rotation); + bool is_lock_rotation_enabled() const; + + void set_freeze_enabled(bool p_freeze); + bool is_freeze_enabled() const; + + void set_freeze_mode(FreezeMode p_freeze_mode); + FreezeMode get_freeze_mode() const; void set_mass(real_t p_mass); real_t get_mass() const; @@ -290,7 +298,7 @@ private: void _reload_physics_characteristics(); }; -VARIANT_ENUM_CAST(RigidDynamicBody2D::Mode); +VARIANT_ENUM_CAST(RigidDynamicBody2D::FreezeMode); VARIANT_ENUM_CAST(RigidDynamicBody2D::CenterOfMassMode); VARIANT_ENUM_CAST(RigidDynamicBody2D::CCDMode); @@ -327,14 +335,14 @@ private: real_t margin = 0.08; MotionMode motion_mode = MOTION_MODE_GROUNDED; - bool floor_stop_on_slope = false; bool floor_constant_speed = false; + bool floor_stop_on_slope = true; bool floor_block_on_wall = true; bool slide_on_ceiling = true; int max_slides = 4; int platform_layer; real_t floor_max_angle = Math::deg2rad((real_t)45.0); - real_t floor_snap_length = 0; + real_t floor_snap_length = 1; real_t free_mode_min_slide_angle = Math::deg2rad((real_t)15.0); Vector2 up_direction = Vector2(0.0, -1.0); uint32_t moving_platform_floor_layers = UINT32_MAX; diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 4bbbc3575d..63a0fb9b89 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -456,7 +456,7 @@ void Bone2D::calculate_length_and_rotation() { if (child) { Vector2 child_local_pos = to_local(child->get_global_position()); length = child_local_pos.length(); - bone_angle = Math::atan2(child_local_pos.normalized().y, child_local_pos.normalized().x); + bone_angle = child_local_pos.normalized().angle(); calculated = true; break; } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index a139a92ab4..929233e4e0 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -891,7 +891,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List modulate.a *= 0.3; } } - draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, modulate); + draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, modulate); // --- Occluders --- for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) { @@ -1008,15 +1008,19 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) { } } -void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation) { +void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation) { ERR_FAIL_COND(!p_tile_set.is_valid()); ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id)); ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords)); ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_alternative_tile(p_atlas_coords, p_alternative_tile)); - TileSetSource *source = *p_tile_set->get_source(p_atlas_source_id); TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { + // Check for the frame. + if (p_frame >= 0) { + ERR_FAIL_INDEX(p_frame, atlas_source->get_tile_animation_frames_count(p_atlas_coords)); + } + // Get the texture. Ref<Texture2D> tex = atlas_source->get_texture(); if (!tex.is_valid()) { @@ -1032,13 +1036,15 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe // Get tile data. TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile)); - // Compute the offset - Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords); + // Get the tile modulation. + Color modulate = tile_data->get_modulate() * p_modulation; + + // Compute the offset. Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(p_atlas_coords, p_alternative_tile); - // Compute the destination rectangle in the CanvasItem. + // Get destination rect. Rect2 dest_rect; - dest_rect.size = source_rect.size; + dest_rect.size = atlas_source->get_tile_texture_region(p_atlas_coords).size; dest_rect.size.x += FP_ADJUST; dest_rect.size.y += FP_ADJUST; @@ -1057,12 +1063,28 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe dest_rect.size.y = -dest_rect.size.y; } - // Get the tile modulation. - Color modulate = tile_data->get_modulate(); - modulate = Color(modulate.r * p_modulation.r, modulate.g * p_modulation.g, modulate.b * p_modulation.b, modulate.a * p_modulation.a); - // Draw the tile. - tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + if (p_frame >= 0) { + Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords, p_frame); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + } else if (atlas_source->get_tile_animation_frames_count(p_atlas_coords) == 1) { + Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords, 0); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + } else { + real_t speed = atlas_source->get_tile_animation_speed(p_atlas_coords); + real_t animation_duration = atlas_source->get_tile_animation_total_duration(p_atlas_coords) / speed; + real_t time = 0.0; + for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(p_atlas_coords); frame++) { + real_t frame_duration = atlas_source->get_tile_animation_frame_duration(p_atlas_coords, frame) / speed; + RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, animation_duration, time, time + frame_duration, 0.0); + + Rect2i source_rect = atlas_source->get_tile_texture_region(p_atlas_coords, frame); + tex->draw_rect_region(p_canvas_item, dest_rect, source_rect, modulate, transpose, p_tile_set->is_uv_clipping()); + + time += frame_duration; + } + RenderingServer::get_singleton()->canvas_item_add_animation_slice(p_canvas_item, 1.0, 0.0, 1.0, 0.0); + } } } @@ -2012,10 +2034,22 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) { return false; } else if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) { int index = components[0].trim_prefix("layer_").to_int(); - if (index < 0 || index >= (int)layers.size()) { + if (index < 0) { return false; } + if (index >= (int)layers.size()) { + _clear_internals(); + while (index >= (int)layers.size()) { + layers.push_back(TileMapLayer()); + } + _recreate_internals(); + + notify_property_list_changed(); + emit_signal(SNAME("changed")); + update_configuration_warnings(); + } + if (components[1] == "name") { set_layer_name(index, p_value); return true; diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 3ac50fc7cc..ca38baa550 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -305,7 +305,7 @@ public: void set_quadrant_size(int p_size); int get_quadrant_size() const; - static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0)); + static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0)); // Layers management. int get_layers_count() const; diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index c34c150145..afd11482e3 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -110,7 +110,7 @@ TypedArray<String> BoneAttachment3D::get_configuration_warnings() const { } else { Skeleton3D *parent = Object::cast_to<Skeleton3D>(get_parent()); if (!parent) { - warnings.append(TTR("Parent node is not a Skeleton3D node! Please use an extenral Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node.")); + warnings.append(TTR("Parent node is not a Skeleton3D node! Please use an external Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node.")); } } diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index c79f956642..4e496fba47 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -124,8 +124,7 @@ TypedArray<String> CollisionShape3D::get_configuration_warnings() const { if (shape.is_valid() && Object::cast_to<RigidDynamicBody3D>(get_parent()) && - Object::cast_to<ConcavePolygonShape3D>(*shape) && - Object::cast_to<RigidDynamicBody3D>(get_parent())->get_mode() != RigidDynamicBody3D::MODE_STATIC) { + Object::cast_to<ConcavePolygonShape3D>(*shape)) { warnings.push_back(TTR("ConcavePolygonShape3D doesn't support RigidDynamicBody3D in another mode than static.")); } diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index baf28ae102..32a62d8c7e 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -469,7 +469,7 @@ void GPUParticles3D::_skinning_changed() { if (draw_pass.is_valid() && draw_pass->get_builtin_bind_pose_count() > 0) { xforms.resize(draw_pass->get_builtin_bind_pose_count()); for (int j = 0; j < draw_pass->get_builtin_bind_pose_count(); j++) { - xforms.write[i] = draw_pass->get_builtin_bind_pose(j); + xforms.write[j] = draw_pass->get_builtin_bind_pose(j); } break; } diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 48da186860..bbf2c81a92 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -38,8 +38,8 @@ #endif void PhysicsBody3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "test_only", "safe_margin", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "collision", "safe_margin", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("move_and_collide", "linear_velocity", "test_only", "safe_margin", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(1)); + ClassDB::bind_method(D_METHOD("test_move", "from", "linear_velocity", "collision", "safe_margin", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(1)); ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &PhysicsBody3D::set_axis_lock); ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &PhysicsBody3D::get_axis_lock); @@ -97,7 +97,10 @@ void PhysicsBody3D::remove_collision_exception_with(Node *p_node) { Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_test_only, real_t p_margin, int p_max_collisions) { PhysicsServer3D::MotionResult result; - if (move_and_collide(p_motion, result, p_margin, p_test_only, p_max_collisions)) { + // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + + if (move_and_collide(p_motion * delta, result, p_margin, p_test_only, p_max_collisions)) { // Create a new instance when the cached reference is invalid or still in use in script. if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) { motion_cache.instantiate(); @@ -177,7 +180,10 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion r = const_cast<PhysicsServer3D::MotionResult *>(&r_collision->result); } - return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_margin, r, p_max_collisions); + // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + + return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion * delta, p_margin, r, p_max_collisions); } void PhysicsBody3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock) { @@ -599,27 +605,60 @@ void RigidDynamicBody3D::_notification(int p_what) { #endif } -void RigidDynamicBody3D::set_mode(Mode p_mode) { - mode = p_mode; - switch (p_mode) { - case MODE_DYNAMIC: { - set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC); - } break; - case MODE_STATIC: { - set_body_mode(PhysicsServer3D::BODY_MODE_STATIC); - } break; - case MODE_DYNAMIC_LOCKED: { - set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC_LOCKED); - } break; - case MODE_KINEMATIC: { - set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC); - } break; +void RigidDynamicBody3D::_apply_body_mode() { + if (freeze) { + switch (freeze_mode) { + case FREEZE_MODE_STATIC: { + set_body_mode(PhysicsServer3D::BODY_MODE_STATIC); + } break; + case FREEZE_MODE_KINEMATIC: { + set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC); + } break; + } + } else if (lock_rotation) { + set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC_LINEAR); + } else { + set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC); + } +} + +void RigidDynamicBody3D::set_lock_rotation_enabled(bool p_lock_rotation) { + if (p_lock_rotation == lock_rotation) { + return; } - update_configuration_warnings(); + + lock_rotation = p_lock_rotation; + _apply_body_mode(); } -RigidDynamicBody3D::Mode RigidDynamicBody3D::get_mode() const { - return mode; +bool RigidDynamicBody3D::is_lock_rotation_enabled() const { + return lock_rotation; +} + +void RigidDynamicBody3D::set_freeze_enabled(bool p_freeze) { + if (p_freeze == freeze) { + return; + } + + freeze = p_freeze; + _apply_body_mode(); +} + +bool RigidDynamicBody3D::is_freeze_enabled() const { + return freeze; +} + +void RigidDynamicBody3D::set_freeze_mode(FreezeMode p_freeze_mode) { + if (p_freeze_mode == freeze_mode) { + return; + } + + freeze_mode = p_freeze_mode; + _apply_body_mode(); +} + +RigidDynamicBody3D::FreezeMode RigidDynamicBody3D::get_freeze_mode() const { + return freeze_mode; } void RigidDynamicBody3D::set_mass(real_t p_mass) { @@ -892,17 +931,14 @@ TypedArray<String> RigidDynamicBody3D::get_configuration_warnings() const { TypedArray<String> warnings = Node::get_configuration_warnings(); - if ((get_mode() == MODE_DYNAMIC || get_mode() == MODE_DYNAMIC_LOCKED) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) { - warnings.push_back(TTR("Size changes to RigidDynamicBody (in dynamic modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); + if (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05) { + warnings.push_back(TTR("Size changes to RigidDynamicBody will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } return warnings; } void RigidDynamicBody3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_mode", "mode"), &RigidDynamicBody3D::set_mode); - ClassDB::bind_method(D_METHOD("get_mode"), &RigidDynamicBody3D::get_mode); - ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidDynamicBody3D::set_mass); ClassDB::bind_method(D_METHOD("get_mass"), &RigidDynamicBody3D::get_mass); @@ -963,11 +999,19 @@ void RigidDynamicBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidDynamicBody3D::set_can_sleep); ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidDynamicBody3D::is_able_to_sleep); + ClassDB::bind_method(D_METHOD("set_lock_rotation_enabled", "lock_rotation"), &RigidDynamicBody3D::set_lock_rotation_enabled); + ClassDB::bind_method(D_METHOD("is_lock_rotation_enabled"), &RigidDynamicBody3D::is_lock_rotation_enabled); + + ClassDB::bind_method(D_METHOD("set_freeze_enabled", "freeze_mode"), &RigidDynamicBody3D::set_freeze_enabled); + ClassDB::bind_method(D_METHOD("is_freeze_enabled"), &RigidDynamicBody3D::is_freeze_enabled); + + ClassDB::bind_method(D_METHOD("set_freeze_mode", "freeze_mode"), &RigidDynamicBody3D::set_freeze_mode); + ClassDB::bind_method(D_METHOD("get_freeze_mode"), &RigidDynamicBody3D::get_freeze_mode); + ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidDynamicBody3D::get_colliding_bodies); GDVIRTUAL_BIND(_integrate_forces, "state"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Dynamic,Static,DynamicLocked,Kinematic"), "set_mode", "get_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "inertia", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater,exp"), "set_inertia", "get_inertia"); ADD_PROPERTY(PropertyInfo(Variant::INT, "center_of_mass_mode", PROPERTY_HINT_ENUM, "Auto,Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_center_of_mass_mode", "get_center_of_mass_mode"); @@ -981,6 +1025,9 @@ void RigidDynamicBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "contact_monitor"), "set_contact_monitor", "is_contact_monitor_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sleeping"), "set_sleeping", "is_sleeping"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "lock_rotation"), "set_lock_rotation_enabled", "is_lock_rotation_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "freeze"), "set_freeze_enabled", "is_freeze_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "freeze_mode", PROPERTY_HINT_ENUM, "Static,Kinematic"), "set_freeze_mode", "get_freeze_mode"); ADD_GROUP("Linear", "linear_"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); @@ -994,10 +1041,8 @@ void RigidDynamicBody3D::_bind_methods() { ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("sleeping_state_changed")); - BIND_ENUM_CONSTANT(MODE_DYNAMIC); - BIND_ENUM_CONSTANT(MODE_STATIC); - BIND_ENUM_CONSTANT(MODE_DYNAMIC_LOCKED); - BIND_ENUM_CONSTANT(MODE_KINEMATIC); + BIND_ENUM_CONSTANT(FREEZE_MODE_STATIC); + BIND_ENUM_CONSTANT(FREEZE_MODE_KINEMATIC); BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO); BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM); @@ -1146,8 +1191,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo if (collision_state.floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) { Transform3D gt = get_global_transform(); - real_t travel_total = result.travel.length(); - if (travel_total <= margin + CMP_EPSILON) { + if (result.travel.length() <= margin + CMP_EPSILON) { gt.origin -= result.travel; } set_global_transform(gt); @@ -1186,7 +1230,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo Transform3D gt = get_global_transform(); real_t travel_total = result.travel.length(); real_t cancel_dist_max = MIN(0.1, margin * 20); - if (travel_total < margin + CMP_EPSILON) { + if (travel_total <= margin + CMP_EPSILON) { gt.origin -= result.travel; } else if (travel_total < cancel_dist_max) { // If the movement is large the body can be prevented from reaching the walls. gt.origin -= result.travel.slide(up_direction); @@ -1377,7 +1421,9 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) return; } + // Snap by at least collision margin to keep floor state consistent. real_t length = MAX(floor_snap_length, margin); + Transform3D gt = get_global_transform(); PhysicsServer3D::MotionResult result; if (move_and_collide(-up_direction * length, result, margin, true, 4, false, true)) { @@ -1403,12 +1449,15 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) } bool CharacterBody3D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) { - if (Math::is_zero_approx(floor_snap_length) || up_direction == Vector3() || collision_state.floor || !was_on_floor || vel_dir_facing_up) { + if (up_direction == Vector3() || collision_state.floor || !was_on_floor || vel_dir_facing_up) { return false; } + // Snap by at least collision margin to keep floor state consistent. + real_t length = MAX(floor_snap_length, margin); + PhysicsServer3D::MotionResult result; - if (move_and_collide(-up_direction * floor_snap_length, result, margin, true, 4, false, true)) { + if (move_and_collide(-up_direction * length, result, margin, true, 4, false, true)) { CollisionState result_state; // Don't apply direction for any type. _set_collision_direction(result, result_state, CollisionState()); diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 96f3d7d747..a53147cb8f 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -105,7 +105,7 @@ private: Vector3 linear_velocity; Vector3 angular_velocity; - bool sync_to_physics = false; + bool sync_to_physics = true; Transform3D last_valid_transform; @@ -133,11 +133,9 @@ class RigidDynamicBody3D : public PhysicsBody3D { GDCLASS(RigidDynamicBody3D, PhysicsBody3D); public: - enum Mode { - MODE_DYNAMIC, - MODE_STATIC, - MODE_DYNAMIC_LOCKED, - MODE_KINEMATIC, + enum FreezeMode { + FREEZE_MODE_STATIC, + FREEZE_MODE_KINEMATIC, }; enum CenterOfMassMode { @@ -145,11 +143,11 @@ public: CENTER_OF_MASS_MODE_CUSTOM, }; - GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState3D *) - -protected: +private: bool can_sleep = true; - Mode mode = MODE_DYNAMIC; + bool lock_rotation = false; + bool freeze = false; + FreezeMode freeze_mode = FREEZE_MODE_STATIC; real_t mass = 1.0; Vector3 inertia; @@ -214,16 +212,28 @@ protected: void _body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape); static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state); - virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state); +protected: void _notification(int p_what); static void _bind_methods(); virtual void _validate_property(PropertyInfo &property) const override; + GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState3D *) + + virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state); + + void _apply_body_mode(); + public: - void set_mode(Mode p_mode); - Mode get_mode() const; + void set_lock_rotation_enabled(bool p_lock_rotation); + bool is_lock_rotation_enabled() const; + + void set_freeze_enabled(bool p_freeze); + bool is_freeze_enabled() const; + + void set_freeze_mode(FreezeMode p_freeze_mode); + FreezeMode get_freeze_mode() const; void set_mass(real_t p_mass); real_t get_mass() const; @@ -298,7 +308,7 @@ private: void _reload_physics_characteristics(); }; -VARIANT_ENUM_CAST(RigidDynamicBody3D::Mode); +VARIANT_ENUM_CAST(RigidDynamicBody3D::FreezeMode); VARIANT_ENUM_CAST(RigidDynamicBody3D::CenterOfMassMode); class KinematicCollision3D; @@ -364,8 +374,8 @@ private: }; CollisionState collision_state; - bool floor_stop_on_slope = false; bool floor_constant_speed = false; + bool floor_stop_on_slope = true; bool floor_block_on_wall = true; bool slide_on_ceiling = true; int max_slides = 6; diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index 466f67afb8..8d90aabfac 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -99,7 +99,7 @@ bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain child_ci->current_pos = child_ci->initial_transform.origin; if (child_ci->parent_item) { - child_ci->length = (child_ci->current_pos - child_ci->parent_item->current_pos).length(); + child_ci->length = child_ci->parent_item->current_pos.distance_to(child_ci->current_pos); } } @@ -140,7 +140,7 @@ void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet, Vec solve_simple_backwards(p_task->chain, p_solve_magnet); solve_simple_forwards(p_task->chain, p_solve_magnet, p_origin_pos); - distance_to_goal = (p_task->chain.tips[0].chain_item->current_pos - p_task->chain.tips[0].end_effector->goal_transform.origin).length(); + distance_to_goal = p_task->chain.tips[0].end_effector->goal_transform.origin.distance_to(p_task->chain.tips[0].chain_item->current_pos); } } diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index bc3bb81ed4..9a2aaa8be2 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -470,7 +470,7 @@ real_t VehicleBody3D::_ray_cast(int p_idx, PhysicsDirectBodyState3D *s) { } void VehicleBody3D::_update_suspension(PhysicsDirectBodyState3D *s) { - real_t chassisMass = mass; + real_t chassisMass = get_mass(); for (int w_it = 0; w_it < wheels.size(); w_it++) { VehicleWheel3D &wheel_info = *wheels[w_it]; @@ -558,7 +558,7 @@ void VehicleBody3D::_resolve_single_bilateral(PhysicsDirectBodyState3D *s, const rel_pos2, normal, s->get_inverse_inertia_tensor().get_main_diagonal(), - 1.0 / mass, + 1.0 / get_mass(), b2invinertia, b2invmass); @@ -584,7 +584,7 @@ void VehicleBody3D::_resolve_single_bilateral(PhysicsDirectBodyState3D *s, const #define ONLY_USE_LINEAR_MASS #ifdef ONLY_USE_LINEAR_MASS - real_t massTerm = real_t(1.) / ((1.0 / mass) + b2invmass); + real_t massTerm = real_t(1.) / ((1.0 / get_mass()) + b2invmass); impulse = -contactDamping * rel_vel * massTerm; #else real_t velocityImpulse = -contactDamping * rel_vel * jacDiagABInv; diff --git a/scene/animation/SCsub b/scene/animation/SCsub index cc33a5af84..d0aa0bc8aa 100644 --- a/scene/animation/SCsub +++ b/scene/animation/SCsub @@ -6,11 +6,8 @@ Import("env") thirdparty_obj = [] -thirdparty_sources = "#thirdparty/misc/easing_equations.cpp" - env_thirdparty = env.Clone() env_thirdparty.disable_warnings() -env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) env.scene_sources += thirdparty_obj # Godot source files diff --git a/scene/animation/easing_equations.h b/scene/animation/easing_equations.h new file mode 100644 index 0000000000..c38d083b7f --- /dev/null +++ b/scene/animation/easing_equations.h @@ -0,0 +1,405 @@ +/*************************************************************************/ +/* easing_equations.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/* + * Derived from Robert Penner's easing equations: http://robertpenner.com/easing/ + * + * Copyright (c) 2001 Robert Penner + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef EASING_EQUATIONS_H +#define EASING_EQUATIONS_H + +namespace linear { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + return c * t / d + b; +} +}; // namespace linear + +namespace sine { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + return -c * cos(t / d * (Math_PI / 2)) + c + b; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + return c * sin(t / d * (Math_PI / 2)) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + return -c / 2 * (cos(Math_PI * t / d) - 1) + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace sine + +namespace quint { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + return c * pow(t / d, 5) + b; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + return c * (pow(t / d - 1, 5) + 1) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + t = t / d * 2; + + if (t < 1) { + return c / 2 * pow(t, 5) + b; + } + return c / 2 * (pow(t - 2, 5) + 2) + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace quint + +namespace quart { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + return c * pow(t / d, 4) + b; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + return -c * (pow(t / d - 1, 4) - 1) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + t = t / d * 2; + + if (t < 1) { + return c / 2 * pow(t, 4) + b; + } + return -c / 2 * (pow(t - 2, 4) - 2) + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace quart + +namespace quad { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + return c * pow(t / d, 2) + b; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + t /= d; + return -c * t * (t - 2) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + t = t / d * 2; + + if (t < 1) { + return c / 2 * pow(t, 2) + b; + } + return -c / 2 * ((t - 1) * (t - 3) - 1) + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace quad + +namespace expo { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + if (t == 0) { + return b; + } + return c * pow(2, 10 * (t / d - 1)) + b - c * 0.001; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + if (t == d) { + return b + c; + } + return c * 1.001 * (-pow(2, -10 * t / d) + 1) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + if (t == 0) { + return b; + } + + if (t == d) { + return b + c; + } + + t = t / d * 2; + + if (t < 1) { + return c / 2 * pow(2, 10 * (t - 1)) + b - c * 0.0005; + } + return c / 2 * 1.0005 * (-pow(2, -10 * (t - 1)) + 2) + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace expo + +namespace elastic { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + if (t == 0) { + return b; + } + + t /= d; + if (t == 1) { + return b + c; + } + + t -= 1; + float p = d * 0.3f; + float a = c * pow(2, 10 * t); + float s = p / 4; + + return -(a * sin((t * d - s) * (2 * Math_PI) / p)) + b; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + if (t == 0) { + return b; + } + + t /= d; + if (t == 1) { + return b + c; + } + + float p = d * 0.3f; + float s = p / 4; + + return (c * pow(2, -10 * t) * sin((t * d - s) * (2 * Math_PI) / p) + c + b); +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + if (t == 0) { + return b; + } + + if ((t /= d / 2) == 2) { + return b + c; + } + + float p = d * (0.3f * 1.5f); + float a = c; + float s = p / 4; + + if (t < 1) { + t -= 1; + a *= pow(2, 10 * t); + return -0.5f * (a * sin((t * d - s) * (2 * Math_PI) / p)) + b; + } + + t -= 1; + a *= pow(2, -10 * t); + return a * sin((t * d - s) * (2 * Math_PI) / p) * 0.5f + c + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace elastic + +namespace cubic { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + t /= d; + return c * t * t * t + b; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + t = t / d - 1; + return c * (t * t * t + 1) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + t /= d / 2; + if (t < 1) { + return c / 2 * t * t * t + b; + } + + t -= 2; + return c / 2 * (t * t * t + 2) + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace cubic + +namespace circ { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + t /= d; + return -c * (sqrt(1 - t * t) - 1) + b; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + t = t / d - 1; + return c * sqrt(1 - t * t) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + t /= d / 2; + if (t < 1) { + return -c / 2 * (sqrt(1 - t * t) - 1) + b; + } + + t -= 2; + return c / 2 * (sqrt(1 - t * t) + 1) + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace circ + +namespace bounce { +static real_t out(real_t t, real_t b, real_t c, real_t d) { + t /= d; + + if (t < (1 / 2.75f)) { + return c * (7.5625f * t * t) + b; + } + + if (t < (2 / 2.75f)) { + t -= 1.5f / 2.75f; + return c * (7.5625f * t * t + 0.75f) + b; + } + + if (t < (2.5 / 2.75)) { + t -= 2.25f / 2.75f; + return c * (7.5625f * t * t + 0.9375f) + b; + } + + t -= 2.625f / 2.75f; + return c * (7.5625f * t * t + 0.984375f) + b; +} + +static real_t in(real_t t, real_t b, real_t c, real_t d) { + return c - out(d - t, 0, c, d) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return in(t * 2, b, c / 2, d); + } + return out(t * 2 - d, b + c / 2, c / 2, d); +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace bounce + +namespace back { +static real_t in(real_t t, real_t b, real_t c, real_t d) { + float s = 1.70158f; + t /= d; + + return c * t * t * ((s + 1) * t - s) + b; +} + +static real_t out(real_t t, real_t b, real_t c, real_t d) { + float s = 1.70158f; + t = t / d - 1; + + return c * (t * t * ((s + 1) * t + s) + 1) + b; +} + +static real_t in_out(real_t t, real_t b, real_t c, real_t d) { + float s = 1.70158f * 1.525f; + t /= d / 2; + + if (t < 1) { + return c / 2 * (t * t * ((s + 1) * t - s)) + b; + } + + t -= 2; + return c / 2 * (t * t * ((s + 1) * t + s) + 2) + b; +} + +static real_t out_in(real_t t, real_t b, real_t c, real_t d) { + if (t < d / 2) { + return out(t * 2, b, c / 2, d); + } + return in(t * 2 - d, b + c / 2, c / 2, d); +} +}; // namespace back + +#endif diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 2847031375..c43b83747b 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -30,8 +30,23 @@ #include "tween.h" +#include "scene/animation/easing_equations.h" #include "scene/main/node.h" +Tween::interpolater Tween::interpolaters[Tween::TRANS_MAX][Tween::EASE_MAX] = { + { &linear::in, &linear::in, &linear::in, &linear::in }, // Linear is the same for each easing. + { &sine::in, &sine::out, &sine::in_out, &sine::out_in }, + { &quint::in, &quint::out, &quint::in_out, &quint::out_in }, + { &quart::in, &quart::out, &quart::in_out, &quart::out_in }, + { &quad::in, &quad::out, &quad::in_out, &quad::out_in }, + { &expo::in, &expo::out, &expo::in_out, &expo::out_in }, + { &elastic::in, &elastic::out, &elastic::in_out, &elastic::out_in }, + { &cubic::in, &cubic::out, &cubic::in_out, &cubic::out_in }, + { &circ::in, &circ::out, &circ::in_out, &circ::out_in }, + { &bounce::in, &bounce::out, &bounce::in_out, &bounce::out_in }, + { &back::in, &back::out, &back::in_out, &back::out_in }, +}; + void Tweener::set_tween(Ref<Tween> p_tween) { tween = p_tween; } @@ -317,6 +332,16 @@ bool Tween::should_pause() { return pause_mode != TWEEN_PAUSE_PROCESS; } +real_t Tween::run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t p_time, real_t p_initial, real_t p_delta, real_t p_duration) { + if (p_duration == 0) { + // Special case to avoid dividing by 0 in equations. + return p_initial + p_delta; + } + + interpolater func = interpolaters[p_trans_type][p_ease_type]; + return func(p_time, p_initial, p_delta, p_duration); +} + Variant Tween::interpolate_variant(Variant p_initial_val, Variant p_delta_val, float p_time, float p_duration, TransitionType p_trans, EaseType p_ease) { ERR_FAIL_INDEX_V(p_trans, TransitionType::TRANS_MAX, Variant()); ERR_FAIL_INDEX_V(p_ease, EaseType::EASE_MAX, Variant()); diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 4dfd6902e6..c7d1d7ef82 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -227,17 +227,17 @@ void CodeEdit::_notification(int p_what) { end = font->get_string_size(line.substr(0, line.rfind(String::chr(0xFFFF))), font_size).x; } - Point2 round_ofs = hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent() + font_height * i + yofs); + Point2 round_ofs = hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent(font_size) + font_height * i + yofs); round_ofs = round_ofs.round(); draw_string(font, round_ofs, line.replace(String::chr(0xFFFF), ""), HALIGN_LEFT, -1, font_size, font_color); if (end > 0) { // Draw an underline for the currently edited function parameter. - const Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font_height + font_height * i + line_spacing); + const Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font_height + font_height * i + yofs); draw_line(b, b + Vector2(end - begin, 0), font_color, 2); // Draw a translucent text highlight as well. const Rect2 highlight_rect = Rect2( - hint_ofs + sb->get_offset() + Vector2(begin, 0), + b - Vector2(0, font_height), Vector2(end - begin, font_height)); draw_rect(highlight_rect, font_color * Color(1, 1, 1, 0.2)); } @@ -296,7 +296,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { mpos.x = get_size().x - mpos.x; } - Point2i pos = get_line_column_at_pos(Point2i(mpos.x, mpos.y)); + Point2i pos = get_line_column_at_pos(mpos); int line = pos.y; int col = pos.x; @@ -321,7 +321,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { mpos.x = get_size().x - mpos.x; } - Point2i pos = get_line_column_at_pos(Point2i(mpos.x, mpos.y)); + Point2i pos = get_line_column_at_pos(mpos); int line = pos.y; int col = pos.x; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index efb6b7d200..611035fff9 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -827,7 +827,7 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { real_t dist = center.distance_to(bev->get_position()); if (dist <= center.x) { - real_t rad = Math::atan2(bev->get_position().y - center.y, bev->get_position().x - center.x); + real_t rad = bev->get_position().angle_to_point(center); h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU; s = CLAMP(dist / center.x, 0, 1); } else { @@ -844,7 +844,7 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { real_t dist = center.distance_to(bev->get_position()); if (dist >= center.x * 0.84 && dist <= center.x) { - real_t rad = Math::atan2(bev->get_position().y - center.y, bev->get_position().x - center.x); + real_t rad = bev->get_position().angle_to_point(center); h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU; spinning = true; } else { @@ -889,12 +889,12 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { Vector2 center = c->get_size() / 2.0; if (picker_type == SHAPE_VHS_CIRCLE) { real_t dist = center.distance_to(mev->get_position()); - real_t rad = Math::atan2(mev->get_position().y - center.y, mev->get_position().x - center.x); + real_t rad = mev->get_position().angle_to_point(center); h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU; s = CLAMP(dist / center.x, 0, 1); } else { if (spinning) { - real_t rad = Math::atan2(mev->get_position().y - center.y, mev->get_position().x - center.x); + real_t rad = mev->get_position().angle_to_point(center); h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU; } else { real_t corner_x = (c == wheel_uv) ? center.x - Math_SQRT12 * c->get_size().width * 0.42 : 0; @@ -996,7 +996,7 @@ void ColorPicker::_screen_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> mev = p_event; if (mev.is_valid()) { Viewport *r = get_tree()->get_root(); - if (!r->get_visible_rect().has_point(Point2(mev->get_global_position().x, mev->get_global_position().y))) { + if (!r->get_visible_rect().has_point(mev->get_global_position())) { return; } diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 071b7f5bb6..d9c08ec272 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -697,7 +697,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { } else if (!just_disconnected) { String from = connecting_from; int from_slot = connecting_index; - Vector2 ofs = Vector2(mb->get_position().x, mb->get_position().y); + Vector2 ofs = mb->get_position(); if (!connecting_out) { emit_signal(SNAME("connection_from_empty"), from, from_slot, ofs); @@ -1071,10 +1071,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { if (mm.is_valid() && box_selecting) { box_selecting_to = mm->get_position(); - box_selecting_rect = Rect2(MIN(box_selecting_from.x, box_selecting_to.x), - MIN(box_selecting_from.y, box_selecting_to.y), - ABS(box_selecting_from.x - box_selecting_to.x), - ABS(box_selecting_from.y - box_selecting_to.y)); + box_selecting_rect = Rect2(box_selecting_from.min(box_selecting_to), (box_selecting_from - box_selecting_to).abs()); for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 08c8c60d7a..ecf735d7f5 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -31,6 +31,9 @@ #include "graph_node.h" #include "core/string/translation.h" +#ifdef TOOLS_ENABLED +#include "graph_edit.h" +#endif struct _MinSizeCache { int min_size; @@ -458,6 +461,27 @@ void GraphNode::_shape() { title_buf->add_string(title, font, font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale()); } +#ifdef TOOLS_ENABLED +void GraphNode::_edit_set_position(const Point2 &p_position) { + GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent()); + if (graph) { + Point2 offset = (p_position + graph->get_scroll_ofs()) * graph->get_zoom(); + set_position_offset(offset); + } + set_position(p_position); +} + +void GraphNode::_validate_property(PropertyInfo &property) const { + Control::_validate_property(property); + GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent()); + if (graph) { + if (property.name == "rect_position") { + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + } +} +#endif + void GraphNode::set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left, const Ref<Texture2D> &p_custom_right) { ERR_FAIL_COND_MSG(p_idx < 0, vformat("Cannot set slot with p_idx (%d) lesser than zero.", p_idx)); @@ -871,7 +895,7 @@ void GraphNode::gui_input(const Ref<InputEvent> &p_ev) { ERR_FAIL_COND_MSG(get_parent_control() == nullptr, "GraphNode must be the child of a GraphEdit node."); if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { - Vector2 mpos = Vector2(mb->get_position().x, mb->get_position().y); + Vector2 mpos = mb->get_position(); if (close_rect.size != Size2() && close_rect.has_point(mpos)) { //send focus to parent get_parent_control()->grab_focus(); diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h index c7c7006bfc..2238cfdb56 100644 --- a/scene/gui/graph_node.h +++ b/scene/gui/graph_node.h @@ -98,6 +98,11 @@ private: Overlay overlay = OVERLAY_DISABLED; +#ifdef TOOLS_ENABLED + void _edit_set_position(const Point2 &p_position) override; + void _validate_property(PropertyInfo &property) const override; +#endif + protected: virtual void gui_input(const Ref<InputEvent> &p_ev) override; void _notification(int p_what); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index d9acbeb828..89fd9bfc88 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -260,24 +260,29 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { } else { if (selecting_enabled) { - if (!b->is_double_click() && (OS::get_singleton()->get_ticks_msec() - selection.last_dblclk) < 600) { + const int triple_click_timeout = 600; + const int triple_click_tolerance = 5; + const bool is_triple_click = !b->is_double_click() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < triple_click_timeout && b->get_position().distance_to(last_dblclk_pos) < triple_click_tolerance; + + if (is_triple_click && text.length()) { // Triple-click select all. selection.enabled = true; selection.begin = 0; selection.end = text.length(); selection.double_click = true; - selection.last_dblclk = 0; + last_dblclk = 0; caret_column = selection.begin; } else if (b->is_double_click()) { // Double-click select word. + last_dblclk = OS::get_singleton()->get_ticks_msec(); + last_dblclk_pos = b->get_position(); Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid); for (int i = 0; i < words.size(); i++) { - if (words[i].x < caret_column && words[i].y > caret_column) { + if ((words[i].x < caret_column && words[i].y > caret_column) || (i == words.size() - 1 && caret_column == words[i].y)) { selection.enabled = true; selection.begin = words[i].x; selection.end = words[i].y; selection.double_click = true; - selection.last_dblclk = OS::get_singleton()->get_ticks_msec(); caret_column = selection.end; break; } @@ -1555,6 +1560,20 @@ void LineEdit::deselect() { update(); } +bool LineEdit::has_selection() const { + return selection.enabled; +} + +int LineEdit::get_selection_from_column() const { + ERR_FAIL_COND_V(!selection.enabled, -1); + return selection.begin; +} + +int LineEdit::get_selection_to_column() const { + ERR_FAIL_COND_V(!selection.enabled, -1); + return selection.end; +} + void LineEdit::selection_delete() { if (selection.enabled) { delete_text(selection.begin, selection.end); @@ -2089,6 +2108,9 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("select_all"), &LineEdit::select_all); ClassDB::bind_method(D_METHOD("deselect"), &LineEdit::deselect); + ClassDB::bind_method(D_METHOD("has_selection"), &LineEdit::has_selection); + ClassDB::bind_method(D_METHOD("get_selection_from_column"), &LineEdit::get_selection_from_column); + ClassDB::bind_method(D_METHOD("get_selection_to_column"), &LineEdit::get_selection_to_column); ClassDB::bind_method(D_METHOD("set_text", "text"), &LineEdit::set_text); ClassDB::bind_method(D_METHOD("get_text"), &LineEdit::get_text); ClassDB::bind_method(D_METHOD("get_draw_control_chars"), &LineEdit::get_draw_control_chars); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index e364a79c83..923024dd56 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -136,7 +136,6 @@ private: bool creating = false; bool double_click = false; bool drag_attempt = false; - uint64_t last_dblclk = 0; } selection; struct TextOperation { @@ -153,6 +152,9 @@ private: bool pressing_inside = false; } clear_button_status; + uint64_t last_dblclk = 0; + Vector2 last_dblclk_pos; + bool caret_blink_enabled = false; bool caret_force_displayed = false; bool draw_caret = true; @@ -229,6 +231,9 @@ public: void select_all(); void selection_delete(); void deselect(); + bool has_selection() const; + int get_selection_from_column() const; + int get_selection_to_column() const; void delete_char(); void delete_text(int p_from_column, int p_to_column); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index eb88570663..277026cc28 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -333,7 +333,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> } else { frame->lines.write[i].offset.y = 0; } - frame->lines.write[i].offset += Vector2(offset.x, offset.y); + frame->lines.write[i].offset += offset; float h = frame->lines[i].text_buf->get_size().y; if (frame->min_size_over.y > 0) { @@ -578,7 +578,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> } else { frame->lines.write[i].offset.y = 0; } - frame->lines.write[i].offset += Vector2(offset.x, offset.y); + frame->lines.write[i].offset += offset; float h = frame->lines[i].text_buf->get_size().y; if (frame->min_size_over.y > 0) { @@ -2378,8 +2378,7 @@ void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, item->size.width = p_image->get_width() * p_height / p_image->get_height(); } else { // keep original width and height - item->size.height = p_image->get_height(); - item->size.width = p_image->get_width(); + item->size = p_image->get_size(); } } diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index 4edf373fbf..4a3a6837d5 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -560,7 +560,7 @@ void ScrollBar::_drag_node_input(const Ref<InputEvent> &p_input) { if (mm.is_valid()) { if (drag_node_touching && !drag_node_touching_deaccel) { - Vector2 motion = Vector2(mm->get_relative().x, mm->get_relative().y); + Vector2 motion = mm->get_relative(); drag_node_accum -= motion; Vector2 diff = drag_node_from + drag_node_accum; diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 0c051f61e2..0c0ec39c7f 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -167,7 +167,7 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) { if (mm.is_valid()) { if (drag_touching && !drag_touching_deaccel) { - Vector2 motion = Vector2(mm->get_relative().x, mm->get_relative().y); + Vector2 motion = mm->get_relative(); drag_accum -= motion; if (beyond_deadzone || (scroll_h && Math::abs(drag_accum.x) > deadzone) || (scroll_v && Math::abs(drag_accum.y) > deadzone)) { diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index a423dc0173..c8a0501d8a 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -79,7 +79,7 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { Popup *popup = get_popup(); if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) { - Point2 pos(mb->get_position().x, mb->get_position().y); + Point2 pos = mb->get_position(); Size2 size = get_size(); // Click must be on tabs in the tab header area. @@ -190,7 +190,7 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { - Point2 pos(mm->get_position().x, mm->get_position().y); + Point2 pos = mm->get_position(); Size2 size = get_size(); // Mouse must be on tabs in the tab header area. @@ -1210,6 +1210,8 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabContainer::get_tab_icon); ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabContainer::set_tab_disabled); ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &TabContainer::get_tab_disabled); + ClassDB::bind_method(D_METHOD("set_tab_hidden", "tab_idx", "hidden"), &TabContainer::set_tab_hidden); + ClassDB::bind_method(D_METHOD("get_tab_hidden", "tab_idx"), &TabContainer::get_tab_hidden); ClassDB::bind_method(D_METHOD("get_tab_idx_at_point", "point"), &TabContainer::get_tab_idx_at_point); ClassDB::bind_method(D_METHOD("set_popup", "popup"), &TabContainer::set_popup); ClassDB::bind_method(D_METHOD("get_popup"), &TabContainer::get_popup); diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index 3ca2d1c1e9..ef34bec347 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -98,29 +98,45 @@ void Tabs::gui_input(const Ref<InputEvent> &p_event) { if (mm.is_valid()) { Point2 pos = mm->get_position(); - highlight_arrow = -1; if (buttons_visible) { Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); Ref<Texture2D> decr = get_theme_icon(SNAME("decrement")); if (is_layout_rtl()) { if (pos.x < decr->get_width()) { - highlight_arrow = 1; + if (highlight_arrow != 1) { + highlight_arrow = 1; + update(); + } } else if (pos.x < incr->get_width() + decr->get_width()) { - highlight_arrow = 0; + if (highlight_arrow != 0) { + highlight_arrow = 0; + update(); + } + } else if (highlight_arrow != -1) { + highlight_arrow = -1; + update(); } } else { int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width(); if (pos.x > limit_minus_buttons + decr->get_width()) { - highlight_arrow = 1; + if (highlight_arrow != 1) { + highlight_arrow = 1; + update(); + } } else if (pos.x > limit_minus_buttons) { - highlight_arrow = 0; + if (highlight_arrow != 0) { + highlight_arrow = 0; + update(); + } + } else if (highlight_arrow != -1) { + highlight_arrow = -1; + update(); } } } _update_hover(); - update(); return; } @@ -167,7 +183,7 @@ void Tabs::gui_input(const Ref<InputEvent> &p_event) { if (mb->is_pressed() && (mb->get_button_index() == MOUSE_BUTTON_LEFT || (select_with_rmb && mb->get_button_index() == MOUSE_BUTTON_RIGHT))) { // clicks - Point2 pos(mb->get_position().x, mb->get_position().y); + Point2 pos = mb->get_position(); if (buttons_visible) { Ref<Texture2D> incr = get_theme_icon(SNAME("increment")); @@ -241,6 +257,7 @@ void Tabs::_shape(int p_tab) { tabs.write[p_tab].xl_text = atr(tabs[p_tab].text); tabs.write[p_tab].text_buf->clear(); + tabs.write[p_tab].text_buf->set_width(-1); if (tabs[p_tab].text_direction == Control::TEXT_DIRECTION_INHERITED) { tabs.write[p_tab].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); } else { @@ -529,7 +546,6 @@ bool Tabs::get_offset_buttons_visible() const { void Tabs::set_tab_title(int p_tab, const String &p_title) { ERR_FAIL_INDEX(p_tab, tabs.size()); tabs.write[p_tab].text = p_title; - tabs.write[p_tab].xl_text = atr(p_title); _shape(p_tab); update(); minimum_size_changed(); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 06dfc31621..5b13e1da0b 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1444,7 +1444,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { if (mb->get_button_index() == MOUSE_BUTTON_LEFT) { _reset_caret_blink_timer(); - Point2i pos = get_line_column_at_pos(Point2i(mpos.x, mpos.y)); + Point2i pos = get_line_column_at_pos(mpos); int row = pos.y; int col = pos.x; @@ -1547,7 +1547,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && context_menu_enabled) { _reset_caret_blink_timer(); - Point2i pos = get_line_column_at_pos(Point2i(mpos.x, mpos.y)); + Point2i pos = get_line_column_at_pos(mpos); int row = pos.y; int col = pos.x; @@ -3689,7 +3689,7 @@ void TextEdit::select_word_under_caret() { int end = 0; const Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid()); for (int i = 0; i < words.size(); i++) { - if (words[i].x <= caret.column && words[i].y >= caret.column) { + if ((words[i].x < caret.column && words[i].y > caret.column) || (i == words.size() - 1 && caret.column == words[i].y)) { begin = words[i].x; end = words[i].y; break; @@ -5385,7 +5385,7 @@ void TextEdit::_update_selection_mode_pointer() { dragging_selection = true; Point2 mp = get_local_mouse_pos(); - Point2i pos = get_line_column_at_pos(Point2i(mp.x, mp.y)); + Point2i pos = get_line_column_at_pos(mp); int line = pos.y; int col = pos.x; @@ -5402,7 +5402,7 @@ void TextEdit::_update_selection_mode_word() { dragging_selection = true; Point2 mp = get_local_mouse_pos(); - Point2i pos = get_line_column_at_pos(Point2i(mp.x, mp.y)); + Point2i pos = get_line_column_at_pos(mp); int line = pos.y; int col = pos.x; @@ -5411,7 +5411,7 @@ void TextEdit::_update_selection_mode_word() { int end = beg; Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid()); for (int i = 0; i < words.size(); i++) { - if (words[i].x < caret_pos && words[i].y > caret_pos) { + if ((words[i].x < caret_pos && words[i].y > caret_pos) || (i == words.size() - 1 && caret_pos == words[i].y)) { beg = words[i].x; end = words[i].y; break; @@ -5450,7 +5450,7 @@ void TextEdit::_update_selection_mode_line() { dragging_selection = true; Point2 mp = get_local_mouse_pos(); - Point2i pos = get_line_column_at_pos(Point2i(mp.x, mp.y)); + Point2i pos = get_line_column_at_pos(mp); int line = pos.y; int col = pos.x; @@ -5765,7 +5765,7 @@ void TextEdit::_update_minimap_hover() { return; } - const int row = get_minimap_line_at_pos(Point2i(mp.x, mp.y)); + const int row = get_minimap_line_at_pos(mp); const bool new_hovering_minimap = row >= get_first_visible_line() && row <= get_last_full_visible_line(); if (new_hovering_minimap != hovering_minimap) { @@ -5786,7 +5786,7 @@ void TextEdit::_update_minimap_click() { minimap_clicked = true; dragging_minimap = true; - int row = get_minimap_line_at_pos(Point2i(mp.x, mp.y)); + int row = get_minimap_line_at_pos(mp); if (row >= get_first_visible_line() && (row < get_last_full_visible_line() || row >= (text.size() - 1))) { minimap_scroll_ratio = v_scroll->get_as_ratio(); diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp index 286f01ee33..35a098c6fd 100644 --- a/scene/gui/texture_progress_bar.cpp +++ b/scene/gui/texture_progress_bar.cpp @@ -471,7 +471,7 @@ void TextureProgressBar::_notification(int p_what) { Vector<Point2> uvs; Vector<Point2> points; uvs.push_back(get_relative_center()); - points.push_back(progress_offset + Point2(s.x * get_relative_center().x, s.y * get_relative_center().y)); + points.push_back(progress_offset + s * get_relative_center()); for (int i = 0; i < pts.size(); i++) { Point2 uv = unit_val_to_uv(pts[i]); if (uvs.find(uv) >= 0) { @@ -493,8 +493,7 @@ void TextureProgressBar::_notification(int p_what) { p = progress->get_size(); } - p.x *= get_relative_center().x; - p.y *= get_relative_center().y; + p *= get_relative_center(); p = p.floor(); draw_line(p - Point2(8, 0), p + Point2(8, 0), Color(0.9, 0.5, 0.5), 2); draw_line(p - Point2(0, 8), p + Point2(0, 8), Color(0.9, 0.5, 0.5), 2); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index f62c09925d..7d7596635c 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -144,6 +144,7 @@ void TreeItem::_change_tree(Tree *p_tree) { /* cell mode */ void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) { ERR_FAIL_INDEX(p_column, cells.size()); + Cell &c = cells.write[p_column]; c.mode = p_mode; c.min = 0; @@ -155,8 +156,9 @@ void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) { c.text = ""; c.dirty = true; c.icon_max_w = 0; + c.cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const { @@ -167,22 +169,27 @@ TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const { /* check mode */ void TreeItem::set_checked(int p_column, bool p_checked) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].checked = p_checked; cells.write[p_column].indeterminate = false; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } void TreeItem::set_indeterminate(int p_column, bool p_indeterminate) { ERR_FAIL_INDEX(p_column, cells.size()); + // Prevent uncheck if indeterminate set to false twice if (p_indeterminate == cells[p_column].indeterminate) { return; } + cells.write[p_column].indeterminate = p_indeterminate; cells.write[p_column].checked = false; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } bool TreeItem::is_checked(int p_column) const { @@ -214,8 +221,10 @@ void TreeItem::set_text(int p_column, String p_text) { } cells.write[p_column].step = 0; } + + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } String TreeItem::get_text(int p_column) const { @@ -231,7 +240,7 @@ void TreeItem::set_text_direction(int p_column, Control::TextDirection p_text_di cells.write[p_column].dirty = true; _changed_notify(p_column); } - cached_minimum_size_dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; } Control::TextDirection TreeItem::get_text_direction(int p_column) const { @@ -241,10 +250,12 @@ Control::TextDirection TreeItem::get_text_direction(int p_column) const { void TreeItem::clear_opentype_features(int p_column) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].opentype_features.clear(); cells.write[p_column].dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } void TreeItem::set_opentype_feature(int p_column, const String &p_name, int p_value) { @@ -253,8 +264,9 @@ void TreeItem::set_opentype_feature(int p_column, const String &p_name, int p_va if (!cells[p_column].opentype_features.has(tag) || (int)cells[p_column].opentype_features[tag] != p_value) { cells.write[p_column].opentype_features[tag] = p_value; cells.write[p_column].dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } } @@ -269,11 +281,13 @@ int TreeItem::get_opentype_feature(int p_column, const String &p_name) const { void TreeItem::set_structured_text_bidi_override(int p_column, Control::StructuredTextParser p_parser) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].st_parser != p_parser) { cells.write[p_column].st_parser = p_parser; cells.write[p_column].dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } } @@ -284,10 +298,12 @@ Control::StructuredTextParser TreeItem::get_structured_text_bidi_override(int p_ void TreeItem::set_structured_text_bidi_override_options(int p_column, Array p_args) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].st_args = p_args; cells.write[p_column].dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } Array TreeItem::get_structured_text_bidi_override_options(int p_column) const { @@ -297,11 +313,13 @@ Array TreeItem::get_structured_text_bidi_override_options(int p_column) const { void TreeItem::set_language(int p_column, const String &p_language) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].language != p_language) { cells.write[p_column].language = p_language; cells.write[p_column].dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } } @@ -312,10 +330,11 @@ String TreeItem::get_language(int p_column) const { void TreeItem::set_suffix(int p_column, String p_suffix) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].suffix = p_suffix; + cells.write[p_column].cached_minimum_size_dirty = true; _changed_notify(p_column); - cached_minimum_size_dirty = true; } String TreeItem::get_suffix(int p_column) const { @@ -325,9 +344,11 @@ String TreeItem::get_suffix(int p_column) const { void TreeItem::set_icon(int p_column, const Ref<Texture2D> &p_icon) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].icon = p_icon; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } Ref<Texture2D> TreeItem::get_icon(int p_column) const { @@ -337,9 +358,11 @@ Ref<Texture2D> TreeItem::get_icon(int p_column) const { void TreeItem::set_icon_region(int p_column, const Rect2 &p_icon_region) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].icon_region = p_icon_region; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } Rect2 TreeItem::get_icon_region(int p_column) const { @@ -360,9 +383,11 @@ Color TreeItem::get_icon_modulate(int p_column) const { void TreeItem::set_icon_max_width(int p_column, int p_max) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].icon_max_w = p_max; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } int TreeItem::get_icon_max_width(int p_column) const { @@ -474,8 +499,11 @@ void TreeItem::uncollapse_tree() { void TreeItem::set_custom_minimum_height(int p_height) { custom_min_height = p_height; + + for (Cell &c : cells) + c.cached_minimum_size_dirty = true; + _changed_notify(); - cached_minimum_size_dirty = true; } int TreeItem::get_custom_minimum_height() const { @@ -799,8 +827,9 @@ void TreeItem::add_button(int p_column, const Ref<Texture2D> &p_button, int p_id button.disabled = p_disabled; button.tooltip = p_tooltip; cells.write[p_column].buttons.push_back(button); + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } int TreeItem::get_button_count(int p_column) const { @@ -843,8 +872,9 @@ void TreeItem::set_button(int p_column, int p_idx, const Ref<Texture2D> &p_butto ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); cells.write[p_column].buttons.write[p_idx].texture = p_button; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } void TreeItem::set_button_color(int p_column, int p_idx, const Color &p_color) { @@ -859,8 +889,9 @@ void TreeItem::set_button_disabled(int p_column, int p_idx, bool p_disabled) { ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); cells.write[p_column].buttons.write[p_idx].disabled = p_disabled; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } bool TreeItem::is_button_disabled(int p_column, int p_idx) const { @@ -872,9 +903,11 @@ bool TreeItem::is_button_disabled(int p_column, int p_idx) const { void TreeItem::set_editable(int p_column, bool p_editable) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].editable = p_editable; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } bool TreeItem::is_editable(int p_column) { @@ -906,8 +939,9 @@ void TreeItem::clear_custom_color(int p_column) { void TreeItem::set_custom_font(int p_column, const Ref<Font> &p_font) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].custom_font = p_font; - cached_minimum_size_dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; } Ref<Font> TreeItem::get_custom_font(int p_column) const { @@ -917,8 +951,9 @@ Ref<Font> TreeItem::get_custom_font(int p_column) const { void TreeItem::set_custom_font_size(int p_column, int p_font_size) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].custom_font_size = p_font_size; - cached_minimum_size_dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; } int TreeItem::get_custom_font_size(int p_column) const { @@ -961,8 +996,9 @@ Color TreeItem::get_custom_bg_color(int p_column) const { void TreeItem::set_custom_as_button(int p_column, bool p_button) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].custom_button = p_button; - cached_minimum_size_dirty = true; + cells.write[p_column].cached_minimum_size_dirty = true; } bool TreeItem::is_custom_set_as_button(int p_column) const { @@ -972,9 +1008,11 @@ bool TreeItem::is_custom_set_as_button(int p_column) const { void TreeItem::set_text_align(int p_column, TextAlign p_align) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].text_align = p_align; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } TreeItem::TextAlign TreeItem::get_text_align(int p_column) const { @@ -984,9 +1022,11 @@ TreeItem::TextAlign TreeItem::get_text_align(int p_column) const { void TreeItem::set_expand_right(int p_column, bool p_enable) { ERR_FAIL_INDEX(p_column, cells.size()); + cells.write[p_column].expand_right = p_enable; + cells.write[p_column].cached_minimum_size_dirty = true; + _changed_notify(p_column); - cached_minimum_size_dirty = true; } bool TreeItem::get_expand_right(int p_column) const { @@ -996,8 +1036,11 @@ bool TreeItem::get_expand_right(int p_column) const { void TreeItem::set_disable_folding(bool p_disable) { disable_folding = p_disable; + + for (Cell &c : cells) + c.cached_minimum_size_dirty = true; + _changed_notify(0); - cached_minimum_size_dirty = true; } bool TreeItem::is_folding_disabled() const { @@ -1009,14 +1052,12 @@ Size2 TreeItem::get_minimum_size(int p_column) { Tree *tree = get_tree(); ERR_FAIL_COND_V(!tree, Size2()); - if (cached_minimum_size_dirty) { - Size2 size; + const TreeItem::Cell &cell = cells[p_column]; - // Default offset? - //size.width += (disable_folding || tree->hide_folding) ? tree->cache.hseparation : tree->cache.item_margin; + if (cell.cached_minimum_size_dirty) { + Size2 size; // Text. - const TreeItem::Cell &cell = cells[p_column]; if (!cell.text.is_empty()) { if (cell.dirty) { tree->update_item_cell(this, p_column); @@ -1052,11 +1093,11 @@ Size2 TreeItem::get_minimum_size(int p_column) { size.width += (cell.buttons.size() - 1) * tree->cache.button_margin; } - cached_minimum_size = size; - cached_minimum_size_dirty = false; + cells.write[p_column].cached_minimum_size = size; + cells.write[p_column].cached_minimum_size_dirty = false; } - return cached_minimum_size; + return cell.cached_minimum_size; } Variant TreeItem::_call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { @@ -1337,10 +1378,6 @@ void Tree::update_cache() { cache.title_button_color = get_theme_color(SNAME("title_button_color")); v_scroll->set_custom_step(cache.font->get_height(cache.font_size)); - - for (TreeItem *item = get_root(); item; item = item->get_next()) { - item->cached_minimum_size_dirty = true; - } } int Tree::compute_item_height(TreeItem *p_item) const { @@ -1712,7 +1749,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } if ((select_mode == SELECT_ROW && selected_item == p_item) || p_item->cells[i].selected || !p_item->has_meta("__focus_rect")) { - Rect2i r(cell_rect.position, cell_rect.size); + Rect2i r = cell_rect; p_item->set_meta("__focus_rect", Rect2(r.position, r.size)); @@ -1968,7 +2005,8 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 arrow = cache.arrow; } - Point2 apos = p_pos + p_draw_ofs + Point2i(0, (label_h - arrow->get_height()) / 2) - cache.offset; + Point2 apos = p_pos + Point2i(0, (label_h - arrow->get_height()) / 2) - cache.offset + p_draw_ofs; + apos.x += cache.item_margin - arrow->get_width(); if (rtl) { apos.x = get_size().width - apos.x - arrow->get_width(); @@ -4000,10 +4038,12 @@ TreeItem *Tree::get_next_selected(TreeItem *p_item) { int Tree::get_column_minimum_width(int p_column) const { ERR_FAIL_INDEX_V(p_column, columns.size(), -1); + // Use the custom minimum width. int min_width = columns[p_column].custom_min_width; + // Check if the visible title of the column is wider. if (show_column_titles) { - min_width = MAX(cache.font->get_string_size(columns[p_column].title).width, min_width); + min_width = MAX(cache.font->get_string_size(columns[p_column].title).width + cache.bg->get_margin(SIDE_LEFT) + cache.bg->get_margin(SIDE_RIGHT), min_width); } if (!columns[p_column].clip_content) { @@ -4028,7 +4068,11 @@ int Tree::get_column_minimum_width(int p_column) const { Size2 item_size = item->get_minimum_size(p_column); if (p_column == 0) { item_size.width += cache.item_margin * depth; + } else { + item_size.width += cache.hseparation; } + + // Check if the item is wider. min_width = MAX(min_width, item_size.width); } } @@ -4068,9 +4112,6 @@ int Tree::get_column_width(int p_column) const { } } - if (p_column < columns.size() - 1) { - column_width += cache.hseparation; - } return column_width; } diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 85fed941dc..c4a6b6b058 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -95,6 +95,9 @@ private: bool expand_right = false; Color icon_color = Color(1, 1, 1); + Size2i cached_minimum_size; + bool cached_minimum_size_dirty = true; + TextAlign text_align = ALIGN_LEFT; Variant meta; @@ -130,9 +133,6 @@ private: bool disable_folding = false; int custom_min_height = 0; - Size2i cached_minimum_size; - bool cached_minimum_size_dirty = true; - TreeItem *parent = nullptr; // parent item TreeItem *prev = nullptr; // previous in list TreeItem *next = nullptr; // next in list diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index de557494c3..49ed9dcb82 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -48,7 +48,7 @@ void BitMap::create_from_image_alpha(const Ref<Image> &p_image, float p_threshol img->convert(Image::FORMAT_LA8); ERR_FAIL_COND(img->get_format() != Image::FORMAT_LA8); - create(Size2(img->get_width(), img->get_height())); + create(img->get_size()); const uint8_t *r = img->get_data().ptr(); uint8_t *w = bitmask.ptrw(); diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 7346b6efc7..492e0512e4 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -51,7 +51,7 @@ static Ref<StyleBoxTexture> make_stylebox(T p_src, float p_left, float p_top, fl } else { texture = Ref<ImageTexture>(memnew(ImageTexture)); Ref<Image> img = memnew(Image(p_src)); - const Size2 orig_size = Size2(img->get_width(), img->get_height()); + const Size2 orig_size = img->get_size(); img->convert(Image::FORMAT_RGBA8); img->resize(orig_size.x * scale, orig_size.y * scale); @@ -97,7 +97,7 @@ template <class T> static Ref<Texture2D> make_icon(T p_src) { Ref<ImageTexture> texture(memnew(ImageTexture)); Ref<Image> img = memnew(Image(p_src)); - const Size2 orig_size = Size2(img->get_width(), img->get_height()); + const Size2 orig_size = img->get_size(); img->convert(Image::FORMAT_RGBA8); img->resize(orig_size.x * scale, orig_size.y * scale); texture->create_from_image(img); diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index e7da41db9d..f8be00f5fb 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -1390,6 +1390,12 @@ void QuadMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset"), "set_center_offset", "get_center_offset"); } +uint32_t QuadMesh::surface_get_format(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, 1, 0); + + return RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV; +} + QuadMesh::QuadMesh() { primitive_type = PRIMITIVE_TRIANGLES; } @@ -1460,7 +1466,7 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const { } else { Vector3 p = Vector3(x * radius * w, y, z * radius * w); points.push_back(p); - Vector3 normal = Vector3(x * radius * w * scale, y / scale, z * radius * w * scale); + Vector3 normal = Vector3(x * w * scale, radius * (y / scale), z * w * scale); normals.push_back(normal.normalized()); }; ADD_TANGENT(z, 0.0, -x, 1.0) diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index 7915cb0028..d447dad97a 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -285,6 +285,8 @@ protected: virtual void _create_mesh_array(Array &p_arr) const override; public: + virtual uint32_t surface_get_format(int p_idx) const override; + QuadMesh(); void set_size(const Size2 &p_size); diff --git a/scene/resources/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp index e533fb054a..7ac40b497d 100644 --- a/scene/resources/skeleton_modification_2d.cpp +++ b/scene/resources/skeleton_modification_2d.cpp @@ -96,37 +96,25 @@ float SkeletonModification2D::clamp_angle(float p_angle, float p_min_bound, floa p_max_bound = Math_TAU + p_max_bound; } if (p_min_bound > p_max_bound) { - float tmp = p_min_bound; - p_min_bound = p_max_bound; - p_max_bound = tmp; + SWAP(p_min_bound, p_max_bound); } + bool is_beyond_bounds = (p_angle < p_min_bound || p_angle > p_max_bound); + bool is_within_bounds = (p_angle > p_min_bound && p_angle < p_max_bound); + // Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle. - if (p_invert == false) { - if (p_angle < p_min_bound || p_angle > p_max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); - Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); - - if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { - p_angle = p_min_bound; - } else { - p_angle = p_max_bound; - } - } - } else { - if (p_angle > p_min_bound && p_angle < p_max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); - Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); + if ((!p_invert && is_beyond_bounds) || (p_invert && is_within_bounds)) { + Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); + Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); + Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); - if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { - p_angle = p_min_bound; - } else { - p_angle = p_max_bound; - } + if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { + p_angle = p_min_bound; + } else { + p_angle = p_max_bound; } } + return p_angle; } @@ -152,9 +140,7 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *p_operation_b arc_angle_max = (Math_PI * 2) + arc_angle_max; } if (arc_angle_min > arc_angle_max) { - float tmp = arc_angle_min; - arc_angle_min = arc_angle_max; - arc_angle_max = tmp; + SWAP(arc_angle_min, arc_angle_max); } arc_angle_min += p_operation_bone->get_bone_angle(); arc_angle_max += p_operation_bone->get_bone_angle(); diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/resources/skeleton_modification_2d_fabrik.cpp index 6e9429034f..3b5c555f89 100644 --- a/scene/resources/skeleton_modification_2d_fabrik.cpp +++ b/scene/resources/skeleton_modification_2d_fabrik.cpp @@ -247,7 +247,7 @@ void SkeletonModification2DFABRIK::chain_backwards() { } float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y); - float length = current_bone2d_node_length / (previous_pose.get_origin() - current_pose.get_origin()).length(); + float length = current_bone2d_node_length / (current_pose.get_origin().distance_to(previous_pose.get_origin())); Vector2 finish_position = previous_pose.get_origin().lerp(current_pose.get_origin(), length); current_pose.set_origin(finish_position); @@ -268,7 +268,7 @@ void SkeletonModification2DFABRIK::chain_forwards() { Transform2D next_pose = fabrik_transform_chain[i + 1]; float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y); - float length = current_bone2d_node_length / (current_pose.get_origin() - next_pose.get_origin()).length(); + float length = current_bone2d_node_length / (next_pose.get_origin().distance_to(current_pose.get_origin())); Vector2 finish_position = current_pose.get_origin().lerp(next_pose.get_origin(), length); current_pose.set_origin(finish_position); diff --git a/scene/resources/skeleton_modification_2d_twoboneik.cpp b/scene/resources/skeleton_modification_2d_twoboneik.cpp index 88d80a501f..4f752896a9 100644 --- a/scene/resources/skeleton_modification_2d_twoboneik.cpp +++ b/scene/resources/skeleton_modification_2d_twoboneik.cpp @@ -144,7 +144,7 @@ void SkeletonModification2DTwoBoneIK::_execute(float p_delta) { // With modifications by TwistedTwigleg Vector2 target_difference = target->get_global_position() - joint_one_bone->get_global_position(); float joint_one_to_target = target_difference.length(); - float angle_atan = Math::atan2(target_difference.y, target_difference.x); + float angle_atan = target_difference.angle(); float bone_one_length = joint_one_bone->get_length() * MIN(joint_one_bone->get_global_scale().x, joint_one_bone->get_global_scale().y); float bone_two_length = joint_two_bone->get_length() * MIN(joint_two_bone->get_global_scale().x, joint_two_bone->get_global_scale().y); diff --git a/scene/resources/skeleton_modification_3d.cpp b/scene/resources/skeleton_modification_3d.cpp index ee02ede2d5..b476952d86 100644 --- a/scene/resources/skeleton_modification_3d.cpp +++ b/scene/resources/skeleton_modification_3d.cpp @@ -72,37 +72,25 @@ real_t SkeletonModification3D::clamp_angle(real_t p_angle, real_t p_min_bound, r p_max_bound = Math_TAU + p_max_bound; } if (p_min_bound > p_max_bound) { - real_t tmp = p_min_bound; - p_min_bound = p_max_bound; - p_max_bound = tmp; + SWAP(p_min_bound, p_max_bound); } + bool is_beyond_bounds = (p_angle < p_min_bound || p_angle > p_max_bound); + bool is_within_bounds = (p_angle > p_min_bound && p_angle < p_max_bound); + // Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle. - if (p_invert == false) { - if (p_angle < p_min_bound || p_angle > p_max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); - Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); - - if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { - p_angle = p_min_bound; - } else { - p_angle = p_max_bound; - } - } - } else { - if (p_angle > p_min_bound && p_angle < p_max_bound) { - Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); - Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); - Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); - - if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { - p_angle = p_min_bound; - } else { - p_angle = p_max_bound; - } + if ((!p_invert && is_beyond_bounds) || (p_invert && is_within_bounds)) { + Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound)); + Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound)); + Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle)); + + if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) { + p_angle = p_min_bound; + } else { + p_angle = p_max_bound; } } + return p_angle; } diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/resources/skeleton_modification_3d_fabrik.cpp index 69f75eb7b5..e615615924 100644 --- a/scene/resources/skeleton_modification_3d_fabrik.cpp +++ b/scene/resources/skeleton_modification_3d_fabrik.cpp @@ -232,7 +232,7 @@ void SkeletonModification3DFABRIK::chain_backwards() { int current_bone_idx = fabrik_data_chain[i].bone_idx; Transform3D current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, stack->skeleton->get_bone_local_pose_override(current_bone_idx)); - real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin - current_trans.origin).length(); + real_t length = fabrik_data_chain[i].length / (current_trans.origin.distance_to(next_bone_trans.origin)); current_trans.origin = next_bone_trans.origin.lerp(current_trans.origin, length); // Apply it back to the skeleton @@ -253,7 +253,7 @@ void SkeletonModification3DFABRIK::chain_forwards() { int next_bone_idx = fabrik_data_chain[i + 1].bone_idx; Transform3D next_bone_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx)); - real_t length = fabrik_data_chain[i].length / (current_trans.origin - next_bone_trans.origin).length(); + real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin.distance_to(current_trans.origin)); next_bone_trans.origin = current_trans.origin.lerp(next_bone_trans.origin, length); // Apply it back to the skeleton diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index d5e370568d..a8cd872408 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -1176,6 +1176,7 @@ Vector<int> SurfaceTool::generate_lod(float p_threshold, int p_target_index_coun ERR_FAIL_COND_V(simplify_func == nullptr, lod); ERR_FAIL_COND_V(vertex_array.size() == 0, lod); ERR_FAIL_COND_V(index_array.size() == 0, lod); + ERR_FAIL_COND_V(index_array.size() % 3 != 0, lod); lod.resize(index_array.size()); LocalVector<float> vertices; //uses floats diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 3dc32632cc..80cab99373 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -1248,6 +1248,14 @@ bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const { return atlas->is_pixel_opaque(x, y); } +Ref<Image> AtlasTexture::get_image() const { + if (!atlas.is_valid()) { + return Ref<Image>(); + } + + return atlas->get_image()->get_rect(region); +} + AtlasTexture::AtlasTexture() {} ///////////////////////////////////////// diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 93f4e2de5a..862b9a47a6 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -250,6 +250,8 @@ public: bool is_pixel_opaque(int p_x, int p_y) const override; + virtual Ref<Image> get_image() const override; + AtlasTexture(); }; diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index 918fc3fe9c..5a8c5b3782 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -1019,20 +1019,25 @@ Vector<Vector2> TileSet::get_tile_shape_polygon() { void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled, Ref<Texture2D> p_texture) { if (tile_meshes_dirty) { - Vector<Vector2> uvs = get_tile_shape_polygon(); + Vector<Vector2> shape = get_tile_shape_polygon(); + Vector<Vector2> uvs; + uvs.resize(shape.size()); + for (int i = 0; i < shape.size(); i++) { + uvs.write[i] = shape[i] + Vector2(0.5, 0.5); + } Vector<Color> colors; - colors.resize(uvs.size()); + colors.resize(shape.size()); colors.fill(Color(1.0, 1.0, 1.0, 1.0)); // Filled mesh. tile_filled_mesh->clear_surfaces(); Array a; a.resize(Mesh::ARRAY_MAX); - a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_VERTEX] = shape; a[Mesh::ARRAY_TEX_UV] = uvs; a[Mesh::ARRAY_COLOR] = colors; - a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(uvs); + a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(shape); tile_filled_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); // Lines mesh. @@ -1040,9 +1045,9 @@ void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform a.clear(); a.resize(Mesh::ARRAY_MAX); // Add the first point again when drawing lines. - uvs.push_back(uvs[0]); + shape.push_back(shape[0]); colors.push_back(colors[0]); - a[Mesh::ARRAY_VERTEX] = uvs; + a[Mesh::ARRAY_VERTEX] = shape; a[Mesh::ARRAY_COLOR] = colors; tile_lines_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINE_STRIP, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES); @@ -3119,7 +3124,7 @@ Vector2i TileSetAtlasSource::get_atlas_grid_size() const { } bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) { - Vector<String> components = String(p_name).split("/", true, 2); + Vector<String> components = String(p_name).split("/", true, 3); // Compute the vector2i if we have coordinates. Vector<String> coords_split = components[0].split(":"); @@ -3138,8 +3143,32 @@ bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) // Properties. if (components[1] == "size_in_atlas") { move_tile_in_atlas(coords, coords, p_value); + return true; } else if (components[1] == "next_alternative_id") { tiles[coords].next_alternative_id = p_value; + return true; + } else if (components[1] == "animation_columns") { + set_tile_animation_columns(coords, p_value); + return true; + } else if (components[1] == "animation_separation") { + set_tile_animation_separation(coords, p_value); + return true; + } else if (components[1] == "animation_speed") { + set_tile_animation_speed(coords, p_value); + return true; + } else if (components[1] == "animation_frames_count") { + set_tile_animation_frames_count(coords, p_value); + return true; + } else if (components.size() >= 3 && components[1].begins_with("animation_frame_") && components[1].trim_prefix("animation_frame_").is_valid_int()) { + int frame = components[1].trim_prefix("animation_frame_").to_int(); + if (components[2] == "duration") { + if (frame >= get_tile_animation_frames_count(coords)) { + set_tile_animation_frames_count(coords, frame + 1); + } + set_tile_animation_frame_duration(coords, frame, p_value); + return true; + } + return false; } else if (components[1].is_valid_int()) { int alternative_id = components[1].to_int(); if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) { @@ -3185,6 +3214,28 @@ bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const { } else if (components[1] == "next_alternative_id") { r_ret = tiles[coords].next_alternative_id; return true; + } else if (components[1] == "animation_columns") { + r_ret = get_tile_animation_columns(coords); + return true; + } else if (components[1] == "animation_separation") { + r_ret = get_tile_animation_separation(coords); + return true; + } else if (components[1] == "animation_speed") { + r_ret = get_tile_animation_speed(coords); + return true; + } else if (components[1] == "animation_frames_count") { + r_ret = get_tile_animation_frames_count(coords); + return true; + } else if (components.size() >= 3 && components[1].begins_with("animation_frame_") && components[1].trim_prefix("animation_frame_").is_valid_int()) { + int frame = components[1].trim_prefix("animation_frame_").to_int(); + if (frame < 0 || frame >= get_tile_animation_frames_count(coords)) { + return false; + } + if (components[2] == "duration") { + r_ret = get_tile_animation_frame_duration(coords, frame); + return true; + } + return false; } else if (components[1].is_valid_int()) { int alternative_id = components[1].to_int(); if (alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE && tiles[coords].alternatives.has(alternative_id)) { @@ -3226,6 +3277,40 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { } tile_property_list.push_back(property_info); + // animation_columns. + property_info = PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (E_tile->get().animation_columns == 0) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + + // animation_separation. + property_info = PropertyInfo(Variant::INT, "animation_separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (E_tile->get().animation_separation == Vector2i()) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + + // animation_speed. + property_info = PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (E_tile->get().animation_speed == 1.0) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + + // animation_frames_count. + tile_property_list.push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NETWORK)); + + // animation_frame_*. + bool store_durations = tiles[E_tile->key()].animation_frames_durations.size() >= 2; + for (int i = 0; i < (int)tiles[E_tile->key()].animation_frames_durations.size(); i++) { + property_info = PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR); + if (!store_durations) { + property_info.usage ^= PROPERTY_USAGE_STORAGE; + } + tile_property_list.push_back(property_info); + } + for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) { // Add a dummy property to show the alternative exists. tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); @@ -3234,11 +3319,10 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const { List<PropertyInfo> alternative_property_list; E_alternative->get()->get_property_list(&alternative_property_list); for (PropertyInfo &alternative_property_info : alternative_property_list) { - bool valid; - Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name, &valid); + Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name); Variant value = E_alternative->get()->get(alternative_property_info.name); - if (valid && value == default_value) { - property_info.usage ^= PROPERTY_USAGE_STORAGE; + if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) { + alternative_property_info.usage ^= PROPERTY_USAGE_STORAGE; } alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), alternative_property_info.name); tile_property_list.push_back(alternative_property_info); @@ -3257,33 +3341,27 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector // Create a tile if it does not exists. ERR_FAIL_COND(p_atlas_coords.x < 0 || p_atlas_coords.y < 0); ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0); - for (int x = 0; x < p_size.x; x++) { - for (int y = 0; y < p_size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - ERR_FAIL_COND_MSG(tiles.has(coords), vformat("Cannot create tile at position %s with size %s. Already a tile present at %s.", p_atlas_coords, p_size, coords)); - } - } + + bool room_for_tile = has_room_for_tile(p_atlas_coords, p_size, 1, Vector2i(), 1); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot create tile, tiles are already present in the space the tile would cover."); + + // Initialize the tile data. + TileAlternativesData tad; + tad.size_in_atlas = p_size; + tad.animation_frames_durations.push_back(1.0); + tad.alternatives[0] = memnew(TileData); + tad.alternatives[0]->set_tile_set(tile_set); + tad.alternatives[0]->set_allow_transform(false); + tad.alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed)); + tad.alternatives[0]->notify_property_list_changed(); + tad.alternatives_ids.append(0); // Create and resize the tile. - tiles.insert(p_atlas_coords, TileSetAtlasSource::TileAlternativesData()); + tiles.insert(p_atlas_coords, tad); tiles_ids.append(p_atlas_coords); tiles_ids.sort(); - tiles[p_atlas_coords].size_in_atlas = p_size; - tiles[p_atlas_coords].alternatives[0] = memnew(TileData); - tiles[p_atlas_coords].alternatives[0]->set_tile_set(tile_set); - tiles[p_atlas_coords].alternatives[0]->set_allow_transform(false); - tiles[p_atlas_coords].alternatives[0]->connect("changed", callable_mp((Resource *)this, &TileSetAtlasSource::emit_changed)); - tiles[p_atlas_coords].alternatives[0]->notify_property_list_changed(); - tiles[p_atlas_coords].alternatives_ids.append(0); - - // Add all covered positions to the mapping cache - for (int x = 0; x < p_size.x; x++) { - for (int y = 0; y < p_size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - _coords_mapping_cache[coords] = p_atlas_coords; - } - } + _create_coords_mapping_cache(p_atlas_coords); emit_signal(SNAME("changed")); } @@ -3292,14 +3370,7 @@ void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) { ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); // Remove all covered positions from the mapping cache - Size2i size = tiles[p_atlas_coords].size_in_atlas; - - for (int x = 0; x < size.x; x++) { - for (int y = 0; y < size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - _coords_mapping_cache.erase(coords); - } - } + _clear_coords_mapping_cache(p_atlas_coords); // Free tile data. for (Map<int, TileData *>::Element *E_tile_data = tiles[p_atlas_coords].alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) { @@ -3326,6 +3397,118 @@ Vector2i TileSetAtlasSource::get_tile_at_coords(Vector2i p_atlas_coords) const { return _coords_mapping_cache[p_atlas_coords]; } +void TileSetAtlasSource::set_tile_animation_columns(const Vector2i p_atlas_coords, int p_frame_columns) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND(p_frame_columns < 0); + + TileAlternativesData &tad = tiles[p_atlas_coords]; + bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, p_frame_columns, tad.animation_separation, tad.animation_frames_durations.size(), p_atlas_coords); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover."); + + _clear_coords_mapping_cache(p_atlas_coords); + + tiles[p_atlas_coords].animation_columns = p_frame_columns; + + _create_coords_mapping_cache(p_atlas_coords); + + emit_signal(SNAME("changed")); +} + +int TileSetAtlasSource::get_tile_animation_columns(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + return tiles[p_atlas_coords].animation_columns; +} + +void TileSetAtlasSource::set_tile_animation_separation(const Vector2i p_atlas_coords, const Vector2i p_separation) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND(p_separation.x < 0 || p_separation.y < 0); + + TileAlternativesData &tad = tiles[p_atlas_coords]; + bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, tad.animation_columns, p_separation, tad.animation_frames_durations.size(), p_atlas_coords); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover."); + + _clear_coords_mapping_cache(p_atlas_coords); + + tiles[p_atlas_coords].animation_separation = p_separation; + + _create_coords_mapping_cache(p_atlas_coords); + + emit_signal(SNAME("changed")); +} + +Vector2i TileSetAtlasSource::get_tile_animation_separation(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + return tiles[p_atlas_coords].animation_separation; +} + +void TileSetAtlasSource::set_tile_animation_speed(const Vector2i p_atlas_coords, real_t p_speed) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND(p_speed <= 0); + + tiles[p_atlas_coords].animation_speed = p_speed; + + emit_signal(SNAME("changed")); +} + +real_t TileSetAtlasSource::get_tile_animation_speed(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1.0, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + return tiles[p_atlas_coords].animation_speed; +} + +void TileSetAtlasSource::set_tile_animation_frames_count(const Vector2i p_atlas_coords, int p_frames_count) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_COND(p_frames_count < 1); + + TileAlternativesData &tad = tiles[p_atlas_coords]; + bool room_for_tile = has_room_for_tile(p_atlas_coords, tad.size_in_atlas, tad.animation_columns, tad.animation_separation, p_frames_count, p_atlas_coords); + ERR_FAIL_COND_MSG(!room_for_tile, "Cannot set animation columns count, tiles are already present in the space the tile would cover."); + + _clear_coords_mapping_cache(p_atlas_coords); + + int old_size = tiles[p_atlas_coords].animation_frames_durations.size(); + tiles[p_atlas_coords].animation_frames_durations.resize(p_frames_count); + for (int i = old_size; i < p_frames_count; i++) { + tiles[p_atlas_coords].animation_frames_durations[i] = 1.0; + } + + _create_coords_mapping_cache(p_atlas_coords); + + notify_property_list_changed(); + + emit_signal(SNAME("changed")); +} + +int TileSetAtlasSource::get_tile_animation_frames_count(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + return tiles[p_atlas_coords].animation_frames_durations.size(); +} + +void TileSetAtlasSource::set_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index, real_t p_duration) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_INDEX(p_frame_index, (int)tiles[p_atlas_coords].animation_frames_durations.size()); + ERR_FAIL_COND(p_duration <= 0.0); + + tiles[p_atlas_coords].animation_frames_durations[p_frame_index] = p_duration; + + emit_signal(SNAME("changed")); +} + +real_t TileSetAtlasSource::get_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + ERR_FAIL_INDEX_V(p_frame_index, (int)tiles[p_atlas_coords].animation_frames_durations.size(), 0.0); + return tiles[p_atlas_coords].animation_frames_durations[p_frame_index]; +} + +real_t TileSetAtlasSource::get_tile_animation_total_duration(const Vector2i p_atlas_coords) const { + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), 1, vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + + real_t sum = 0.0; + for (int frame = 0; frame < (int)tiles[p_atlas_coords].animation_frames_durations.size(); frame++) { + sum += tiles[p_atlas_coords].animation_frames_durations[frame]; + } + return sum; +} + Vector2i TileSetAtlasSource::get_tile_size_in_atlas(Vector2i p_atlas_coords) const { ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Vector2i(-1, -1), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); @@ -3341,16 +3524,46 @@ Vector2i TileSetAtlasSource::get_tile_id(int p_index) const { return tiles_ids[p_index]; } -Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords) const { +bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile) const { + if (p_atlas_coords.x < 0 || p_atlas_coords.y < 0) { + return false; + } + if (p_size.x <= 0 || p_size.y <= 0) { + return false; + } + Size2i atlas_grid_size = get_atlas_grid_size(); + for (int frame = 0; frame < p_frames_count; frame++) { + Vector2i frame_coords = p_atlas_coords + (p_size + p_animation_separation) * ((p_animation_columns > 0) ? Vector2i(frame % p_animation_columns, frame / p_animation_columns) : Vector2i(frame, 0)); + for (int x = 0; x < p_size.x; x++) { + for (int y = 0; y < p_size.y; y++) { + Vector2i coords = frame_coords + Vector2i(x, y); + if (_coords_mapping_cache.has(coords) && _coords_mapping_cache[coords] != p_ignored_tile) { + return false; + } + if (coords.x >= atlas_grid_size.x || coords.y >= atlas_grid_size.y) { + if (!(_coords_mapping_cache.has(coords) && _coords_mapping_cache[coords] == p_ignored_tile)) { + return false; // Only accept tiles outside the atlas if they are part of the ignored tile. + } + } + } + } + } + return true; +} + +Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int p_frame) const { ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_INDEX_V(p_frame, (int)tiles[p_atlas_coords].animation_frames_durations.size(), Rect2i()); + + const TileAlternativesData &tad = tiles[p_atlas_coords]; - Vector2i size_in_atlas = tiles[p_atlas_coords].size_in_atlas; + Vector2i size_in_atlas = tad.size_in_atlas; Vector2 region_size = texture_region_size * size_in_atlas + separation * (size_in_atlas - Vector2i(1, 1)); - Vector2 origin = margins + (p_atlas_coords * (texture_region_size + separation)); + Vector2i frame_coords = p_atlas_coords + (size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(p_frame % tad.animation_columns, p_frame / tad.animation_columns) : Vector2i(p_frame, 0)); + Vector2 origin = margins + (frame_coords * (texture_region_size + separation)); return Rect2(origin, region_size); - ; } Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const { @@ -3362,63 +3575,29 @@ Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_ margin = Vector2i(MAX(0, margin.x), MAX(0, margin.y)); Vector2i effective_texture_offset = Object::cast_to<TileData>(get_tile_data(p_atlas_coords, p_alternative_tile))->get_texture_offset(); if (ABS(effective_texture_offset.x) > margin.x || ABS(effective_texture_offset.y) > margin.y) { - effective_texture_offset.x = CLAMP(effective_texture_offset.x, -margin.x, margin.x); - effective_texture_offset.y = CLAMP(effective_texture_offset.y, -margin.y, margin.y); + effective_texture_offset = effective_texture_offset.clamp(-margin, margin); } return effective_texture_offset; } -bool TileSetAtlasSource::can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), false, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - - Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords; - if (new_atlas_coords.x < 0 || new_atlas_coords.y < 0) { - return false; - } - - Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas; - ERR_FAIL_COND_V(size.x <= 0 || size.y <= 0, false); - - Size2i grid_size = get_atlas_grid_size(); - if (new_atlas_coords.x + size.x > grid_size.x || new_atlas_coords.y + size.y > grid_size.y) { - return false; - } - - Rect2i new_rect = Rect2i(new_atlas_coords, size); - // Check if the new tile can fit in the new rect. - for (int x = new_rect.position.x; x < new_rect.get_end().x; x++) { - for (int y = new_rect.position.y; y < new_rect.get_end().y; y++) { - Vector2i coords = get_tile_at_coords(Vector2i(x, y)); - if (coords != p_atlas_coords && coords != TileSetSource::INVALID_ATLAS_COORDS) { - return false; - } - } - } - - return true; -} - void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords, Vector2i p_new_size) { - bool can_move = can_move_tile_in_atlas(p_atlas_coords, p_new_atlas_coords, p_new_size); - ERR_FAIL_COND_MSG(!can_move, vformat("Cannot move tile at position %s with size %s. Tile already present.", p_new_atlas_coords, p_new_size)); + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + + TileAlternativesData &tad = tiles[p_atlas_coords]; // Compute the actual new rect from arguments. Vector2i new_atlas_coords = (p_new_atlas_coords != INVALID_ATLAS_COORDS) ? p_new_atlas_coords : p_atlas_coords; - Vector2i size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tiles[p_atlas_coords].size_in_atlas; + Vector2i new_size = (p_new_size != Vector2i(-1, -1)) ? p_new_size : tad.size_in_atlas; - if (new_atlas_coords == p_atlas_coords && size == tiles[p_atlas_coords].size_in_atlas) { + if (new_atlas_coords == p_atlas_coords && new_size == tad.size_in_atlas) { return; } - // Remove all covered positions from the mapping cache. - Size2i old_size = tiles[p_atlas_coords].size_in_atlas; - for (int x = 0; x < old_size.x; x++) { - for (int y = 0; y < old_size.y; y++) { - Vector2i coords = p_atlas_coords + Vector2i(x, y); - _coords_mapping_cache.erase(coords); - } - } + bool room_for_tile = has_room_for_tile(new_atlas_coords, new_size, tad.animation_columns, tad.animation_separation, tad.animation_frames_durations.size(), p_atlas_coords); + ERR_FAIL_COND_MSG(!room_for_tile, vformat("Cannot move tile at position %s with size %s. Tile already present.", new_atlas_coords, new_size)); + + _clear_coords_mapping_cache(p_atlas_coords); // Move the tile and update its size. if (new_atlas_coords != p_atlas_coords) { @@ -3429,15 +3608,9 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_ tiles_ids.append(new_atlas_coords); tiles_ids.sort(); } - tiles[new_atlas_coords].size_in_atlas = size; + tiles[new_atlas_coords].size_in_atlas = new_size; - // Add all covered positions to the mapping cache again. - for (int x = 0; x < size.x; x++) { - for (int y = 0; y < size.y; y++) { - Vector2i coords = new_atlas_coords + Vector2i(x, y); - _coords_mapping_cache[coords] = new_atlas_coords; - } - } + _create_coords_mapping_cache(new_atlas_coords); emit_signal(SNAME("changed")); } @@ -3566,12 +3739,25 @@ void TileSetAtlasSource::_bind_methods() { // Base tiles ClassDB::bind_method(D_METHOD("create_tile", "atlas_coords", "size"), &TileSetAtlasSource::create_tile, DEFVAL(Vector2i(1, 1))); ClassDB::bind_method(D_METHOD("remove_tile", "atlas_coords"), &TileSetAtlasSource::remove_tile); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative - ClassDB::bind_method(D_METHOD("can_move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::can_move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1))); ClassDB::bind_method(D_METHOD("move_tile_in_atlas", "atlas_coords", "new_atlas_coords", "new_size"), &TileSetAtlasSource::move_tile_in_atlas, DEFVAL(INVALID_ATLAS_COORDS), DEFVAL(Vector2i(-1, -1))); ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas); + ClassDB::bind_method(D_METHOD("has_room_for_tile", "atlas_coords", "size", "animation_columns", "animation_separation", "frames_count", "ignored_tile"), &TileSetAtlasSource::has_room_for_tile, DEFVAL(INVALID_ATLAS_COORDS)); + ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords); + ClassDB::bind_method(D_METHOD("set_tile_animation_columns", "atlas_coords", "frame_columns"), &TileSetAtlasSource::set_tile_animation_columns); + ClassDB::bind_method(D_METHOD("get_tile_animation_columns", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_columns); + ClassDB::bind_method(D_METHOD("set_tile_animation_separation", "atlas_coords", "separation"), &TileSetAtlasSource::set_tile_animation_separation); + ClassDB::bind_method(D_METHOD("get_tile_animation_separation", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_separation); + ClassDB::bind_method(D_METHOD("set_tile_animation_speed", "atlas_coords", "speed"), &TileSetAtlasSource::set_tile_animation_speed); + ClassDB::bind_method(D_METHOD("get_tile_animation_speed", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_speed); + ClassDB::bind_method(D_METHOD("set_tile_animation_frames_count", "atlas_coords", "frames_count"), &TileSetAtlasSource::set_tile_animation_frames_count); + ClassDB::bind_method(D_METHOD("get_tile_animation_frames_count", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_frames_count); + ClassDB::bind_method(D_METHOD("set_tile_animation_frame_duration", "atlas_coords", "frame_index", "duration"), &TileSetAtlasSource::set_tile_animation_frame_duration); + ClassDB::bind_method(D_METHOD("get_tile_animation_frame_duration", "atlas_coords", "frame_index"), &TileSetAtlasSource::get_tile_animation_frame_duration); + ClassDB::bind_method(D_METHOD("get_tile_animation_total_duration", "atlas_coords"), &TileSetAtlasSource::get_tile_animation_total_duration); + // Alternative tiles ClassDB::bind_method(D_METHOD("create_alternative_tile", "atlas_coords", "alternative_id_override"), &TileSetAtlasSource::create_alternative_tile, DEFVAL(INVALID_TILE_ALTERNATIVE)); ClassDB::bind_method(D_METHOD("remove_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::remove_alternative_tile); @@ -3584,7 +3770,7 @@ void TileSetAtlasSource::_bind_methods() { ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size); ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture); ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture); - ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords"), &TileSetAtlasSource::get_tile_texture_region); + ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_tile_texture_region, DEFVAL(0)); } TileSetAtlasSource::~TileSetAtlasSource() { @@ -3618,6 +3804,45 @@ void TileSetAtlasSource::_compute_next_alternative_id(const Vector2i p_atlas_coo }; } +void TileSetAtlasSource::_clear_coords_mapping_cache(Vector2i p_atlas_coords) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + TileAlternativesData &tad = tiles[p_atlas_coords]; + for (int frame = 0; frame < (int)tad.animation_frames_durations.size(); frame++) { + Vector2i frame_coords = p_atlas_coords + (tad.size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(frame % tad.animation_columns, frame / tad.animation_columns) : Vector2i(frame, 0)); + for (int x = 0; x < tad.size_in_atlas.x; x++) { + for (int y = 0; y < tad.size_in_atlas.y; y++) { + Vector2i coords = frame_coords + Vector2i(x, y); + if (!_coords_mapping_cache.has(coords)) { + WARN_PRINT(vformat("TileSetAtlasSource has no cached tile at position %s, the position cache might be corrupted.", coords)); + } else { + if (_coords_mapping_cache[coords] != p_atlas_coords) { + WARN_PRINT(vformat("The position cache at position %s is pointing to a wrong tile, the position cache might be corrupted.", coords)); + } + _coords_mapping_cache.erase(coords); + } + } + } + } +} + +void TileSetAtlasSource::_create_coords_mapping_cache(Vector2i p_atlas_coords) { + ERR_FAIL_COND_MSG(!tiles.has(p_atlas_coords), vformat("TileSetAtlasSource has no tile at %s.", Vector2i(p_atlas_coords))); + + TileAlternativesData &tad = tiles[p_atlas_coords]; + for (int frame = 0; frame < (int)tad.animation_frames_durations.size(); frame++) { + Vector2i frame_coords = p_atlas_coords + (tad.size_in_atlas + tad.animation_separation) * ((tad.animation_columns > 0) ? Vector2i(frame % tad.animation_columns, frame / tad.animation_columns) : Vector2i(frame, 0)); + for (int x = 0; x < tad.size_in_atlas.x; x++) { + for (int y = 0; y < tad.size_in_atlas.y; y++) { + Vector2i coords = frame_coords + Vector2i(x, y); + if (_coords_mapping_cache.has(coords)) { + WARN_PRINT(vformat("The cache already has a tile for position %s, the position cache might be corrupted.", coords)); + } + _coords_mapping_cache[coords] = p_atlas_coords; + } + } + } +} + /////////////////////////////// TileSetScenesCollectionSource ////////////////////////////////////// void TileSetScenesCollectionSource::_compute_next_alternative_id() { diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index ba7207241a..46cb1fb36d 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -447,16 +447,23 @@ public: class TileSetAtlasSource : public TileSetSource { GDCLASS(TileSetAtlasSource, TileSetSource); -public: +private: struct TileAlternativesData { Vector2i size_in_atlas = Vector2i(1, 1); Vector2i texture_offset; + + // Animation + int animation_columns = 0; + Vector2i animation_separation; + real_t animation_speed = 1.0; + LocalVector<real_t> animation_frames_durations; + + // Alternatives Map<int, TileData *> alternatives; Vector<int> alternatives_ids; int next_alternative_id = 1; }; -private: Ref<Texture2D> texture; Vector2i margins; Vector2i separation; @@ -471,6 +478,9 @@ private: void _compute_next_alternative_id(const Vector2i p_atlas_coords); + void _create_coords_mapping_cache(Vector2i p_atlas_coords); + void _clear_coords_mapping_cache(Vector2i p_atlas_coords); + protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -513,18 +523,32 @@ public: Vector2i get_texture_region_size() const; // Base tiles. - void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1)); // Create a tile if it does not exists, or add alternative tile if it does. - void remove_tile(Vector2i p_atlas_coords); // Remove a tile. If p_tile_key.alternative_tile if different from 0, remove the alternative + void create_tile(const Vector2i p_atlas_coords, const Vector2i p_size = Vector2i(1, 1)); + void remove_tile(Vector2i p_atlas_coords); virtual bool has_tile(Vector2i p_atlas_coords) const override; - bool can_move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1)) const; void move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_new_atlas_coords = INVALID_ATLAS_COORDS, Vector2i p_new_size = Vector2i(-1, -1)); Vector2i get_tile_size_in_atlas(Vector2i p_atlas_coords) const; virtual int get_tiles_count() const override; virtual Vector2i get_tile_id(int p_index) const override; + bool has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile = INVALID_ATLAS_COORDS) const; + Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const; + // Animation. + void set_tile_animation_columns(const Vector2i p_atlas_coords, int p_frame_columns); + int get_tile_animation_columns(const Vector2i p_atlas_coords) const; + void set_tile_animation_separation(const Vector2i p_atlas_coords, const Vector2i p_separation); + Vector2i get_tile_animation_separation(const Vector2i p_atlas_coords) const; + void set_tile_animation_speed(const Vector2i p_atlas_coords, real_t p_speed); + real_t get_tile_animation_speed(const Vector2i p_atlas_coords) const; + void set_tile_animation_frames_count(const Vector2i p_atlas_coords, int p_frames_count); + int get_tile_animation_frames_count(const Vector2i p_atlas_coords) const; + void set_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index, real_t p_duration); + real_t get_tile_animation_frame_duration(const Vector2i p_atlas_coords, int p_frame_index) const; + real_t get_tile_animation_total_duration(const Vector2i p_atlas_coords) const; + // Alternative tiles. int create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override = -1); void remove_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_tile); @@ -542,7 +566,7 @@ public: Vector2i get_atlas_grid_size() const; bool has_tiles_outside_texture(); void clear_tiles_outside_texture(); - Rect2i get_tile_texture_region(Vector2i p_atlas_coords) const; + Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const; Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const; ~TileSetAtlasSource(); diff --git a/servers/physics_2d/body_2d_sw.cpp b/servers/physics_2d/body_2d_sw.cpp index edd769aa9a..38b98b7bca 100644 --- a/servers/physics_2d/body_2d_sw.cpp +++ b/servers/physics_2d/body_2d_sw.cpp @@ -113,7 +113,7 @@ void Body2DSW::update_mass_properties() { _inv_inertia = 0; _inv_mass = 0; } break; - case PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED: { + case PhysicsServer2D::BODY_MODE_DYNAMIC_LINEAR: { _inv_inertia = 0; _inv_mass = 1.0 / mass; @@ -257,7 +257,7 @@ void Body2DSW::set_mode(PhysicsServer2D::BodyMode p_mode) { set_active(true); } break; - case PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED: { + case PhysicsServer2D::BODY_MODE_DYNAMIC_LINEAR: { _inv_mass = mass > 0 ? (1.0 / mass) : 0; _inv_inertia = 0; angular_velocity = 0; diff --git a/servers/physics_2d/body_direct_state_2d_sw.cpp b/servers/physics_2d/body_direct_state_2d_sw.cpp index 58250c3077..1e924a831e 100644 --- a/servers/physics_2d/body_direct_state_2d_sw.cpp +++ b/servers/physics_2d/body_direct_state_2d_sw.cpp @@ -59,6 +59,7 @@ real_t PhysicsDirectBodyState2DSW::get_inverse_inertia() const { } void PhysicsDirectBodyState2DSW::set_linear_velocity(const Vector2 &p_velocity) { + body->set_active(true); body->set_linear_velocity(p_velocity); } @@ -67,6 +68,7 @@ Vector2 PhysicsDirectBodyState2DSW::get_linear_velocity() const { } void PhysicsDirectBodyState2DSW::set_angular_velocity(real_t p_velocity) { + body->set_active(true); body->set_angular_velocity(p_velocity); } @@ -87,26 +89,32 @@ Vector2 PhysicsDirectBodyState2DSW::get_velocity_at_local_position(const Vector2 } void PhysicsDirectBodyState2DSW::add_central_force(const Vector2 &p_force) { + body->set_active(true); body->add_central_force(p_force); } void PhysicsDirectBodyState2DSW::add_force(const Vector2 &p_force, const Vector2 &p_position) { + body->set_active(true); body->add_force(p_force, p_position); } void PhysicsDirectBodyState2DSW::add_torque(real_t p_torque) { + body->set_active(true); body->add_torque(p_torque); } void PhysicsDirectBodyState2DSW::apply_central_impulse(const Vector2 &p_impulse) { + body->set_active(true); body->apply_central_impulse(p_impulse); } void PhysicsDirectBodyState2DSW::apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position) { + body->set_active(true); body->apply_impulse(p_impulse, p_position); } void PhysicsDirectBodyState2DSW::apply_torque_impulse(real_t p_torque) { + body->set_active(true); body->apply_torque_impulse(p_torque); } diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp index b9b26eb21d..1058f75407 100644 --- a/servers/physics_2d/space_2d_sw.cpp +++ b/servers/physics_2d/space_2d_sw.cpp @@ -34,6 +34,9 @@ #include "core/os/os.h" #include "core/templates/pair.h" #include "physics_server_2d_sw.h" + +#define TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR 0.05 + _FORCE_INLINE_ static bool _can_collide_with(CollisionObject2DSW *p_object, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) { if (!(p_object->get_collision_layer() & p_collision_mask)) { return false; @@ -435,6 +438,8 @@ bool PhysicsDirectSpaceState2DSW::rest_info(RID p_shape, const Transform2D &p_sh Shape2DSW *shape = PhysicsServer2DSW::singletonsw->shape_owner.getornull(p_shape); ERR_FAIL_COND_V(!shape, 0); + real_t min_contact_depth = p_margin * TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR; + Rect2 aabb = p_shape_xform.xform(shape->get_aabb()); aabb = aabb.merge(Rect2(aabb.position + p_motion, aabb.size)); //motion aabb = aabb.grow(p_margin); @@ -445,7 +450,7 @@ bool PhysicsDirectSpaceState2DSW::rest_info(RID p_shape, const Transform2D &p_sh rcd.best_len = 0; rcd.best_object = nullptr; rcd.best_shape = 0; - rcd.min_allowed_depth = space->test_motion_min_contact_depth; + rcd.min_allowed_depth = min_contact_depth; for (int i = 0; i < amount; i++) { if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask, p_collide_with_bodies, p_collide_with_areas)) { @@ -569,6 +574,8 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co ExcludedShapeSW excluded_shape_pairs[max_excluded_shape_pairs]; int excluded_shape_pair_count = 0; + real_t min_contact_depth = p_margin * TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR; + real_t motion_length = p_motion.length(); Vector2 motion_normal = p_motion / motion_length; @@ -671,6 +678,8 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co break; } + recovered = true; + Vector2 recover_motion; for (int i = 0; i < cbk.amount; i++) { Vector2 a = sr[i * 2 + 0]; @@ -682,9 +691,9 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co // Compute depth on recovered motion. real_t depth = n.dot(a + recover_motion) - d; - if (depth > 0.0) { + if (depth > min_contact_depth + CMP_EPSILON) { // Only recover if there is penetration. - recover_motion -= n * depth * 0.4; + recover_motion -= n * (depth - min_contact_depth) * 0.4; } } @@ -693,8 +702,6 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co break; } - recovered = true; - body_transform.elements[2] += recover_motion; body_aabb.position += recover_motion; @@ -870,7 +877,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co rcd.best_shape = 0; // Allowed depth can't be lower than motion length, in order to handle contacts at low speed. - rcd.min_allowed_depth = MIN(motion_length, test_motion_min_contact_depth); + rcd.min_allowed_depth = MIN(motion_length, min_contact_depth); int from_shape = best_shape != -1 ? best_shape : 0; int to_shape = best_shape != -1 ? best_shape + 1 : p_body->get_shape_count(); @@ -1141,9 +1148,6 @@ void Space2DSW::set_param(PhysicsServer2D::SpaceParameter p_param, real_t p_valu case PhysicsServer2D::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS: constraint_bias = p_value; break; - case PhysicsServer2D::SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH: - test_motion_min_contact_depth = p_value; - break; } } @@ -1163,8 +1167,6 @@ real_t Space2DSW::get_param(PhysicsServer2D::SpaceParameter p_param) const { return body_time_to_sleep; case PhysicsServer2D::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS: return constraint_bias; - case PhysicsServer2D::SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH: - return test_motion_min_contact_depth; } return 0; } diff --git a/servers/physics_2d/space_2d_sw.h b/servers/physics_2d/space_2d_sw.h index 30c6b4cf55..a1a8a77ee4 100644 --- a/servers/physics_2d/space_2d_sw.h +++ b/servers/physics_2d/space_2d_sw.h @@ -102,7 +102,6 @@ private: real_t contact_max_separation = 1.5; real_t contact_max_allowed_penetration = 0.3; real_t constraint_bias = 0.2; - real_t test_motion_min_contact_depth = 0.005; enum { INTERSECTION_QUERY_MAX = 2048 diff --git a/servers/physics_3d/body_3d_sw.cpp b/servers/physics_3d/body_3d_sw.cpp index 5924e249a5..91fe80a3dc 100644 --- a/servers/physics_3d/body_3d_sw.cpp +++ b/servers/physics_3d/body_3d_sw.cpp @@ -154,7 +154,7 @@ void Body3DSW::update_mass_properties() { _inv_inertia = Vector3(); _inv_mass = 0; } break; - case PhysicsServer3D::BODY_MODE_DYNAMIC_LOCKED: { + case PhysicsServer3D::BODY_MODE_DYNAMIC_LINEAR: { _inv_inertia_tensor.set_zero(); _inv_mass = 1.0 / mass; @@ -310,7 +310,7 @@ void Body3DSW::set_mode(PhysicsServer3D::BodyMode p_mode) { set_active(true); } break; - case PhysicsServer3D::BODY_MODE_DYNAMIC_LOCKED: { + case PhysicsServer3D::BODY_MODE_DYNAMIC_LINEAR: { _inv_mass = mass > 0 ? (1.0 / mass) : 0; _inv_inertia = Vector3(); angular_velocity = Vector3(); diff --git a/servers/physics_3d/body_direct_state_3d_sw.cpp b/servers/physics_3d/body_direct_state_3d_sw.cpp index d197dd288d..e74baefc3a 100644 --- a/servers/physics_3d/body_direct_state_3d_sw.cpp +++ b/servers/physics_3d/body_direct_state_3d_sw.cpp @@ -66,6 +66,7 @@ Basis PhysicsDirectBodyState3DSW::get_inverse_inertia_tensor() const { } void PhysicsDirectBodyState3DSW::set_linear_velocity(const Vector3 &p_velocity) { + body->set_active(true); body->set_linear_velocity(p_velocity); } @@ -74,6 +75,7 @@ Vector3 PhysicsDirectBodyState3DSW::get_linear_velocity() const { } void PhysicsDirectBodyState3DSW::set_angular_velocity(const Vector3 &p_velocity) { + body->set_active(true); body->set_angular_velocity(p_velocity); } @@ -94,26 +96,32 @@ Vector3 PhysicsDirectBodyState3DSW::get_velocity_at_local_position(const Vector3 } void PhysicsDirectBodyState3DSW::add_central_force(const Vector3 &p_force) { + body->set_active(true); body->add_central_force(p_force); } void PhysicsDirectBodyState3DSW::add_force(const Vector3 &p_force, const Vector3 &p_position) { + body->set_active(true); body->add_force(p_force, p_position); } void PhysicsDirectBodyState3DSW::add_torque(const Vector3 &p_torque) { + body->set_active(true); body->add_torque(p_torque); } void PhysicsDirectBodyState3DSW::apply_central_impulse(const Vector3 &p_impulse) { + body->set_active(true); body->apply_central_impulse(p_impulse); } void PhysicsDirectBodyState3DSW::apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position) { + body->set_active(true); body->apply_impulse(p_impulse, p_position); } void PhysicsDirectBodyState3DSW::apply_torque_impulse(const Vector3 &p_impulse) { + body->set_active(true); body->apply_torque_impulse(p_impulse); } diff --git a/servers/physics_3d/space_3d_sw.cpp b/servers/physics_3d/space_3d_sw.cpp index cc4eab1f0b..78c4e30c83 100644 --- a/servers/physics_3d/space_3d_sw.cpp +++ b/servers/physics_3d/space_3d_sw.cpp @@ -34,6 +34,8 @@ #include "core/config/project_settings.h" #include "physics_server_3d_sw.h" +#define TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR 0.05 + _FORCE_INLINE_ static bool _can_collide_with(CollisionObject3DSW *p_object, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) { if (!(p_object->get_collision_layer() & p_collision_mask)) { return false; @@ -488,13 +490,15 @@ bool PhysicsDirectSpaceState3DSW::rest_info(RID p_shape, const Transform3D &p_sh Shape3DSW *shape = PhysicsServer3DSW::singletonsw->shape_owner.getornull(p_shape); ERR_FAIL_COND_V(!shape, 0); + real_t min_contact_depth = p_margin * TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR; + AABB aabb = p_shape_xform.xform(shape->get_aabb()); aabb = aabb.grow(p_margin); int amount = space->broadphase->cull_aabb(aabb, space->intersection_query_results, Space3DSW::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results); _RestCallbackData rcd; - rcd.min_allowed_depth = space->test_motion_min_contact_depth; + rcd.min_allowed_depth = min_contact_depth; for (int i = 0; i < amount; i++) { if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask, p_collide_with_bodies, p_collide_with_areas)) { @@ -658,6 +662,8 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co body_aabb = p_from.xform(p_body->get_inv_transform().xform(body_aabb)); body_aabb = body_aabb.grow(p_margin); + real_t min_contact_depth = p_margin * TEST_MOTION_MIN_CONTACT_DEPTH_FACTOR; + real_t motion_length = p_motion.length(); Vector3 motion_normal = p_motion / motion_length; @@ -711,8 +717,9 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co break; } - Vector3 recover_motion; + recovered = true; + Vector3 recover_motion; for (int i = 0; i < cbk.amount; i++) { Vector3 a = sr[i * 2 + 0]; Vector3 b = sr[i * 2 + 1]; @@ -723,9 +730,9 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co // Compute depth on recovered motion. real_t depth = n.dot(a + recover_motion) - d; - if (depth > 0.0) { + if (depth > min_contact_depth + CMP_EPSILON) { // Only recover if there is penetration. - recover_motion -= n * depth * 0.4; + recover_motion -= n * (depth - min_contact_depth) * 0.4; } } @@ -734,8 +741,6 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co break; } - recovered = true; - body_transform.origin += recover_motion; body_aabb.position += recover_motion; @@ -889,7 +894,7 @@ bool Space3DSW::test_body_motion(Body3DSW *p_body, const Transform3D &p_from, co } // Allowed depth can't be lower than motion length, in order to handle contacts at low speed. - rcd.min_allowed_depth = MIN(motion_length, test_motion_min_contact_depth); + rcd.min_allowed_depth = MIN(motion_length, min_contact_depth); int from_shape = best_shape != -1 ? best_shape : 0; int to_shape = best_shape != -1 ? best_shape + 1 : p_body->get_shape_count(); @@ -1158,9 +1163,6 @@ void Space3DSW::set_param(PhysicsServer3D::SpaceParameter p_param, real_t p_valu case PhysicsServer3D::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS: constraint_bias = p_value; break; - case PhysicsServer3D::SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH: - test_motion_min_contact_depth = p_value; - break; } } @@ -1182,8 +1184,6 @@ real_t Space3DSW::get_param(PhysicsServer3D::SpaceParameter p_param) const { return body_angular_velocity_damp_ratio; case PhysicsServer3D::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS: return constraint_bias; - case PhysicsServer3D::SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH: - return test_motion_min_contact_depth; } return 0; } diff --git a/servers/physics_3d/space_3d_sw.h b/servers/physics_3d/space_3d_sw.h index fc2a7d304d..daa1244bf8 100644 --- a/servers/physics_3d/space_3d_sw.h +++ b/servers/physics_3d/space_3d_sw.h @@ -96,7 +96,6 @@ private: real_t contact_max_separation = 0.05; real_t contact_max_allowed_penetration = 0.01; real_t constraint_bias = 0.01; - real_t test_motion_min_contact_depth = 0.00001; enum { INTERSECTION_QUERY_MAX = 2048 diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp index 8d5367e735..3fc94cd727 100644 --- a/servers/physics_server_2d.cpp +++ b/servers/physics_server_2d.cpp @@ -670,7 +670,6 @@ void PhysicsServer2D::_bind_methods() { BIND_ENUM_CONSTANT(SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD); BIND_ENUM_CONSTANT(SPACE_PARAM_BODY_TIME_TO_SLEEP); BIND_ENUM_CONSTANT(SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS); - BIND_ENUM_CONSTANT(SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH); BIND_ENUM_CONSTANT(SHAPE_WORLD_BOUNDARY); BIND_ENUM_CONSTANT(SHAPE_SEPARATION_RAY); @@ -700,7 +699,7 @@ void PhysicsServer2D::_bind_methods() { BIND_ENUM_CONSTANT(BODY_MODE_STATIC); BIND_ENUM_CONSTANT(BODY_MODE_KINEMATIC); BIND_ENUM_CONSTANT(BODY_MODE_DYNAMIC); - BIND_ENUM_CONSTANT(BODY_MODE_DYNAMIC_LOCKED); + BIND_ENUM_CONSTANT(BODY_MODE_DYNAMIC_LINEAR); BIND_ENUM_CONSTANT(BODY_PARAM_BOUNCE); BIND_ENUM_CONSTANT(BODY_PARAM_FRICTION); diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h index 30d3c47051..4b85382fe5 100644 --- a/servers/physics_server_2d.h +++ b/servers/physics_server_2d.h @@ -262,7 +262,6 @@ public: SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD, SPACE_PARAM_BODY_TIME_TO_SLEEP, SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS, - SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH, }; virtual void space_set_param(RID p_space, SpaceParameter p_param, real_t p_value) = 0; @@ -350,7 +349,7 @@ public: BODY_MODE_STATIC, BODY_MODE_KINEMATIC, BODY_MODE_DYNAMIC, - BODY_MODE_DYNAMIC_LOCKED, + BODY_MODE_DYNAMIC_LINEAR, }; virtual RID body_create() = 0; diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp index e868246636..9384cdc7b8 100644 --- a/servers/physics_server_3d.cpp +++ b/servers/physics_server_3d.cpp @@ -834,7 +834,7 @@ void PhysicsServer3D::_bind_methods() { BIND_ENUM_CONSTANT(BODY_MODE_STATIC); BIND_ENUM_CONSTANT(BODY_MODE_KINEMATIC); BIND_ENUM_CONSTANT(BODY_MODE_DYNAMIC); - BIND_ENUM_CONSTANT(BODY_MODE_DYNAMIC_LOCKED); + BIND_ENUM_CONSTANT(BODY_MODE_DYNAMIC_LINEAR); BIND_ENUM_CONSTANT(BODY_PARAM_BOUNCE); BIND_ENUM_CONSTANT(BODY_PARAM_FRICTION); @@ -867,7 +867,6 @@ void PhysicsServer3D::_bind_methods() { BIND_ENUM_CONSTANT(SPACE_PARAM_BODY_TIME_TO_SLEEP); BIND_ENUM_CONSTANT(SPACE_PARAM_BODY_ANGULAR_VELOCITY_DAMP_RATIO); BIND_ENUM_CONSTANT(SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS); - BIND_ENUM_CONSTANT(SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH); BIND_ENUM_CONSTANT(BODY_AXIS_LINEAR_X); BIND_ENUM_CONSTANT(BODY_AXIS_LINEAR_Y); diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h index 3e34da9561..14d395e714 100644 --- a/servers/physics_server_3d.h +++ b/servers/physics_server_3d.h @@ -271,7 +271,6 @@ public: SPACE_PARAM_BODY_TIME_TO_SLEEP, SPACE_PARAM_BODY_ANGULAR_VELOCITY_DAMP_RATIO, SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS, - SPACE_PARAM_TEST_MOTION_MIN_CONTACT_DEPTH }; virtual void space_set_param(RID p_space, SpaceParameter p_param, real_t p_value) = 0; @@ -361,7 +360,7 @@ public: BODY_MODE_STATIC, BODY_MODE_KINEMATIC, BODY_MODE_DYNAMIC, - BODY_MODE_DYNAMIC_LOCKED, + BODY_MODE_DYNAMIC_LINEAR, }; virtual RID body_create() = 0; diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index efa3a457d3..456c736731 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -182,7 +182,7 @@ void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item * if (ci->commands != nullptr) { ci->final_transform = xform; - ci->final_modulate = Color(modulate.r * ci->self_modulate.r, modulate.g * ci->self_modulate.g, modulate.b * ci->self_modulate.b, modulate.a * ci->self_modulate.a); + ci->final_modulate = modulate * ci->self_modulate; ci->global_rect_cache = global_rect; ci->global_rect_cache.position -= p_clip_rect.position; ci->light_masked = false; diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp index c53c202bab..559e6d5ad7 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp @@ -197,7 +197,7 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color } } else { screenrect = imgrect; - screenrect.position += ((Size2(window_size.width, window_size.height) - screenrect.size) / 2.0).floor(); + screenrect.position += ((window_size - screenrect.size) / 2.0).floor(); } screenrect.position /= window_size; diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index fa66ed85a9..0f98417215 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -2415,6 +2415,13 @@ RID RendererSceneRenderRD::render_buffers_get_back_depth_texture(RID p_render_bu return rb->depth_back_texture; } +RID RendererSceneRenderRD::render_buffers_get_depth_texture(RID p_render_buffers) { + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, RID()); + + return rb->depth_texture; +} + RID RendererSceneRenderRD::render_buffers_get_ao_texture(RID p_render_buffers) { RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); ERR_FAIL_COND_V(!rb, RID()); @@ -3365,7 +3372,7 @@ void RendererSceneRenderRD::_setup_decals(const PagedArray<RID> &p_decals, const Vector3 decal_extents = storage->decal_get_extents(decal); Transform3D scale_xform; - scale_xform.basis.scale(Vector3(decal_extents.x, decal_extents.y, decal_extents.z)); + scale_xform.basis.scale(decal_extents); Transform3D to_decal_xform = (p_camera_inverse_xform * di->transform * scale_xform * uv_xform).affine_inverse(); RendererStorageRD::store_transform(to_decal_xform, dd.xform); @@ -4258,10 +4265,7 @@ void RendererSceneRenderRD::_render_shadow_pass(RID p_light, RID p_shadow_atlas, light_projection = light_instance->shadow_transform[p_pass].camera; light_transform = light_instance->shadow_transform[p_pass].transform; - atlas_rect.position.x = light_instance->directional_rect.position.x; - atlas_rect.position.y = light_instance->directional_rect.position.y; - atlas_rect.size.width = light_instance->directional_rect.size.x; - atlas_rect.size.height = light_instance->directional_rect.size.y; + atlas_rect = light_instance->directional_rect; if (storage->light_directional_get_shadow_mode(light_instance->light) == RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_4_SPLITS) { atlas_rect.size.width /= 2; @@ -4272,8 +4276,7 @@ void RendererSceneRenderRD::_render_shadow_pass(RID p_light, RID p_shadow_atlas, } else if (p_pass == 2) { atlas_rect.position.y += atlas_rect.size.height; } else if (p_pass == 3) { - atlas_rect.position.x += atlas_rect.size.width; - atlas_rect.position.y += atlas_rect.size.height; + atlas_rect.position += atlas_rect.size; } } else if (storage->light_directional_get_shadow_mode(light_instance->light) == RS::LIGHT_DIRECTIONAL_SHADOW_PARALLEL_2_SPLITS) { atlas_rect.size.height /= 2; @@ -4382,10 +4385,8 @@ void RendererSceneRenderRD::_render_shadow_pass(RID p_light, RID p_shadow_atlas, _render_shadow_end(); //reblit Rect2 atlas_rect_norm = atlas_rect; - atlas_rect_norm.position.x /= float(atlas_size); - atlas_rect_norm.position.y /= float(atlas_size); - atlas_rect_norm.size.x /= float(atlas_size); - atlas_rect_norm.size.y /= float(atlas_size); + atlas_rect_norm.position /= float(atlas_size); + atlas_rect_norm.size /= float(atlas_size); storage->get_effects()->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), false); atlas_rect_norm.position += Vector2(dual_paraboloid_offset) * atlas_rect_norm.size; storage->get_effects()->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), true); diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index eb61af517a..db423b7d25 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -1197,6 +1197,7 @@ public: virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding, uint32_t p_view_count) override; virtual void gi_set_use_half_resolution(bool p_enable) override; + RID render_buffers_get_depth_texture(RID p_render_buffers); RID render_buffers_get_ao_texture(RID p_render_buffers); RID render_buffers_get_back_buffer_texture(RID p_render_buffers); RID render_buffers_get_back_depth_texture(RID p_render_buffers); diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp index ec0d25376f..ed87932762 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp @@ -4138,6 +4138,7 @@ void RendererStorageRD::particles_set_use_local_coordinates(RID p_particles, boo ERR_FAIL_COND(!particles); particles->use_local_coords = p_enable; + particles->dependency.changed_notify(DEPENDENCY_CHANGED_PARTICLES); } void RendererStorageRD::particles_set_fixed_fps(RID p_particles, int p_fps) { @@ -4352,10 +4353,8 @@ AABB RendererStorageRD::particles_get_current_aabb(RID p_particles) { total_amount *= particles->trail_bind_poses.size(); } - Vector<ParticleData> data; - data.resize(total_amount); - Vector<uint8_t> buffer = RD::get_singleton()->buffer_get_data(particles->particle_buffer); + ERR_FAIL_COND_V(buffer.size() != (int)(total_amount * sizeof(ParticleData)), AABB()); Transform3D inv = particles->emission_transform.affine_inverse(); @@ -4363,7 +4362,7 @@ AABB RendererStorageRD::particles_get_current_aabb(RID p_particles) { if (buffer.size()) { bool first = true; - const ParticleData *particle_data = (const ParticleData *)data.ptr(); + const ParticleData *particle_data = reinterpret_cast<const ParticleData *>(buffer.ptr()); for (int i = 0; i < total_amount; i++) { if (particle_data[i].active) { Vector3 pos = Vector3(particle_data[i].xform[12], particle_data[i].xform[13], particle_data[i].xform[14]); diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.h b/servers/rendering/renderer_rd/renderer_storage_rd.h index 02395a884f..4950b7d5e5 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.h +++ b/servers/rendering/renderer_rd/renderer_storage_rd.h @@ -621,7 +621,6 @@ private: float color[4]; float custom[3]; float lifetime; - uint32_t pad[3]; }; struct ParticlesFrameParams { diff --git a/servers/xr/xr_interface_extension.cpp b/servers/xr/xr_interface_extension.cpp index 315442fc57..7fdf90770d 100644 --- a/servers/xr/xr_interface_extension.cpp +++ b/servers/xr/xr_interface_extension.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "xr_interface_extension.h" +#include "servers/rendering/renderer_rd/renderer_storage_rd.h" #include "servers/rendering/renderer_storage.h" #include "servers/rendering/rendering_server_globals.h" @@ -246,17 +247,21 @@ void XRInterfaceExtension::notification(int p_what) { } RID XRInterfaceExtension::get_render_target_texture(RID p_render_target) { - RendererStorage *storage = RSG::storage; - ERR_FAIL_NULL_V_MSG(storage, RID(), "Renderer storage not setup"); + // In due time this will need to be enhance to return the correct INTERNAL RID for the chosen rendering engine. + // So once a GLES driver is implemented we'll return that and the implemented plugin needs to handle this correctly too. + RendererStorageRD *rd_storage = RendererStorageRD::base_singleton; + ERR_FAIL_NULL_V_MSG(rd_storage, RID(), "Renderer storage not setup"); - return storage->render_target_get_texture(p_render_target); + return rd_storage->render_target_get_rd_texture(p_render_target); } /* RID XRInterfaceExtension::get_render_target_depth(RID p_render_target) { - RendererStorage *storage = RSG::storage; - ERR_FAIL_NULL_V_MSG(storage, RID(), "Renderer storage not setup"); + // TODO implement this, the problem is that our depth texture isn't part of our render target as it is used for 3D rendering only + // but we don't have access to our render buffers from here.... + RendererSceneRenderRD * rd_scene = ?????; + ERR_FAIL_NULL_V_MSG(rd_scene, RID(), "Renderer scene render not setup"); - return storage->render_target_get_depth(p_render_target); + return rd_scene->render_buffers_get_depth_texture(????????????); } */ diff --git a/tests/test_physics_2d.cpp b/tests/test_physics_2d.cpp index f63b866c3e..f6619db8fd 100644 --- a/tests/test_physics_2d.cpp +++ b/tests/test_physics_2d.cpp @@ -199,7 +199,7 @@ protected: if (mb.is_valid()) { if (mb->is_pressed()) { - Point2 p(mb->get_position().x, mb->get_position().y); + Point2 p = mb->get_position(); if (mb->get_button_index() == 1) { ray_to = p; diff --git a/tests/test_physics_3d.cpp b/tests/test_physics_3d.cpp index 3d1dad6545..d839ae26b7 100644 --- a/tests/test_physics_3d.cpp +++ b/tests/test_physics_3d.cpp @@ -361,7 +361,7 @@ public: RID mesh_instance = vs->instance_create2(capsule_mesh, scenario); character = ps->body_create(); - ps->body_set_mode(character, PhysicsServer3D::BODY_MODE_DYNAMIC_LOCKED); + ps->body_set_mode(character, PhysicsServer3D::BODY_MODE_DYNAMIC_LINEAR); ps->body_set_space(character, space); //todo add space ps->body_add_shape(character, capsule_shape); diff --git a/tests/test_translation.h b/tests/test_translation.h index 52ff49bf9b..93c53bbbc9 100644 --- a/tests/test_translation.h +++ b/tests/test_translation.h @@ -35,6 +35,10 @@ #include "core/string/translation.h" #include "core/string/translation_po.h" +#ifdef TOOLS_ENABLED +#include "editor/import/resource_importer_csv_translation.h" +#endif + #include "thirdparty/doctest/doctest.h" namespace TestTranslation { @@ -145,6 +149,33 @@ TEST_CASE("[OptimizedTranslation] Generate from Translation and read messages") CHECK(messages.size() == 0); } +#ifdef TOOLS_ENABLED +TEST_CASE("[Translation] CSV import") { + Ref<ResourceImporterCSVTranslation> import_csv_translation = memnew(ResourceImporterCSVTranslation); + + Map<StringName, Variant> options; + options["compress"] = false; + options["delimiter"] = 0; + + List<String> gen_files; + + Error result = import_csv_translation->import(TestUtils::get_data_path("translations.csv"), + "", options, nullptr, &gen_files); + CHECK(result == OK); + CHECK(gen_files.size() == 2); + + for (const String &file : gen_files) { + Ref<Translation> translation = ResourceLoader::load(file); + CHECK(translation.is_valid()); + TranslationServer::get_singleton()->add_translation(translation); + } + + TranslationServer::get_singleton()->set_locale("de"); + + CHECK(Object().tr("GOOD_MORNING", "") == "Guten Morgen"); +} +#endif // TOOLS_ENABLED + } // namespace TestTranslation #endif // TEST_TRANSLATION_H diff --git a/thirdparty/README.md b/thirdparty/README.md index 990bd30320..2d24beec15 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -422,10 +422,6 @@ Collection of single-file libraries used in Godot components. * Upstream: https://research.activision.com/publications/archives/fast-filtering-of-reflection-probes File coeffs_const_8.txt (retrieved April 2020) * License: MIT -- `easing_equations.cpp` - * Upstream: http://robertpenner.com/easing/ via https://github.com/jesusgollonet/ofpennereasing (modified to fit Godot types) - * Version: git (af72c147c3a74e7e872aa28c7e2abfcced04fdce, 2008) + Godot types and style changes - * License: BSD-3-Clause - `fastlz.{c,h}` * Upstream: https://github.com/ariya/FastLZ * Version: 0.5.0 (4f20f54d46f5a6dd4fae4def134933369b7602d2, 2020) diff --git a/thirdparty/misc/easing_equations.cpp b/thirdparty/misc/easing_equations.cpp deleted file mode 100644 index ce32c1a362..0000000000 --- a/thirdparty/misc/easing_equations.cpp +++ /dev/null @@ -1,319 +0,0 @@ -/** - * Adapted from Penner Easing equations' C++ port. - * Source: https://github.com/jesusgollonet/ofpennereasing - * License: BSD-3-clause - */ - -#include "scene/animation/tween.h" - -const real_t pi = 3.1415926535898; - -/////////////////////////////////////////////////////////////////////////// -// linear -/////////////////////////////////////////////////////////////////////////// -namespace linear { -static real_t in(real_t t, real_t b, real_t c, real_t d) { - return c * t / d + b; -} - -static real_t out(real_t t, real_t b, real_t c, real_t d) { - return c * t / d + b; -} - -static real_t in_out(real_t t, real_t b, real_t c, real_t d) { - return c * t / d + b; -} - -static real_t out_in(real_t t, real_t b, real_t c, real_t d) { - return c * t / d + b; -} -}; // namespace linear -/////////////////////////////////////////////////////////////////////////// -// sine -/////////////////////////////////////////////////////////////////////////// -namespace sine { -static real_t in(real_t t, real_t b, real_t c, real_t d) { - return -c * cos(t / d * (pi / 2)) + c + b; -} - -static real_t out(real_t t, real_t b, real_t c, real_t d) { - return c * sin(t / d * (pi / 2)) + b; -} - -static real_t in_out(real_t t, real_t b, real_t c, real_t d) { - return -c / 2 * (cos(pi * t / d) - 1) + b; -} - -static real_t out_in(real_t t, real_t b, real_t c, real_t d) { - return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d); -} -}; // namespace sine -/////////////////////////////////////////////////////////////////////////// -// quint -/////////////////////////////////////////////////////////////////////////// -namespace quint { -static real_t in(real_t t, real_t b, real_t c, real_t d) { - return c * pow(t / d, 5) + b; -} - -static real_t out(real_t t, real_t b, real_t c, real_t d) { - return c * (pow(t / d - 1, 5) + 1) + b; -} - -static real_t in_out(real_t t, real_t b, real_t c, real_t d) { - t = t / d * 2; - if (t < 1) return c / 2 * pow(t, 5) + b; - return c / 2 * (pow(t - 2, 5) + 2) + b; -} - -static real_t out_in(real_t t, real_t b, real_t c, real_t d) { - return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d); -} -}; // namespace quint -/////////////////////////////////////////////////////////////////////////// -// quart -/////////////////////////////////////////////////////////////////////////// -namespace quart { -static real_t in(real_t t, real_t b, real_t c, real_t d) { - return c * pow(t / d, 4) + b; -} - -static real_t out(real_t t, real_t b, real_t c, real_t d) { - return -c * (pow(t / d - 1, 4) - 1) + b; -} - -static real_t in_out(real_t t, real_t b, real_t c, real_t d) { - t = t / d * 2; - if (t < 1) return c / 2 * pow(t, 4) + b; - return -c / 2 * (pow(t - 2, 4) - 2) + b; -} - -static real_t out_in(real_t t, real_t b, real_t c, real_t d) { - return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d); -} -}; // namespace quart -/////////////////////////////////////////////////////////////////////////// -// quad -/////////////////////////////////////////////////////////////////////////// -namespace quad { -static real_t in(real_t t, real_t b, real_t c, real_t d) { - return c * pow(t / d, 2) + b; -} - -static real_t out(real_t t, real_t b, real_t c, real_t d) { - t = t / d; - return -c * t * (t - 2) + b; -} - -static real_t in_out(real_t t, real_t b, real_t c, real_t d) { - t = t / d * 2; - if (t < 1) return c / 2 * pow(t, 2) + b; - return -c / 2 * ((t - 1) * (t - 3) - 1) + b; -} - -static real_t out_in(real_t t, real_t b, real_t c, real_t d) { - return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d); -} -}; // namespace quad -/////////////////////////////////////////////////////////////////////////// -// expo -/////////////////////////////////////////////////////////////////////////// -namespace expo { -static real_t in(real_t t, real_t b, real_t c, real_t d) { - if (t == 0) return b; - return c * pow(2, 10 * (t / d - 1)) + b - c * 0.001; -} - -static real_t out(real_t t, real_t b, real_t c, real_t d) { - if (t == d) return b + c; - return c * 1.001 * (-pow(2, -10 * t / d) + 1) + b; -} - -static real_t in_out(real_t t, real_t b, real_t c, real_t d) { - if (t == 0) return b; - if (t == d) return b + c; - t = t / d * 2; - if (t < 1) return c / 2 * pow(2, 10 * (t - 1)) + b - c * 0.0005; - return c / 2 * 1.0005 * (-pow(2, -10 * (t - 1)) + 2) + b; -} - -static real_t out_in(real_t t, real_t b, real_t c, real_t d) { - return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d); -} -}; // namespace expo -/////////////////////////////////////////////////////////////////////////// -// elastic -/////////////////////////////////////////////////////////////////////////// -namespace elastic { -static real_t in(real_t t, real_t b, real_t c, real_t d) { - if (t == 0) return b; - if ((t /= d) == 1) return b + c; - float p = d * 0.3f; - float a = c; - float s = p / 4; - float postFix = a * pow(2, 10 * (t -= 1)); // this is a fix, again, with post-increment operators - return -(postFix * sin((t * d - s) * (2 * pi) / p)) + b; -} - -static real_t out(real_t t, real_t b, real_t c, real_t d) { - if (t == 0) return b; - if ((t /= d) == 1) return b + c; - float p = d * 0.3f; - float a = c; - float s = p / 4; - return (a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p) + c + b); -} - -static real_t in_out(real_t t, real_t b, real_t c, real_t d) { - if (t == 0) return b; - if ((t /= d / 2) == 2) return b + c; - float p = d * (0.3f * 1.5f); - float a = c; - float s = p / 4; - - if (t < 1) { - float postFix = a * pow(2, 10 * (t -= 1)); // postIncrement is evil - return -0.5f * (postFix * sin((t * d - s) * (2 * pi) / p)) + b; - } - float postFix = a * pow(2, -10 * (t -= 1)); // postIncrement is evil - return postFix * sin((t * d - s) * (2 * pi) / p) * 0.5f + c + b; -} - -static real_t out_in(real_t t, real_t b, real_t c, real_t d) { - return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d); -} -}; // namespace elastic -/////////////////////////////////////////////////////////////////////////// -// cubic -/////////////////////////////////////////////////////////////////////////// -namespace cubic { -static real_t in(real_t t, real_t b, real_t c, real_t d) { - t /= d; - return c * t * t * t + b; -} - -static real_t out(real_t t, real_t b, real_t c, real_t d) { - t = t / d - 1; - return c * (t * t * t + 1) + b; -} - -static real_t in_out(real_t t, real_t b, real_t c, real_t d) { - t /= d / 2; - if (t < 1) return c / 2 * t * t * t + b; - t -= 2; - return c / 2 * (t * t * t + 2) + b; -} - -static real_t out_in(real_t t, real_t b, real_t c, real_t d) { - return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d); -} -}; // namespace cubic -/////////////////////////////////////////////////////////////////////////// -// circ -/////////////////////////////////////////////////////////////////////////// -namespace circ { -static real_t in(real_t t, real_t b, real_t c, real_t d) { - t /= d; - return -c * (sqrt(1 - t * t) - 1) + b; -} - -static real_t out(real_t t, real_t b, real_t c, real_t d) { - t = t / d - 1; - return c * sqrt(1 - t * t) + b; -} - -static real_t in_out(real_t t, real_t b, real_t c, real_t d) { - t /= d / 2; - if (t < 1) { - return -c / 2 * (sqrt(1 - t * t) - 1) + b; - } - t -= 2; - return c / 2 * (sqrt(1 - t * t) + 1) + b; -} - -static real_t out_in(real_t t, real_t b, real_t c, real_t d) { - return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d); -} -}; // namespace circ -/////////////////////////////////////////////////////////////////////////// -// bounce -/////////////////////////////////////////////////////////////////////////// -namespace bounce { -static real_t out(real_t t, real_t b, real_t c, real_t d); - -static real_t in(real_t t, real_t b, real_t c, real_t d) { - return c - out(d - t, 0, c, d) + b; -} - -static real_t out(real_t t, real_t b, real_t c, real_t d) { - if ((t /= d) < (1 / 2.75f)) { - return c * (7.5625f * t * t) + b; - } else if (t < (2 / 2.75f)) { - float postFix = t -= (1.5f / 2.75f); - return c * (7.5625f * (postFix)*t + .75f) + b; - } else if (t < (2.5 / 2.75)) { - float postFix = t -= (2.25f / 2.75f); - return c * (7.5625f * (postFix)*t + .9375f) + b; - } else { - float postFix = t -= (2.625f / 2.75f); - return c * (7.5625f * (postFix)*t + .984375f) + b; - } -} - -static real_t in_out(real_t t, real_t b, real_t c, real_t d) { - return (t < d / 2) ? in(t * 2, b, c / 2, d) : out((t * 2) - d, b + c / 2, c / 2, d); -} - -static real_t out_in(real_t t, real_t b, real_t c, real_t d) { - return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d); -} -}; // namespace bounce -/////////////////////////////////////////////////////////////////////////// -// back -/////////////////////////////////////////////////////////////////////////// -namespace back { -static real_t in(real_t t, real_t b, real_t c, real_t d) { - float s = 1.70158f; - float postFix = t /= d; - return c * (postFix)*t * ((s + 1) * t - s) + b; -} - -static real_t out(real_t t, real_t b, real_t c, real_t d) { - float s = 1.70158f; - t = t / d - 1; - return c * (t * t * ((s + 1) * t + s) + 1) + b; -} - -static real_t in_out(real_t t, real_t b, real_t c, real_t d) { - float s = 1.70158f * 1.525f; - t /= d / 2; - if (t < 1) return c / 2 * (t * t * ((s + 1) * t - s)) + b; - t -= 2; - return c / 2 * (t * t * ((s + 1) * t + s) + 2) + b; -} - -static real_t out_in(real_t t, real_t b, real_t c, real_t d) { - return (t < d / 2) ? out(t * 2, b, c / 2, d) : in((t * 2) - d, b + c / 2, c / 2, d); -} -}; // namespace back - -Tween::interpolater Tween::interpolaters[Tween::TRANS_MAX][Tween::EASE_MAX] = { - { &linear::in, &linear::out, &linear::in_out, &linear::out_in }, - { &sine::in, &sine::out, &sine::in_out, &sine::out_in }, - { &quint::in, &quint::out, &quint::in_out, &quint::out_in }, - { &quart::in, &quart::out, &quart::in_out, &quart::out_in }, - { &quad::in, &quad::out, &quad::in_out, &quad::out_in }, - { &expo::in, &expo::out, &expo::in_out, &expo::out_in }, - { &elastic::in, &elastic::out, &elastic::in_out, &elastic::out_in }, - { &cubic::in, &cubic::out, &cubic::in_out, &cubic::out_in }, - { &circ::in, &circ::out, &circ::in_out, &circ::out_in }, - { &bounce::in, &bounce::out, &bounce::in_out, &bounce::out_in }, - { &back::in, &back::out, &back::in_out, &back::out_in }, -}; - -real_t Tween::run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d) { - - interpolater cb = interpolaters[p_trans_type][p_ease_type]; - ERR_FAIL_COND_V(cb == NULL, b); - return cb(t, b, c, d); -} |