Browse Source

Merge pull request #39280 from akien-mga/3.2-cherrypicks

Cherry-picks for the 3.2 branch (future 3.2.2) - 7th batch
Rémi Verschelde 5 years ago
parent
commit
ed1fc50bb9
56 changed files with 708 additions and 617 deletions
  1. 1 0
      AUTHORS.md
  2. 1 1
      COPYRIGHT.txt
  3. 60 27
      DONORS.md
  4. 9 4
      SConstruct
  5. 9 3
      core/os/os.cpp
  6. 40 30
      core/project_settings.cpp
  7. 4 0
      doc/classes/AnimationTree.xml
  8. 0 11
      doc/classes/BitmapFont.xml
  9. 4 4
      doc/classes/DirectionalLight.xml
  10. 1 1
      doc/classes/DynamicFontData.xml
  11. 4 8
      doc/classes/EditorInterface.xml
  12. 11 0
      doc/classes/Font.xml
  13. 1 1
      doc/classes/ProjectSettings.xml
  14. 6 66
      doc/tools/makerst.py
  15. 0 4
      drivers/windows/dir_access_windows.cpp
  16. 14 9
      editor/create_dialog.cpp
  17. 3 3
      editor/editor_autoload_settings.cpp
  18. 3 6
      editor/editor_node.cpp
  19. 1 1
      editor/editor_node.h
  20. 7 0
      editor/editor_plugin.cpp
  21. 1 0
      editor/editor_plugin.h
  22. 1 1
      editor/import/resource_importer_layered_texture.cpp
  23. 2 1
      editor/import/resource_importer_texture_atlas.cpp
  24. 1 1
      editor/plugins/canvas_item_editor_plugin.cpp
  25. 14 9
      editor/plugins/script_text_editor.cpp
  26. 18 1
      editor/plugins/tile_map_editor_plugin.cpp
  27. 1 0
      editor/plugins/tile_map_editor_plugin.h
  28. 25 9
      editor/plugins/tile_set_editor_plugin.cpp
  29. 1 0
      editor/plugins/tile_set_editor_plugin.h
  30. 6 0
      editor/project_manager.cpp
  31. 5 0
      editor/scene_tree_dock.cpp
  32. 4 3
      editor/script_create_dialog.cpp
  33. 1 1
      methods.py
  34. 5 2
      modules/gdscript/gdscript_function.cpp
  35. 2 0
      modules/mono/csharp_script.cpp
  36. 1 1
      modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
  37. 1 1
      modules/mono/mono_gd/gd_mono.cpp
  38. 6 0
      modules/visual_script/visual_script_editor.cpp
  39. 1 1
      modules/xatlas_unwrap/register_types.cpp
  40. 42 4
      platform/android/export/export.cpp
  41. 5 0
      platform/android/java/app/build.gradle
  42. 16 0
      platform/android/plugin/godot_plugin_config.h
  43. 8 1
      platform/osx/joypad_osx.cpp
  44. 23 14
      platform/windows/joypad_windows.cpp
  45. 3 0
      platform/windows/os_windows.cpp
  46. 1 1
      scene/3d/light.cpp
  47. 1 1
      scene/gui/color_picker.cpp
  48. 7 1
      scene/gui/rich_text_label.cpp
  49. 8 0
      scene/gui/scroll_bar.cpp
  50. 2 1
      scene/gui/scroll_bar.h
  51. 1 1
      scene/resources/dynamic_font.cpp
  52. 1 2
      scene/resources/font.cpp
  53. 1 1
      thirdparty/README.md
  54. 1 1
      thirdparty/xatlas/LICENSE
  55. 277 352
      thirdparty/xatlas/xatlas.cpp
  56. 36 27
      thirdparty/xatlas/xatlas.h

+ 1 - 0
AUTHORS.md

@@ -95,6 +95,7 @@ name is available.
     Indah Sylvia (ISylvox)
     J08nY
     Jakub Grzesik (kubecz3k)
+    James Buck (jbuck3)
     Jérôme Gully (Nutriz)
     Joan Fons Sanchez (JFonS)
     Johan Manuel (29jm)

+ 1 - 1
COPYRIGHT.txt

@@ -350,7 +350,7 @@ License: Expat
 
 Files: ./thirdparty/xatlas/
 Comment: xatlas
-Copyright: 2018, Jonathan Young
+Copyright: 2018-2020, Jonathan Young
   2013, Thekla, Inc
   2006, NVIDIA Corporation, Ignacio Castano
 License: Expat

+ 60 - 27
DONORS.md

@@ -24,11 +24,12 @@ generous deed immortalized in the next stable release of Godot Engine.
 
     AD Ford
     Alan Beauchamp
-    Anand Mallik
+    albinaask
     Andrew Dunai
     Brandon Lamb
     Christian Baune
     Christopher Montesano
+    Darius Pranskus
     Darkhan Baimyrza
     Darrin Massena
     Dov Zimring
@@ -41,15 +42,18 @@ generous deed immortalized in the next stable release of Godot Engine.
     Jasper Brooks
     Javary Co.
     Jeffery Chiu
+    John Benard (Linuxydable)
     Justin Arnold
     Justo Delgado Baudí
     Kyle Szklenski
+    Marcel Kräml
     Matthieu Huvé
     Maxim Karsten
     Mike King
     Nathan Warden
     Neal Gompa (Conan Kudo)
     Péter Magyar
+    Ronnie Cheng
     Slobodan Milnovic
     Stephan Lanfermann
     Steve
@@ -58,12 +62,14 @@ generous deed immortalized in the next stable release of Godot Engine.
 
 ## Gold donors
 
+    Bjarke
     David Gehrig
     David Graham
     David Snopek
     Ed Morley
     Florian Rämisch
     Jakub Grzesik
+    HardRound
     Manuele Finocchiaro
     Officine Pixel S.n.c.
     Ronan Zeegers
@@ -92,10 +98,10 @@ generous deed immortalized in the next stable release of Godot Engine.
     Maciej Pendolski
     Matthew Hillier
     Mohamed Ikbel Boulabiar
-    Mored4u
     Rene
     Retro Village
     Rob Messick
+    Roland Fredenhagen
     Ryan Badour
     Sandro Jenny
     Scott Wadden
@@ -108,16 +114,19 @@ generous deed immortalized in the next stable release of Godot Engine.
 
     Alex Khayrullin
     alice gambrell
+    Barugon
     Chris Goddard
     Chris Serino
     Christian Padilla
     Conrad Curry
     Craig Smith
     Darrian Little
+    Hoai Nam Tran
     Horváth Péter
-    Ivan Trombley
+    Jamal Aboudrar
     Joan Fons
     Joshua Flores
+    Leo Fidel R Liban
     Petr Malac
     Rami
     Rob
@@ -149,20 +158,20 @@ generous deed immortalized in the next stable release of Godot Engine.
     Christoph Schröder
     Codee Leaf
     Cody Parker
-    Coldragon
     Craig Ostrin
+    curtis Kramer
     D
     Easypete
+    Edgar Sun
     Eric Monson
     Eugenio Hugo Salgüero Jáñez
-    Fain
     flesk
     Gary Hulst
     gavlig
     GGGames.org
     Guilherme Felipe de C. G. da Silva
-    Halom Vered
     Heath Hayes
+    Hu Hund
     Isaac Clausman
     Jared White
     Jeff Nyte
@@ -171,18 +180,21 @@ generous deed immortalized in the next stable release of Godot Engine.
     Jose Malheiro
     Joshua Lesperance
     Juan Velandia
+    Julian Todd
     Juraj Móza
     Kelteseth
     kickmaniac
     kinfox
     Lain Ballard
     Marcelo Dornbusch Lopes
+    Marcelo Henrique Gonçalves
     Markus Fehr
     Markus Wiesner
     Martin Eigel
     Matt Eunson
     m kaersten
     MuffinManKen
+    Nick Abousselam
     Oliver Dick
     Oscar Campos
     Patrick Ting
@@ -197,14 +209,16 @@ generous deed immortalized in the next stable release of Godot Engine.
     Samuel Judd
     Scott Pilet
     Sean Morgan
+    Sean Robertson
+    Sébastien
     Serban Serafimescu
-    Sindre Sømme
     SleepCircle
     spilldata
     Stoned Xander
     TheLevelOfDetail .
     Thomas Kurz
     Tobias Bocanegra
+    Trent Fehl
     Urho
     William Foster
     Zhou Tuizhi
@@ -214,6 +228,7 @@ generous deed immortalized in the next stable release of Godot Engine.
 ## Silver donors
 
     1D_Inc
+    Aaron Winter
     Abraham Haskins
     Acheron
     Adam
@@ -229,7 +244,6 @@ generous deed immortalized in the next stable release of Godot Engine.
     Agustinus Arya
     Aidan O'Flannagain
     Aki Mimoto
-    Alan Mervitz
     Alan Stice
     Albin Jonasson Svärdsby
     Alder Stefano
@@ -237,14 +251,17 @@ generous deed immortalized in the next stable release of Godot Engine.
     Alessandro Senese
     Alexander Erlemann
     alex clavelle
+    Alfred Reinold Baudisch
     Allan Davis
     Allen Schade
-    Andreas Evers
     Andreas Krampitz
+    Andres Hernandez
     André Simões
+    andrew james morris
+    Andrew Mansuetti
     Andrew Thomas
+    Ano Nim
     Anthony Avina
-    Anthony Staunton
     AP Condomines
     Arda Erol
     Armin Preiml
@@ -252,10 +269,11 @@ generous deed immortalized in the next stable release of Godot Engine.
     Arthur S. Muszynski
     Asger
     Ashley Claymore
+    Ashton Scott Snapp
     Aubrey Falconer
-    Avencherus
     B A
     Balázs Batári
+    Bartosz Bielecki
     Benedikt
     Ben Vercammen
     Bernd Jänichen
@@ -265,10 +283,8 @@ generous deed immortalized in the next stable release of Godot Engine.
     Bobby CC Wong
     Bram
     brian
-    bugcaptor
     Burney Waring
     Cameron Meyer
-    Carlo Sitaro
     Carl van der Geest
     Carwyn Edwards
     Cas Brugman
@@ -289,7 +305,7 @@ generous deed immortalized in the next stable release of Godot Engine.
     Daniel Tebbutt
     David May
     David Woodard
-    DiCola Jamn
+    Dimitri Stanojevic
     Dominic Cooney
     Dominik Wetzel
     Donn Eddy
@@ -301,24 +317,25 @@ generous deed immortalized in the next stable release of Godot Engine.
     Eduardo Teixeira
     Edward Herbert
     Edward Swartz
+    Eelco F Hillenius
     Egon Elbre
     Elgenzay
     Elias Nykrem
-    Elmeri '- Duy Kevin Nguyen
     Ephemeral
     Eric Ellingson
-    Eric Rogers
     Eric Williams
     Erkki Seppälä
     Evan Rose
+    Fain
+    Faisal Alkubaisi
     Fancy Ants Studios
     Fekinox
     Felix Bohmann
     Felix Kollmann
     Flaredown
     Forty Doubleu
-    fox
     FuDiggity
+    Frank
     Gadzhi Kharkharov
     gamedev by Celio
     Gary Thomas
@@ -328,16 +345,17 @@ generous deed immortalized in the next stable release of Godot Engine.
     Greyson Richey
     Grid
     Guillaume Audirac
+    Guillaume Pham Ngoc
     Guldoman
     Hal A
     Heribert Hirth
     Hunter Jones
     Hylpher
-    Ichiro Dohi
     Iiari
     iKlem
     IndustrialRobot
     Ivan Nikolaev
+    Jackson Harmer
     Jacob
     Jaiden Gerig
     Jaime Ruiz-Borau Vizárraga
@@ -346,9 +364,11 @@ generous deed immortalized in the next stable release of Godot Engine.
     Janders
     Jannik Gröger
     JARKKO PARVIAINEN
+    Jean-Baptiste LEPESME
     Jeff Hungerford
     Jennifer Graves
     Jesse Dubay
+    Joe Alden
     Joel Fivat
     Joel Höglund
     Joel Setterberg
@@ -363,14 +383,18 @@ generous deed immortalized in the next stable release of Godot Engine.
     Jonathan G
     Jon Bonazza
     Jon Sully
+    Jordy Goodridge
+    Jorge Antunes
     Jorge Caballero
     Jose Aleman
     Jose C. Rubio
     Joseph Catrambone
     Josh Mitchell
+    Joshua Southerland
     Juanfran
     Judd
     Julian Murgia
+    June Little
     JungleRobba
     Justin Hamilton
     Justin Spedding
@@ -382,7 +406,6 @@ generous deed immortalized in the next stable release of Godot Engine.
     Kent Jofur
     Kevin McPhillips
     Kiri Jolly
-    Kiyohiro Kawamura (kyorohiro)
     Kjetil Haugland
     Klagsam
     KsyTek Games
@@ -390,26 +413,24 @@ generous deed immortalized in the next stable release of Godot Engine.
     kycho
     Kyle Appelgate
     Laurent Tréguier
+    Leonard Meagher
     Leonardo Dimano
     Levi Lindsey
     Lin Chear
     Linus Lind Lundgren
     Lionel Gaillard
-    Lukáš Rendvanský
     Luigi Renna
     LunaticInAHat
     Lurkars
     Major Haul
     Malcolm
-    Malik Ahmed
-    Malik Nejer
+    Marco Lardelli
     Markus Michael Egger
     Martin Holas
     Martin Liška
     Marvin
-    Mathieu Rimelen
+    Mathieu
     Matt Edwards
-    Matthew Little
     Mauro Pellegrini
     Max Fiedler
     Maxime Blade
@@ -426,7 +447,7 @@ generous deed immortalized in the next stable release of Godot Engine.
     Mikayla
     Mike Birkhead
     Mike Cunningham
-    Mitchell J. Wagner
+    Molinghu
     MoM
     Mored4u
     Nathan Fish
@@ -435,8 +456,10 @@ generous deed immortalized in the next stable release of Godot Engine.
     Neil Blakey-Milner
     Neil Wang
     Nerdforge
+    Nerdyninja
     Nicholas
     Nicholas Girga
+    Nick Allen
     Nick Macholl
     Niclas Eriksen
     Nicolás Montaña
@@ -453,11 +476,12 @@ generous deed immortalized in the next stable release of Godot Engine.
     Paweł Kowal
     Pedro Assuncao
     Penguin
+    Peter
     Philip Cohoe
     Point08
+    pwab
     Rad Cat
     Rafa Laguna
-    rainerLinux
     Remi Rampin
     Rémi Verschelde
     Ricardo Alcantara
@@ -470,6 +494,7 @@ generous deed immortalized in the next stable release of Godot Engine.
     Roman Tinkov
     Ronald Ho Hip (CrimsonZA)
     Ronan
+    Ronny Mühle
     Ryan Groom
     Ryan Hentz
     Sam Edson
@@ -483,6 +508,7 @@ generous deed immortalized in the next stable release of Godot Engine.
     Shane
     Shane Sicienski
     Shane Spoor
+    Shiomi '- Duy Kevin Nguyen
     Siim Raidma
     Simon Jonas Larsen
     Simon Wenner
@@ -490,17 +516,21 @@ generous deed immortalized in the next stable release of Godot Engine.
     SK
     smbe19
     smo1704
+    soft circles
+    Squirrel
     Stefano Caronia
     Steve Cloete
     Svenne Krap
     Taylor Fahlman
     Terry
     tezuvholovdr
+    TheVoiceInMyHead
     thomas
     Thomas Bechtold
     Thomas Detoy
     Thomas Kelly
     Tim Drumheller
+    Tim Erskine
     Timothy B. MacDonald
     Title Plinsut
     Tobbun
@@ -516,6 +546,7 @@ generous deed immortalized in the next stable release of Godot Engine.
     Tyler Compton
     Tyler Stafos
     UltyX
+    Valentí Gàmez
     Vaughan Ling
     Victor
     Vigilant Watch
@@ -525,10 +556,12 @@ generous deed immortalized in the next stable release of Godot Engine.
     werner mendizabal
     Wiley Thompson
     Will
-    William Hogben
     Wyatt Goodin
     Yegor
+    Yuri LaPointe
     Yuri Sizov
+    Zgegnesh Hemomancer
+    郝晨煜
 
 ## Bronze donors
 

+ 9 - 4
SConstruct

@@ -177,12 +177,16 @@ for k in platform_opts.keys():
     for o in opt_list:
         opts.Add(o)
 
+# Update the environment now as the "custom_modules" option may be
+# defined in a file rather than specified via the command line.
+opts.Update(env_base)
+
 # Detect modules.
 modules_detected = OrderedDict()
 module_search_paths = ["modules"]  # Built-in path.
 
-if ARGUMENTS.get("custom_modules"):
-    paths = ARGUMENTS.get("custom_modules").split(",")
+if env_base["custom_modules"]:
+    paths = env_base["custom_modules"].split(",")
     for p in paths:
         try:
             module_search_paths.append(methods.convert_custom_modules_path(p))
@@ -213,8 +217,9 @@ for name, path in modules_detected.items():
 
 methods.write_modules(modules_detected)
 
-opts.Update(env_base)  # update environment
-Help(opts.GenerateHelpText(env_base))  # generate help
+# Update the environment again after all the module options are added.
+opts.Update(env_base)
+Help(opts.GenerateHelpText(env_base))
 
 # add default include paths
 

+ 9 - 3
core/os/os.cpp

@@ -728,19 +728,25 @@ PoolStringArray OS::get_connected_midi_inputs() {
 		return MIDIDriver::get_singleton()->get_connected_inputs();
 
 	PoolStringArray list;
-	return list;
+	ERR_FAIL_V_MSG(list, vformat("MIDI input isn't supported on %s.", OS::get_singleton()->get_name()));
 }
 
 void OS::open_midi_inputs() {
 
-	if (MIDIDriver::get_singleton())
+	if (MIDIDriver::get_singleton()) {
 		MIDIDriver::get_singleton()->open();
+	} else {
+		ERR_PRINT(vformat("MIDI input isn't supported on %s.", OS::get_singleton()->get_name()));
+	}
 }
 
 void OS::close_midi_inputs() {
 
-	if (MIDIDriver::get_singleton())
+	if (MIDIDriver::get_singleton()) {
 		MIDIDriver::get_singleton()->close();
+	} else {
+		ERR_PRINT(vformat("MIDI input isn't supported on %s.", OS::get_singleton()->get_name()));
+	}
 }
 
 OS::OS() {

+ 40 - 30
core/project_settings.cpp

@@ -307,10 +307,16 @@ void ProjectSettings::_convert_to_last_version(int p_from_version) {
  * using the following merit order:
  *  - If using NetworkClient, try to lookup project file or fail.
  *  - If --main-pack was passed by the user (`p_main_pack`), load it or fail.
- *  - Search for .pck file matching binary name. There are two possibilities:
- *    o exec_path.get_basename() + '.pck' (e.g. 'win_game.exe' -> 'win_game.pck')
- *    o exec_path + '.pck' (e.g. 'linux_game' -> 'linux_game.pck')
- *    For each tentative, if the file exists, load it or fail.
+ *  - Search for project PCKs automatically. For each step we try loading a potential
+ *    PCK, and if it doesn't work, we proceed to the next step. If any step succeeds,
+ *    we try loading the project settings, and abort if it fails. Steps:
+ *    o Bundled PCK in the executable.
+ *    o [macOS only] PCK with same basename as the binary in the .app resource dir.
+ *    o PCK with same basename as the binary in the binary's directory. We handle both
+ *      changing the extension to '.pck' (e.g. 'win_game.exe' -> 'win_game.pck') and
+ *      appending '.pck' to the binary name (e.g. 'linux_game' -> 'linux_game.pck').
+ *    o PCK with the same basename as the binary in the current working directory.
+ *      Same as above for the two possible PCK file names.
  *  - On relevant platforms (Android/iOS), lookup project file in OS resource path.
  *    If found, load it or fail.
  *  - Lookup project file in passed `p_path` (--path passed by the user), i.e. we
@@ -354,65 +360,68 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b
 	String exec_path = OS::get_singleton()->get_executable_path();
 
 	if (exec_path != "") {
-		// Attempt with exec_name.pck
-		// (This is the usual case when distributing a Godot game.)
+		// We do several tests sequentially until one succeeds to find a PCK,
+		// and if so we attempt loading it at the end.
 
-		// Based on the OS, it can be the exec path + '.pck' (Linux w/o extension, macOS in .app bundle)
-		// or the exec path's basename + '.pck' (Windows).
-		// We need to test both possibilities as extensions for Linux binaries are optional
-		// (so both 'mygame.bin' and 'mygame' should be able to find 'mygame.pck').
+		// Attempt with PCK bundled into executable.
+		bool found = _load_resource_pack(exec_path);
 
+		// Attempt with exec_name.pck.
+		// (This is the usual case when distributing a Godot game.)
 		String exec_dir = exec_path.get_base_dir();
 		String exec_filename = exec_path.get_file();
 		String exec_basename = exec_filename.get_basename();
 
-		// Attempt with PCK bundled into executable
-		bool found = _load_resource_pack(exec_path);
+		// Based on the OS, it can be the exec path + '.pck' (Linux w/o extension, macOS in .app bundle)
+		// or the exec path's basename + '.pck' (Windows).
+		// We need to test both possibilities as extensions for Linux binaries are optional
+		// (so both 'mygame.bin' and 'mygame' should be able to find 'mygame.pck').
 
 #ifdef OSX_ENABLED
 		if (!found) {
-			// Attempt to load PCK from macOS .app bundle resources
+			// Attempt to load PCK from macOS .app bundle resources.
 			found = _load_resource_pack(OS::get_singleton()->get_bundle_resource_dir().plus_file(exec_basename + ".pck"));
 		}
 #endif
 
 		if (!found) {
-			// Try to load data pack at the location of the executable
-			// As mentioned above, we have two potential names to attempt
+			// Try to load data pack at the location of the executable.
+			// As mentioned above, we have two potential names to attempt.
 			found = _load_resource_pack(exec_dir.plus_file(exec_basename + ".pck")) || _load_resource_pack(exec_dir.plus_file(exec_filename + ".pck"));
+		}
 
-			if (!found) {
-				// If we couldn't find them next to the executable, we attempt
-				// the current working directory. Same story, two tests.
-				found = _load_resource_pack(exec_basename + ".pck") || _load_resource_pack(exec_filename + ".pck");
-			}
+		if (!found) {
+			// If we couldn't find them next to the executable, we attempt
+			// the current working directory. Same story, two tests.
+			found = _load_resource_pack(exec_basename + ".pck") || _load_resource_pack(exec_filename + ".pck");
 		}
 
-		// If we opened our package, try and load our project
+		// If we opened our package, try and load our project.
 		if (found) {
 			Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary");
 			if (err == OK) {
-				// Load override from location of executable
-				// Optional, we don't mind if it fails
+				// Load override from location of the executable.
+				// Optional, we don't mind if it fails.
 				_load_settings_text(exec_path.get_base_dir().plus_file("override.cfg"));
 			}
 			return err;
 		}
 	}
 
-	// Try to use the filesystem for files, according to OS. (only Android -when reading from pck- and iOS use this)
+	// Try to use the filesystem for files, according to OS.
+	// (Only Android -when reading from pck- and iOS use this.)
 
 	if (OS::get_singleton()->get_resource_dir() != "") {
 		// OS will call ProjectSettings->get_resource_path which will be empty if not overridden!
 		// If the OS would rather use a specific location, then it will not be empty.
 		resource_path = OS::get_singleton()->get_resource_dir().replace("\\", "/");
 		if (resource_path != "" && resource_path[resource_path.length() - 1] == '/') {
-			resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end
+			resource_path = resource_path.substr(0, resource_path.length() - 1); // Chop end.
 		}
 
 		Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary");
 		if (err == OK) {
-			// Optional, we don't mind if it fails
+			// Optional, we don't mind if it fails.
 			_load_settings_text("res://override.cfg");
 		}
 		return err;
@@ -433,7 +442,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b
 	while (true) {
 		err = _load_settings_text_or_binary(current_dir.plus_file("project.godot"), current_dir.plus_file("project.binary"));
 		if (err == OK) {
-			// Optional, we don't mind if it fails
+			// Optional, we don't mind if it fails.
 			_load_settings_text(current_dir.plus_file("override.cfg"));
 			candidate = current_dir;
 			found = true;
@@ -452,14 +461,15 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b
 	}
 
 	resource_path = candidate;
-	resource_path = resource_path.replace("\\", "/"); // windows path to unix path just in case
+	resource_path = resource_path.replace("\\", "/"); // Windows path to Unix path just in case.
 	memdelete(d);
 
 	if (!found)
 		return err;
 
-	if (resource_path.length() && resource_path[resource_path.length() - 1] == '/')
-		resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end
+	if (resource_path.length() && resource_path[resource_path.length() - 1] == '/') {
+		resource_path = resource_path.substr(0, resource_path.length() - 1); // Chop end.
+	}
 
 	return OK;
 }

+ 4 - 0
doc/classes/AnimationTree.xml

@@ -4,6 +4,7 @@
 		A node to be used for advanced animation transitions in an [AnimationPlayer].
 	</brief_description>
 	<description>
+		Note: When linked with an [AnimationPlayer], several properties and methods of the corresponding [AnimationPlayer] will not function as expected. Playback and transitions should be handled using only the [AnimationTree] and its constituent [AnimationNode](s). The [AnimationPlayer] node should be used solely for adding, deleting, and editing animations.
 	</description>
 	<tutorials>
 		<link>https://docs.godotengine.org/en/latest/tutorials/animation/animation_tree.html</link>
@@ -23,6 +24,7 @@
 			<return type="Transform">
 			</return>
 			<description>
+				Retrieve the motion of the [member root_motion_track] as a [Transform] that can be used elsewhere. If [member root_motion_track] is not a path to a track of type [constant Animation.TYPE_TRANSFORM], returns an identity transformation.
 			</description>
 		</method>
 		<method name="rename_parameter">
@@ -47,6 +49,8 @@
 			The process mode of this [AnimationTree]. See [enum AnimationProcessMode] for available modes.
 		</member>
 		<member name="root_motion_track" type="NodePath" setter="set_root_motion_track" getter="get_root_motion_track" default="NodePath(&quot;&quot;)">
+			The path to the Animation track used for root motion. Paths must be valid scene-tree paths to a node, and must be specified starting from the parent node of the node that will reproduce the animation. To specify a track that controls properties or bones, append its name after the path, separated by [code]":"[/code]. For example, [code]"character/skeleton:ankle"[/code] or [code]"character/mesh:transform/local"[/code].
+			If the track has type [constant Animation.TYPE_TRANSFORM], the transformation will be cancelled visually, and the animation will appear to stay in place.
 		</member>
 		<member name="tree_root" type="AnimationNode" setter="set_tree_root" getter="get_tree_root">
 			The root animation node of this [AnimationTree]. See [AnimationNode].

+ 0 - 11
doc/classes/BitmapFont.xml

@@ -65,17 +65,6 @@
 				Creates a BitmapFont from the [code]*.fnt[/code] file at [code]path[/code].
 			</description>
 		</method>
-		<method name="get_char_size" qualifiers="const">
-			<return type="Vector2">
-			</return>
-			<argument index="0" name="char" type="int">
-			</argument>
-			<argument index="1" name="next" type="int" default="0">
-			</argument>
-			<description>
-				Returns the size of a character, optionally taking kerning into account if the next character is provided.
-			</description>
-		</method>
 		<method name="get_kerning_pair" qualifiers="const">
 			<return type="int">
 			</return>

+ 4 - 4
doc/classes/DirectionalLight.xml

@@ -43,19 +43,19 @@
 	</members>
 	<constants>
 		<constant name="SHADOW_ORTHOGONAL" value="0" enum="ShadowMode">
-			Renders the entire scene's shadow map from an orthogonal point of view. May result in blockier shadows on close objects.
+			Renders the entire scene's shadow map from an orthogonal point of view. This is the fastest directional shadow mode. May result in blurrier shadows on close objects.
 		</constant>
 		<constant name="SHADOW_PARALLEL_2_SPLITS" value="1" enum="ShadowMode">
-			Splits the view frustum in 2 areas, each with its own shadow map.
+			Splits the view frustum in 2 areas, each with its own shadow map. This shadow mode is a compromise between [constant SHADOW_ORTHOGONAL] and [constant SHADOW_PARALLEL_4_SPLITS] in terms of performance.
 		</constant>
 		<constant name="SHADOW_PARALLEL_4_SPLITS" value="2" enum="ShadowMode">
-			Splits the view frustum in 4 areas, each with its own shadow map.
+			Splits the view frustum in 4 areas, each with its own shadow map. This is the slowest directional shadow mode.
 		</constant>
 		<constant name="SHADOW_DEPTH_RANGE_STABLE" value="0" enum="ShadowDepthRange">
 			Keeps the shadow stable when the camera moves, at the cost of lower effective shadow resolution.
 		</constant>
 		<constant name="SHADOW_DEPTH_RANGE_OPTIMIZED" value="1" enum="ShadowDepthRange">
-			Tries to achieve maximum shadow resolution. May result in saw effect on shadow edges.
+			Tries to achieve maximum shadow resolution. May result in saw effect on shadow edges. This mode typically works best in games where the camera will often move at high speeds, such as most racing games.
 		</constant>
 	</constants>
 </class>

+ 1 - 1
doc/classes/DynamicFontData.xml

@@ -12,7 +12,7 @@
 	</methods>
 	<members>
 		<member name="antialiased" type="bool" setter="set_antialiased" getter="is_antialiased" default="true">
-			If [code]true[/code], the font is rendered with anti-aliasing.
+			If [code]true[/code], the font is rendered with anti-aliasing. This property applies both to the main font and its outline (if it has one).
 		</member>
 		<member name="font_path" type="String" setter="set_font_path" getter="get_font_path" default="&quot;&quot;">
 			The path to the vector font file.

+ 4 - 8
doc/classes/EditorInterface.xml

@@ -182,14 +182,6 @@
 				Selects the file, with the path provided by [code]file[/code], in the FileSystem dock.
 			</description>
 		</method>
-		<method name="set_distraction_free_mode">
-			<return type="void">
-			</return>
-			<argument index="0" name="enter" type="bool">
-			</argument>
-			<description>
-			</description>
-		</method>
 		<method name="set_main_screen_editor">
 			<return type="void">
 			</return>
@@ -210,6 +202,10 @@
 			</description>
 		</method>
 	</methods>
+	<members>
+		<member name="distraction_free_mode" type="bool" setter="set_distraction_free_mode" getter="is_distraction_free_mode_enabled">
+		</member>
+	</members>
 	<constants>
 	</constants>
 </class>

+ 11 - 0
doc/classes/Font.xml

@@ -54,6 +54,17 @@
 				Returns the font ascent (number of pixels above the baseline).
 			</description>
 		</method>
+		<method name="get_char_size" qualifiers="const">
+			<return type="Vector2">
+			</return>
+			<argument index="0" name="char" type="int">
+			</argument>
+			<argument index="1" name="next" type="int" default="0">
+			</argument>
+			<description>
+				Returns the size of a character, optionally taking kerning into account if the next character is provided.
+			</description>
+		</method>
 		<method name="get_descent" qualifiers="const">
 			<return type="float">
 			</return>

+ 1 - 1
doc/classes/ProjectSettings.xml

@@ -137,7 +137,7 @@
 			<argument index="0" name="file" type="String">
 			</argument>
 			<description>
-				Saves the configuration to a custom file.
+				Saves the configuration to a custom file. The file extension must be [code].godot[/code] (to save in text-based [ConfigFile] format) or [code].binary[/code] (to save in binary format).
 			</description>
 		</method>
 		<method name="set_initial_value">

+ 6 - 66
doc/tools/makerst.py

@@ -276,6 +276,8 @@ def main():  # type: () -> None
     group.add_argument("--dry-run", action="store_true", help="If passed, no output will be generated and XML files are only checked for errors.")
     args = parser.parse_args()
 
+    print("Checking for errors in the XML class reference...")
+
     file_list = []  # type: List[str]
 
     for path in args.path:
@@ -334,7 +336,10 @@ def main():  # type: () -> None
         state.current_class = class_name
         make_rst_class(class_def, state, args.dry_run, args.output)
 
-    if state.errored:
+    if not state.errored:
+        print("No errors found.")
+    else:
+        print("Errors were found in the class reference XML. Please check the messages above.")
         exit(1)
 
 def make_rst_class(class_def, state, dry_run, output_dir):  # type: (ClassDef, State, bool, str) -> None
@@ -549,71 +554,6 @@ def make_rst_class(class_def, state, dry_run, output_dir):  # type: (ClassDef, S
                 index += 1
 
 
-def make_class_list(class_list, columns):  # type: (List[str], int) -> None
-    # This function is no longer used.
-    f = open('class_list.rst', 'w', encoding='utf-8')
-    col_max = len(class_list) // columns + 1
-    print(('col max is ', col_max))
-    fit_columns = []  # type: List[List[str]]
-
-    for _ in range(0, columns):
-        fit_columns.append([])
-
-    indexers = []  # type List[str]
-    last_initial = ''
-
-    for idx, name in enumerate(class_list):
-        col = idx // col_max
-        if col >= columns:
-            col = columns - 1
-        fit_columns[col].append(name)
-        idx += 1
-        if name[:1] != last_initial:
-            indexers.append(name)
-        last_initial = name[:1]
-
-    row_max = 0
-    f.write("\n")
-
-    for n in range(0, columns):
-        if len(fit_columns[n]) > row_max:
-            row_max = len(fit_columns[n])
-
-    f.write("| ")
-    for n in range(0, columns):
-        f.write(" | |")
-
-    f.write("\n")
-    f.write("+")
-    for n in range(0, columns):
-        f.write("--+-------+")
-    f.write("\n")
-
-    for r in range(0, row_max):
-        s = '+ '
-        for c in range(0, columns):
-            if r >= len(fit_columns[c]):
-                continue
-
-            classname = fit_columns[c][r]
-            initial = classname[0]
-            if classname in indexers:
-                s += '**' + initial + '** | '
-            else:
-                s += ' | '
-
-            s += '[' + classname + '](class_' + classname.lower() + ') | '
-
-        s += '\n'
-        f.write(s)
-
-    for n in range(0, columns):
-        f.write("--+-------+")
-    f.write("\n")
-
-    f.close()
-
-
 def escape_rst(text, until_pos=-1):  # type: (str) -> str
     # Escape \ character, otherwise it ends up as an escape character in rst
     pos = 0

+ 0 - 4
drivers/windows/dir_access_windows.cpp

@@ -308,10 +308,6 @@ Error DirAccessWindows::remove(String p_path) {
 
 	p_path = fix_path(p_path);
 
-	printf("erasing %s\n", p_path.utf8().get_data());
-	//WIN32_FILE_ATTRIBUTE_DATA    fileInfo;
-	//DWORD fileAttr = GetFileAttributesExW(p_path.c_str(), GetFileExInfoStandard, &fileInfo);
-
 	DWORD fileAttr;
 
 	fileAttr = GetFileAttributesW(p_path.c_str());

+ 14 - 9
editor/create_dialog.cpp

@@ -55,13 +55,15 @@ void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode, const St
 
 		TreeItem *root = recent->create_item();
 
+		String icon_fallback = has_icon(base_type, "EditorIcons") ? base_type : "Object";
+
 		while (!f->eof_reached()) {
 			String l = f->get_line().strip_edges();
 			String name = l.split(" ")[0];
 			if ((ClassDB::class_exists(name) || ScriptServer::is_global_class(name)) && !_is_class_disabled_by_feature_profile(name)) {
 				TreeItem *ti = recent->create_item(root);
 				ti->set_text(0, l);
-				ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(l, base_type));
+				ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(name, icon_fallback));
 			}
 		}
 
@@ -251,7 +253,8 @@ void CreateDialog::add_type(const String &p_type, HashMap<String, TreeItem *> &p
 	const String &description = EditorHelp::get_doc_data()->class_list[p_type].brief_description;
 	item->set_tooltip(0, description);
 
-	item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, base_type));
+	String icon_fallback = has_icon(base_type, "EditorIcons") ? base_type : "Object";
+	item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, icon_fallback));
 
 	p_types[p_type] = item;
 }
@@ -310,9 +313,8 @@ void CreateDialog::_update_search() {
 	EditorData &ed = EditorNode::get_editor_data();
 
 	root->set_text(0, base_type);
-	if (has_icon(base_type, "EditorIcons")) {
-		root->set_icon(0, get_icon(base_type, "EditorIcons"));
-	}
+	String base_icon = has_icon(base_type, "EditorIcons") ? base_type : "Object";
+	root->set_icon(0, get_icon(base_icon, "EditorIcons"));
 
 	TreeItem *to_select = search_box->get_text() == base_type ? root : NULL;
 
@@ -395,9 +397,7 @@ void CreateDialog::_update_search() {
 				TreeItem *item = search_options->create_item(ti);
 				item->set_metadata(0, type);
 				item->set_text(0, ct[i].name);
-				if (ct[i].icon.is_valid()) {
-					item->set_icon(0, ct[i].icon);
-				}
+				item->set_icon(0, ct[i].icon.is_valid() ? ct[i].icon : get_icon(base_icon, "EditorIcons"));
 
 				if (!to_select || ct[i].name == search_box->get_text()) {
 					to_select = item;
@@ -600,15 +600,20 @@ void CreateDialog::_save_favorite_list() {
 void CreateDialog::_update_favorite_list() {
 
 	favorites->clear();
+
 	TreeItem *root = favorites->create_item();
+
+	String icon_fallback = has_icon(base_type, "EditorIcons") ? base_type : "Object";
+
 	for (int i = 0; i < favorite_list.size(); i++) {
 		String l = favorite_list[i];
 		String name = l.split(" ")[0];
 		if (!((ClassDB::class_exists(name) || ScriptServer::is_global_class(name)) && !_is_class_disabled_by_feature_profile(name)))
 			continue;
+
 		TreeItem *ti = favorites->create_item(root);
 		ti->set_text(0, l);
-		ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(l, base_type));
+		ti->set_icon(0, EditorNode::get_singleton()->get_class_icon(name, icon_fallback));
 	}
 	emit_signal("favorites_updated");
 }

+ 3 - 3
editor/editor_autoload_settings.cpp

@@ -669,18 +669,18 @@ bool EditorAutoloadSettings::autoload_add(const String &p_name, const String &p_
 
 	String error;
 	if (!_autoload_name_is_valid(name, &error)) {
-		EditorNode::get_singleton()->show_warning(error);
+		EditorNode::get_singleton()->show_warning(TTR("Can't add autoload:") + "\n" + error);
 		return false;
 	}
 
 	const String &path = p_path;
 	if (!FileAccess::exists(path)) {
-		EditorNode::get_singleton()->show_warning(TTR("Invalid path.") + "\n" + TTR("File does not exist."));
+		EditorNode::get_singleton()->show_warning(TTR("Can't add autoload:") + "\n" + TTR(vformat("%s is an invalid path. File does not exist.", path)));
 		return false;
 	}
 
 	if (!path.begins_with("res://")) {
-		EditorNode::get_singleton()->show_warning(TTR("Invalid path.") + "\n" + TTR("Not in resource path."));
+		EditorNode::get_singleton()->show_warning(TTR("Can't add autoload:") + "\n" + TTR(vformat("%s is an invalid path. Not in resource path (res://).", path)));
 		return false;
 	}
 

+ 3 - 6
editor/editor_node.cpp

@@ -2847,8 +2847,8 @@ void EditorNode::_update_debug_options() {
 	bool check_file_server = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_file_server", false);
 	bool check_debug_collisons = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_collisons", false);
 	bool check_debug_navigation = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_debug_navigation", false);
-	bool check_live_debug = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_live_debug", false);
-	bool check_reload_scripts = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_reload_scripts", false);
+	bool check_live_debug = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_live_debug", true);
+	bool check_reload_scripts = EditorSettings::get_singleton()->get_project_metadata("debug_options", "run_reload_scripts", true);
 
 	if (check_deploy_remote) _menu_option_confirm(RUN_DEPLOY_REMOTE_DEBUG, true);
 	if (check_file_server) _menu_option_confirm(RUN_FILE_SERVER, true);
@@ -4945,7 +4945,7 @@ void EditorNode::set_distraction_free_mode(bool p_enter) {
 	}
 }
 
-bool EditorNode::get_distraction_free_mode() const {
+bool EditorNode::is_distraction_free_mode_enabled() const {
 	return distraction_free->is_pressed();
 }
 
@@ -6229,13 +6229,10 @@ EditorNode::EditorNode() {
 	p->add_check_shortcut(ED_SHORTCUT("editor/visible_navigation", TTR("Visible Navigation")), RUN_DEBUG_NAVIGATION);
 	p->set_item_tooltip(p->get_item_count() - 1, TTR("Navigation meshes and polygons will be visible on the running game if this option is turned on."));
 	p->add_separator();
-	//those are now on by default, since they are harmless
 	p->add_check_shortcut(ED_SHORTCUT("editor/sync_scene_changes", TTR("Sync Scene Changes")), RUN_LIVE_DEBUG);
 	p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any changes made to the scene in the editor will be replicated in the running game.\nWhen used remotely on a device, this is more efficient with network filesystem."));
-	p->set_item_checked(p->get_item_count() - 1, true);
 	p->add_check_shortcut(ED_SHORTCUT("editor/sync_script_changes", TTR("Sync Script Changes")), RUN_RELOAD_SCRIPTS);
 	p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any script that is saved will be reloaded on the running game.\nWhen used remotely on a device, this is more efficient with network filesystem."));
-	p->set_item_checked(p->get_item_count() - 1, true);
 	p->connect("id_pressed", this, "_menu_option");
 
 	menu_hb->add_spacer();

+ 1 - 1
editor/editor_node.h

@@ -691,7 +691,7 @@ public:
 	bool get_docks_visible() const;
 
 	void set_distraction_free_mode(bool p_enter);
-	bool get_distraction_free_mode() const;
+	bool is_distraction_free_mode_enabled() const;
 
 	void add_control_to_dock(DockSlot p_slot, Control *p_control);
 	void remove_control_from_dock(Control *p_control);

+ 7 - 0
editor/editor_plugin.cpp

@@ -281,6 +281,10 @@ void EditorInterface::set_distraction_free_mode(bool p_enter) {
 
 EditorInterface *EditorInterface::singleton = NULL;
 
+bool EditorInterface::is_distraction_free_mode_enabled() const {
+	return EditorNode::get_singleton()->is_distraction_free_mode_enabled();
+}
+
 void EditorInterface::_bind_methods() {
 
 	ClassDB::bind_method(D_METHOD("inspect_object", "object", "for_property"), &EditorInterface::inspect_object, DEFVAL(String()));
@@ -312,6 +316,9 @@ void EditorInterface::_bind_methods() {
 
 	ClassDB::bind_method(D_METHOD("set_main_screen_editor", "name"), &EditorInterface::set_main_screen_editor);
 	ClassDB::bind_method(D_METHOD("set_distraction_free_mode", "enter"), &EditorInterface::set_distraction_free_mode);
+	ClassDB::bind_method(D_METHOD("is_distraction_free_mode_enabled"), &EditorInterface::is_distraction_free_mode_enabled);
+
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distraction_free_mode"), "set_distraction_free_mode", "is_distraction_free_mode_enabled");
 }
 
 EditorInterface::EditorInterface() {

+ 1 - 0
editor/editor_plugin.h

@@ -105,6 +105,7 @@ public:
 
 	void set_main_screen_editor(const String &p_name);
 	void set_distraction_free_mode(bool p_enter);
+	bool is_distraction_free_mode_enabled() const;
 
 	EditorInterface();
 };

+ 1 - 1
editor/import/resource_importer_layered_texture.cpp

@@ -81,7 +81,7 @@ String ResourceImporterLayeredTexture::get_preset_name(int p_idx) const {
 
 void ResourceImporterLayeredTexture::get_import_options(List<ImportOption> *r_options, int p_preset) const {
 
-	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Video RAM,Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 1 : 0));
+	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless (PNG),Lossy (WebP),Video RAM (S3TC/ETC/BPTC),Uncompressed", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 1 : 0));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress/no_bptc_if_rgb"), false));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "flags/repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirrored"), 0));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/filter"), true));

+ 2 - 1
editor/import/resource_importer_texture_atlas.cpp

@@ -137,7 +137,8 @@ static void _plot_triangle(Vector2 *vertices, const Vector2 &p_offset, bool p_tr
 	double dx_low = double(x[2] - x[1]) / (y[2] - y[1] + 1);
 	double xf = x[0];
 	double xt = x[0] + dx_upper; // if y[0] == y[1], special case
-	for (int yi = y[0]; yi <= (y[2] > height - 1 ? height - 1 : y[2]); yi++) {
+	int max_y = MIN(y[2], height - p_offset.y - 1);
+	for (int yi = y[0]; yi <= max_y; yi++) {
 		if (yi >= 0) {
 			for (int xi = (xf > 0 ? int(xf) : 0); xi <= (xt < width ? xt : width - 1); xi++) {
 

+ 1 - 1
editor/plugins/canvas_item_editor_plugin.cpp

@@ -86,7 +86,6 @@ public:
 		GridContainer *child_container;
 
 		set_title(TTR("Configure Snap"));
-		get_ok()->set_text(TTR("Close"));
 
 		container = memnew(VBoxContainer);
 		add_child(container);
@@ -5490,6 +5489,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
 	hb->add_child(pan_button);
 	pan_button->set_toggle_mode(true);
 	pan_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_PAN));
+	pan_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/pan_mode", TTR("Pan Mode"), KEY_G));
 	pan_button->set_tooltip(TTR("Pan Mode"));
 
 	ruler_button = memnew(ToolButton);

+ 14 - 9
editor/plugins/script_text_editor.cpp

@@ -625,6 +625,18 @@ void ScriptTextEditor::_validate_script() {
 	for (List<ScriptLanguage::Warning>::Element *E = warnings.front(); E; E = E->next()) {
 		ScriptLanguage::Warning w = E->get();
 
+		Dictionary ignore_meta;
+		ignore_meta["line"] = w.line;
+		ignore_meta["code"] = w.string_code.to_lower();
+		warnings_panel->push_cell();
+		warnings_panel->push_meta(ignore_meta);
+		warnings_panel->push_color(
+				warnings_panel->get_color("accent_color", "Editor").linear_interpolate(warnings_panel->get_color("mono_color", "Editor"), 0.5));
+		warnings_panel->add_text(TTR("[Ignore]"));
+		warnings_panel->pop(); // Color.
+		warnings_panel->pop(); // Meta ignore.
+		warnings_panel->pop(); // Cell.
+
 		warnings_panel->push_cell();
 		warnings_panel->push_meta(w.line - 1);
 		warnings_panel->push_color(warnings_panel->get_color("warning_color", "Editor"));
@@ -637,15 +649,6 @@ void ScriptTextEditor::_validate_script() {
 		warnings_panel->push_cell();
 		warnings_panel->add_text(w.message);
 		warnings_panel->pop(); // Cell.
-
-		Dictionary ignore_meta;
-		ignore_meta["line"] = w.line;
-		ignore_meta["code"] = w.string_code.to_lower();
-		warnings_panel->push_cell();
-		warnings_panel->push_meta(ignore_meta);
-		warnings_panel->add_text(TTR("(ignore)"));
-		warnings_panel->pop(); // Meta ignore.
-		warnings_panel->pop(); // Cell.
 	}
 	warnings_panel->pop(); // Table.
 
@@ -1805,6 +1808,8 @@ ScriptTextEditor::ScriptTextEditor() {
 
 	warnings_panel = memnew(RichTextLabel);
 	editor_box->add_child(warnings_panel);
+	warnings_panel->add_font_override(
+			"normal_font", EditorNode::get_singleton()->get_gui_base()->get_font("main", "EditorFonts"));
 	warnings_panel->set_custom_minimum_size(Size2(0, 100 * EDSCALE));
 	warnings_panel->set_h_size_flags(SIZE_EXPAND_FILL);
 	warnings_panel->set_meta_underline(true);

+ 18 - 1
editor/plugins/tile_map_editor_plugin.cpp

@@ -205,6 +205,21 @@ void TileMapEditor::_palette_multi_selected(int index, bool selected) {
 	_update_palette();
 }
 
+void TileMapEditor::_palette_input(const Ref<InputEvent> &p_event) {
+	const Ref<InputEventMouseButton> mb = p_event;
+
+	// Zoom in/out using Ctrl + mouse wheel.
+	if (mb.is_valid() && mb->is_pressed() && mb->get_command()) {
+		if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) {
+			size_slider->set_value(size_slider->get_value() + 0.2);
+		}
+
+		if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) {
+			size_slider->set_value(size_slider->get_value() - 0.2);
+		}
+	}
+}
+
 void TileMapEditor::_canvas_mouse_enter() {
 
 	mouse_over = true;
@@ -1834,6 +1849,7 @@ void TileMapEditor::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("_clear_transform"), &TileMapEditor::_clear_transform);
 	ClassDB::bind_method(D_METHOD("_palette_selected"), &TileMapEditor::_palette_selected);
 	ClassDB::bind_method(D_METHOD("_palette_multi_selected"), &TileMapEditor::_palette_multi_selected);
+	ClassDB::bind_method(D_METHOD("_palette_input"), &TileMapEditor::_palette_input);
 
 	ClassDB::bind_method(D_METHOD("_fill_points"), &TileMapEditor::_fill_points);
 	ClassDB::bind_method(D_METHOD("_erase_points"), &TileMapEditor::_erase_points);
@@ -1996,6 +2012,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
 	palette->add_constant_override("vseparation", 8 * EDSCALE);
 	palette->connect("item_selected", this, "_palette_selected");
 	palette->connect("multi_selected", this, "_palette_multi_selected");
+	palette->connect("gui_input", this, "_palette_input");
 	palette_container->add_child(palette);
 
 	// Add message for when no texture is selected.
@@ -2034,7 +2051,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
 	toolbar->add_child(paint_button);
 
 	bucket_fill_button = memnew(ToolButton);
-	bucket_fill_button->set_shortcut(ED_SHORTCUT("tile_map_editor/bucket_fill", TTR("Bucket Fill"), KEY_G));
+	bucket_fill_button->set_shortcut(ED_SHORTCUT("tile_map_editor/bucket_fill", TTR("Bucket Fill"), KEY_B));
 	bucket_fill_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_BUCKET));
 	bucket_fill_button->set_toggle_mode(true);
 	toolbar->add_child(bucket_fill_button);

+ 1 - 0
editor/plugins/tile_map_editor_plugin.h

@@ -192,6 +192,7 @@ class TileMapEditor : public VBoxContainer {
 	void _menu_option(int p_option);
 	void _palette_selected(int index);
 	void _palette_multi_selected(int index, bool selected);
+	void _palette_input(const Ref<InputEvent> &p_event);
 
 	Dictionary _create_cell_dictionary(int tile, bool flip_x, bool flip_y, bool transpose, Vector2 autotile_coord);
 	void _start_undo(const String &p_action);

+ 25 - 9
editor/plugins/tile_set_editor_plugin.cpp

@@ -265,6 +265,7 @@ void TileSetEditor::_bind_methods() {
 	ClassDB::bind_method("_on_tileset_toolbar_confirm", &TileSetEditor::_on_tileset_toolbar_confirm);
 	ClassDB::bind_method("_on_texture_list_selected", &TileSetEditor::_on_texture_list_selected);
 	ClassDB::bind_method("_on_edit_mode_changed", &TileSetEditor::_on_edit_mode_changed);
+	ClassDB::bind_method("_on_scroll_container_input", &TileSetEditor::_on_scroll_container_input);
 	ClassDB::bind_method("_on_workspace_mode_changed", &TileSetEditor::_on_workspace_mode_changed);
 	ClassDB::bind_method("_on_workspace_overlay_draw", &TileSetEditor::_on_workspace_overlay_draw);
 	ClassDB::bind_method("_on_workspace_process", &TileSetEditor::_on_workspace_process);
@@ -592,6 +593,7 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) {
 	scroll = memnew(ScrollContainer);
 	main_vb->add_child(scroll);
 	scroll->set_v_size_flags(SIZE_EXPAND_FILL);
+	scroll->connect("gui_input", this, "_on_scroll_container_input");
 	scroll->set_clip_contents(true);
 
 	empty_message = memnew(Label);
@@ -1216,6 +1218,27 @@ bool TileSetEditor::is_within_grabbing_distance_of_first_point(const Vector2 &p_
 	return distance < p_grab_threshold;
 }
 
+void TileSetEditor::_on_scroll_container_input(const Ref<InputEvent> &p_event) {
+	const Ref<InputEventMouseButton> mb = p_event;
+
+	if (mb.is_valid()) {
+		// Zoom in/out using Ctrl + mouse wheel. This is done on the ScrollContainer
+		// to allow performing this action anywhere, even if the cursor isn't
+		// hovering the texture in the workspace.
+		if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed() && mb->get_control()) {
+			print_line("zooming in");
+			_zoom_in();
+			// Don't scroll up after zooming in.
+			accept_event();
+		} else if (mb->get_button_index() == BUTTON_WHEEL_DOWN && mb->is_pressed() && mb->get_control()) {
+			print_line("zooming out");
+			_zoom_out();
+			// Don't scroll down after zooming out.
+			accept_event();
+		}
+	}
+}
+
 void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
 
 	if (tileset.is_null() || !get_current_texture().is_valid())
@@ -1232,8 +1255,8 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
 	}
 	current_tile_region.position += WORKSPACE_MARGIN;
 
-	Ref<InputEventMouseButton> mb = p_ie;
-	Ref<InputEventMouseMotion> mm = p_ie;
+	const Ref<InputEventMouseButton> mb = p_ie;
+	const Ref<InputEventMouseMotion> mm = p_ie;
 
 	if (mb.is_valid()) {
 		if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && !creating_shape) {
@@ -1257,13 +1280,6 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
 				delete tiles;
 			}
 		}
-
-		// Mouse Wheel Event
-		if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed() && mb->get_control()) {
-			_zoom_in();
-		} else if (mb->get_button_index() == BUTTON_WHEEL_DOWN && mb->is_pressed() && mb->get_control()) {
-			_zoom_out();
-		}
 	}
 	// Drag Middle Mouse
 	if (mm.is_valid()) {

+ 1 - 0
editor/plugins/tile_set_editor_plugin.h

@@ -201,6 +201,7 @@ private:
 	void _on_workspace_overlay_draw();
 	void _on_workspace_draw();
 	void _on_workspace_process();
+	void _on_scroll_container_input(const Ref<InputEvent> &p_event);
 	void _on_workspace_input(const Ref<InputEvent> &p_ie);
 	void _on_tool_clicked(int p_tool);
 	void _on_priority_changed(float val);

+ 6 - 0
editor/project_manager.cpp

@@ -945,6 +945,8 @@ public:
 		icon = NULL;
 		icon_needs_reload = true;
 		hover = false;
+
+		set_focus_mode(FocusMode::FOCUS_ALL);
 	}
 
 	void set_is_favorite(bool fav) {
@@ -1728,6 +1730,10 @@ void ProjectList::_panel_input(const Ref<InputEvent> &p_ev, Node *p_hb) {
 			select_project(clicked_index);
 		}
 
+		if (_selected_project_keys.has(clicked_project.project_key)) {
+			clicked_project.control->grab_focus();
+		}
+
 		emit_signal(SIGNAL_SELECTION_CHANGED);
 
 		if (!mb->get_control() && mb->is_doubleclick()) {

+ 5 - 0
editor/scene_tree_dock.cpp

@@ -2591,6 +2591,11 @@ void SceneTreeDock::_focus_node() {
 }
 
 void SceneTreeDock::attach_script_to_selected(bool p_extend) {
+	if (ScriptServer::get_language_count() == 0) {
+		EditorNode::get_singleton()->show_warning(TTR("Cannot attach a script: there are no languages registered.\nThis is probably because this editor was built with all language modules disabled."));
+		return;
+	}
+
 	if (!profile_allow_script_editing) {
 		return;
 	}

+ 4 - 3
editor/script_create_dialog.cpp

@@ -800,7 +800,7 @@ ScriptCreateDialog::ScriptCreateDialog() {
 	gc->add_child(memnew(Label(TTR("Language:"))));
 	gc->add_child(language_menu);
 
-	default_language = 0;
+	default_language = -1;
 	for (int i = 0; i < ScriptServer::get_language_count(); i++) {
 
 		String lang = ScriptServer::get_language(i)->get_name();
@@ -809,8 +809,9 @@ ScriptCreateDialog::ScriptCreateDialog() {
 			default_language = i;
 		}
 	}
-
-	language_menu->select(default_language);
+	if (default_language >= 0) {
+		language_menu->select(default_language);
+	}
 	current_language = default_language;
 
 	language_menu->connect("item_selected", this, "_lang_changed");

+ 1 - 1
methods.py

@@ -149,7 +149,7 @@ def detect_modules(at_path):
 
 
 def is_module(path):
-    return os.path.isdir(path) and os.path.exists(path + "/config.py")
+    return os.path.isdir(path) and os.path.exists(os.path.join(path, "SCsub"))
 
 
 def write_modules(module_list):

+ 5 - 2
modules/gdscript/gdscript_function.cpp

@@ -1501,11 +1501,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 
 #ifdef DEBUG_ENABLED
 				GET_VARIANT_PTR(test, 1);
-				GET_VARIANT_PTR(message, 2);
 				bool result = test->booleanize();
 
 				if (!result) {
-					const String &message_str = *message;
+					String message_str;
+					if (_code_ptr[ip + 2] != 0) {
+						GET_VARIANT_PTR(message, 2);
+						message_str = *message;
+					}
 					if (message_str.empty()) {
 						err_text = "Assertion failed.";
 					} else {

+ 2 - 0
modules/mono/csharp_script.cpp

@@ -2629,7 +2629,9 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect
 		return true;
 	}
 
+#ifdef TOOLS_ENABLED
 	MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute));
+#endif
 
 	PropertyHint hint = PROPERTY_HINT_NONE;
 	String hint_string;

+ 1 - 1
modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs

@@ -436,7 +436,7 @@ namespace GodotTools
                 aboutLabel.Text =
                     "C# support in Godot Engine is in late alpha stage and, while already usable, " +
                     "it is not meant for use in production.\n\n" +
-                    "Projects can be exported to Linux, macOS, Windows and Android, but not yet to iOS, HTML5 or UWP. " +
+                    "Projects can be exported to Linux, macOS, Windows, Android, iOS and HTML5, but not yet to UWP. " +
                     "Bugs and usability issues will be addressed gradually over future releases, " +
                     "potentially including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" +
                     "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, MSBuild version, IDE, etc.:\n\n" +

+ 1 - 1
modules/mono/mono_gd/gd_mono.cpp

@@ -425,10 +425,10 @@ void GDMono::initialize_load_assemblies() {
 #if defined(TOOLS_ENABLED)
 	bool tool_assemblies_loaded = _load_tools_assemblies();
 	CRASH_COND_MSG(!tool_assemblies_loaded, "Mono: Failed to load '" TOOLS_ASM_NAME "' assemblies.");
-#endif
 
 	if (Main::is_project_manager())
 		return;
+#endif
 
 	// Load the project's main assembly. This doesn't necessarily need to succeed.
 	// The game may not be using .NET at all, or if the project does use .NET and

+ 6 - 0
modules/visual_script/visual_script_editor.cpp

@@ -3126,6 +3126,7 @@ void VisualScriptEditor::_move_nodes_with_rescan(const StringName &p_func_from,
 	{
 		List<VisualScript::DataConnection> data_connections;
 		script->get_data_connection_list(p_func_from, &data_connections);
+		int func_from_node_id = script->get_function_node_id(p_func_from);
 
 		HashMap<int, Map<int, Pair<int, int> > > connections;
 
@@ -3135,6 +3136,11 @@ void VisualScriptEditor::_move_nodes_with_rescan(const StringName &p_func_from,
 			int out_p = E->get().from_port;
 			int in_p = E->get().to_port;
 
+			// skip if the from_node is a function node
+			if (from == func_from_node_id) {
+				continue;
+			}
+
 			if (!connections.has(to))
 				connections.set(to, Map<int, Pair<int, int> >());
 			connections[to].insert(in_p, Pair<int, int>(from, out_p));

+ 1 - 1
modules/xatlas_unwrap/register_types.cpp

@@ -68,7 +68,7 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
 	ERR_FAIL_COND_V_MSG(err != xatlas::AddMeshError::Enum::Success, false, xatlas::StringForEnum(err));
 
 	printf("Generate..\n");
-	xatlas::Generate(atlas, chart_options, NULL, pack_options);
+	xatlas::Generate(atlas, chart_options, xatlas::ParameterizeOptions(), pack_options);
 
 	*r_size_hint_x = atlas->width;
 	*r_size_hint_y = atlas->height;

+ 42 - 4
platform/android/export/export.cpp

@@ -257,6 +257,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
 	};
 
 	Vector<PluginConfig> plugins;
+	String last_plugin_names;
+	uint64_t last_custom_build_time = 0;
 	volatile bool plugins_changed;
 	Mutex *plugins_lock;
 	Vector<Device> devices;
@@ -1786,23 +1788,32 @@ public:
 		// Look for export templates (first official, and if defined custom templates).
 
 		if (!bool(p_preset->get("custom_template/use_custom_build"))) {
-			bool dvalid = exists_export_template("android_debug.apk", &err);
-			bool rvalid = exists_export_template("android_release.apk", &err);
+			String template_err;
+			bool dvalid = false;
+			bool rvalid = false;
 
 			if (p_preset->get("custom_template/debug") != "") {
 				dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
 				if (!dvalid) {
-					err += TTR("Custom debug template not found.") + "\n";
+					template_err += TTR("Custom debug template not found.") + "\n";
 				}
+			} else {
+				dvalid = exists_export_template("android_debug.apk", &template_err);
 			}
+
 			if (p_preset->get("custom_template/release") != "") {
 				rvalid = FileAccess::exists(p_preset->get("custom_template/release"));
 				if (!rvalid) {
-					err += TTR("Custom release template not found.") + "\n";
+					template_err += TTR("Custom release template not found.") + "\n";
 				}
+			} else {
+				rvalid = exists_export_template("android_release.apk", &template_err);
 			}
 
 			valid = dvalid || rvalid;
+			if (!valid) {
+				err += template_err;
+			}
 		} else {
 			valid = exists_export_template("android_source.zip", &err);
 		}
@@ -2201,6 +2212,29 @@ public:
 		}
 	}
 
+	inline bool is_clean_build_required(Vector<PluginConfig> enabled_plugins) {
+		String plugin_names = get_plugins_names(enabled_plugins);
+		bool first_build = last_custom_build_time == 0;
+		bool have_plugins_changed = false;
+
+		if (!first_build) {
+			have_plugins_changed = plugin_names != last_plugin_names;
+			if (!have_plugins_changed) {
+				for (int i = 0; i < enabled_plugins.size(); i++) {
+					if (enabled_plugins.get(i).last_updated > last_custom_build_time) {
+						have_plugins_changed = true;
+						break;
+					}
+				}
+			}
+		}
+
+		last_custom_build_time = OS::get_singleton()->get_unix_time();
+		last_plugin_names = plugin_names;
+
+		return have_plugins_changed || first_build;
+	}
+
 	virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) {
 
 		ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
@@ -2250,8 +2284,12 @@ public:
 			String local_plugins_binaries = get_plugins_binaries(BINARY_TYPE_LOCAL, enabled_plugins);
 			String remote_plugins_binaries = get_plugins_binaries(BINARY_TYPE_REMOTE, enabled_plugins);
 			String custom_maven_repos = get_plugins_custom_maven_repos(enabled_plugins);
+			bool clean_build_required = is_clean_build_required(enabled_plugins);
 
 			List<String> cmdline;
+			if (clean_build_required) {
+				cmdline.push_back("clean");
+			}
 			cmdline.push_back("build");
 			cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name.
 			cmdline.push_back("-Pplugins_local_binaries=" + local_plugins_binaries); // argument to specify the list of plugins local dependencies.

+ 5 - 0
platform/android/java/app/build.gradle

@@ -87,6 +87,11 @@ android {
     }
 
     defaultConfig {
+        // The default ignore pattern for the 'assets' directory includes hidden files and directories which are used by Godot projects.
+        aaptOptions {
+            ignoreAssetsPattern "!.svn:!.git:!.ds_store:!*.scc:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~"
+        }
+
         // Feel free to modify the application id to your own.
         applicationId getExportPackageName()
         minSdkVersion versions.minSdk

+ 16 - 0
platform/android/plugin/godot_plugin_config.h

@@ -70,6 +70,8 @@ The `dependencies` section and fields are optional and defined as follow:
 struct PluginConfig {
 	// Set to true when the config file is properly loaded.
 	bool valid_config = false;
+	// Unix timestamp of last change to this plugin.
+	uint64_t last_updated = 0;
 
 	// Required config section
 	String name;
@@ -87,6 +89,7 @@ struct PluginConfig {
  */
 static const PluginConfig GODOT_PAYMENT = {
 	/*.valid_config =*/true,
+	/*.last_updated =*/0,
 	/*.name =*/"GodotPayment",
 	/*.binary_type =*/"local",
 	/*.binary =*/"res://android/build/libs/plugins/GodotPayment.release.aar",
@@ -150,6 +153,18 @@ static inline bool is_plugin_config_valid(PluginConfig plugin_config) {
 	return valid_name && valid_binary && valid_binary_type && valid_local_dependencies;
 }
 
+static inline uint64_t get_plugin_modification_time(const PluginConfig &plugin_config, const String &config_path) {
+	uint64_t last_updated = FileAccess::get_modified_time(config_path);
+	last_updated = MAX(last_updated, FileAccess::get_modified_time(plugin_config.binary));
+
+	for (int i = 0; i < plugin_config.local_dependencies.size(); i++) {
+		String binary = plugin_config.local_dependencies.get(i);
+		last_updated = MAX(last_updated, FileAccess::get_modified_time(binary));
+	}
+
+	return last_updated;
+}
+
 static inline PluginConfig load_plugin_config(Ref<ConfigFile> config_file, const String &path) {
 	PluginConfig plugin_config = {};
 
@@ -177,6 +192,7 @@ static inline PluginConfig load_plugin_config(Ref<ConfigFile> config_file, const
 			}
 
 			plugin_config.valid_config = is_plugin_config_valid(plugin_config);
+			plugin_config.last_updated = get_plugin_modification_time(plugin_config, path);
 		}
 	}
 

+ 8 - 1
platform/osx/joypad_osx.cpp

@@ -314,9 +314,16 @@ bool JoypadOSX::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) {
 	if (refCF) {
 		CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &product_id);
 	}
+
+	int version = 0;
+	refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDVersionNumberKey));
+	if (refCF) {
+		CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &version);
+	}
+
 	if (vendor && product_id) {
 		char uid[128];
-		sprintf(uid, "%04x%08x%04x%08x", OSSwapHostToBigInt32(vendor), 0, OSSwapHostToBigInt32(product_id), 0);
+		sprintf(uid, "%08x%08x%08x%08x", OSSwapHostToBigInt32(3), OSSwapHostToBigInt32(vendor), OSSwapHostToBigInt32(product_id), OSSwapHostToBigInt32(version));
 		input->joy_connection_changed(id, true, name, uid);
 	} else {
 		//bluetooth device

+ 23 - 14
platform/windows/joypad_windows.cpp

@@ -33,10 +33,6 @@
 #include <oleauto.h>
 #include <wbemidl.h>
 
-#ifndef __GNUC__
-#define __builtin_bswap32 _byteswap_ulong
-#endif
-
 #if defined(__GNUC__)
 // Workaround GCC warning from -Wcast-function-type.
 #define GetProcAddress (void *)GetProcAddress
@@ -67,18 +63,26 @@ JoypadWindows::JoypadWindows(InputDefault *_input, HWND *hwnd) {
 	for (int i = 0; i < JOYPADS_MAX; i++)
 		attached_joypads[i] = false;
 
-	HRESULT result;
-	result = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, NULL);
-	if (FAILED(result)) {
-		printf("failed init DINPUT: %ld\n", result);
+	HRESULT result = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, nullptr);
+	if (result == DI_OK) {
+		probe_joypads();
+	} else {
+		ERR_PRINT("Couldn't initialize DirectInput. Error: " + itos(result));
+		if (result == DIERR_OUTOFMEMORY) {
+			ERR_PRINT("The Windows DirectInput subsystem could not allocate sufficient memory.");
+			ERR_PRINT("Rebooting your PC may solve this issue.");
+		}
+		// Ensure dinput is still a nullptr.
+		dinput = nullptr;
 	}
-	probe_joypads();
 }
 
 JoypadWindows::~JoypadWindows() {
 
 	close_joypad();
-	dinput->Release();
+	if (dinput) {
+		dinput->Release();
+	}
 	unload_xinput();
 }
 
@@ -142,6 +146,7 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) {
 
 bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) {
 
+	ERR_FAIL_NULL_V_MSG(dinput, false, "DirectInput not initialized. Rebooting your PC may solve this issue.");
 	HRESULT hr;
 	int num = input->get_unused_joy_id();
 
@@ -165,10 +170,13 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) {
 
 	const GUID &guid = instance->guidProduct;
 	char uid[128];
-	sprintf_s(uid, "%08lx%04hx%04hx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
-			__builtin_bswap32(guid.Data1), guid.Data2, guid.Data3,
-			guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
-			guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
+
+	ERR_FAIL_COND_V_MSG(memcmp(&guid.Data4[2], "PIDVID", 6), false, "DirectInput device not recognised.");
+	WORD type = BSWAP16(0x03);
+	WORD vendor = BSWAP16(LOWORD(guid.Data1));
+	WORD product = BSWAP16(HIWORD(guid.Data1));
+	WORD version = 0;
+	sprintf_s(uid, "%04x%04x%04x%04x%04x%04x%04x%04x", type, 0, vendor, 0, product, 0, version, 0);
 
 	id_to_change = joypad_count;
 
@@ -280,6 +288,7 @@ void JoypadWindows::close_joypad(int id) {
 
 void JoypadWindows::probe_joypads() {
 
+	ERR_FAIL_NULL_MSG(dinput, "DirectInput not initialized. Rebooting your PC may solve this issue.");
 	DWORD dwResult;
 	for (DWORD i = 0; i < XUSER_MAX_COUNT; i++) {
 

+ 3 - 0
platform/windows/os_windows.cpp

@@ -3508,6 +3508,9 @@ String OS_Windows::get_current_tablet_driver() const {
 }
 
 void OS_Windows::set_current_tablet_driver(const String &p_driver) {
+	if (get_tablet_driver_count() == 0) {
+		return;
+	}
 	bool found = false;
 	for (int i = 0; i < get_tablet_driver_count(); i++) {
 		if (p_driver == get_tablet_driver_name(i)) {

+ 1 - 1
scene/3d/light.cpp

@@ -389,7 +389,7 @@ void DirectionalLight::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("is_blend_splits_enabled"), &DirectionalLight::is_blend_splits_enabled);
 
 	ADD_GROUP("Directional Shadow", "directional_shadow_");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_mode", PROPERTY_HINT_ENUM, "Orthogonal,PSSM 2 Splits,PSSM 4 Splits"), "set_shadow_mode", "get_shadow_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_mode", PROPERTY_HINT_ENUM, "Orthogonal (Fast),PSSM 2 Splits (Average),PSSM 4 Splits (Slow)"), "set_shadow_mode", "get_shadow_mode");
 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_split_1", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_1_OFFSET);
 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_split_2", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_2_OFFSET);
 	ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_split_3", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_3_OFFSET);

+ 1 - 1
scene/gui/color_picker.cpp

@@ -70,7 +70,7 @@ void ColorPicker::_notification(int p_what) {
 		case NOTIFICATION_PARENTED: {
 
 			for (int i = 0; i < 4; i++)
-				set_margin((Margin)i, get_constant("margin"));
+				set_margin((Margin)i, get_margin((Margin)i) + get_constant("margin"));
 		} break;
 		case NOTIFICATION_VISIBILITY_CHANGED: {
 

+ 7 - 1
scene/gui/rich_text_label.cpp

@@ -247,6 +247,11 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
 			lh = line < l.height_caches.size() ? l.height_caches[line] : 1;                                                                                     \
 			line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1;                                                                            \
 			line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1;                                                                         \
+			if (p_mode == PROCESS_DRAW) {                                                                                                                       \
+				if (line < l.offset_caches.size()) {                                                                                                            \
+					wofs = l.offset_caches[line];                                                                                                               \
+				}                                                                                                                                               \
+			}                                                                                                                                                   \
 		}                                                                                                                                                       \
 		if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh && p_click_pos.x < p_ofs.x + wofs) { \
 			if (r_outside) *r_outside = true;                                                                                                                   \
@@ -870,7 +875,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
 
 void RichTextLabel::_scroll_changed(double) {
 
-	if (updating_scroll || !scroll_active)
+	if (updating_scroll)
 		return;
 
 	if (scroll_follow && vscroll->get_value() >= (vscroll->get_max() - vscroll->get_page()))
@@ -2009,6 +2014,7 @@ void RichTextLabel::set_scroll_active(bool p_active) {
 		return;
 
 	scroll_active = p_active;
+	vscroll->set_drag_node_enabled(p_active);
 	update();
 }
 

+ 8 - 0
scene/gui/scroll_bar.cpp

@@ -545,6 +545,9 @@ void ScrollBar::_drag_node_exit() {
 }
 
 void ScrollBar::_drag_node_input(const Ref<InputEvent> &p_input) {
+	if (!drag_node_enabled) {
+		return;
+	}
 
 	Ref<InputEventMouseButton> mb = p_input;
 
@@ -638,6 +641,10 @@ NodePath ScrollBar::get_drag_node() const {
 	return drag_node_path;
 }
 
+void ScrollBar::set_drag_node_enabled(bool p_enable) {
+	drag_node_enabled = p_enable;
+}
+
 void ScrollBar::set_smooth_scroll_enabled(bool p_enable) {
 	smooth_scroll_enabled = p_enable;
 }
@@ -668,6 +675,7 @@ ScrollBar::ScrollBar(Orientation p_orientation) {
 
 	drag.active = false;
 
+	drag_node_enabled = true;
 	drag_node_speed = Vector2();
 	drag_node_touching = false;
 	drag_node_touching_deaccel = false;

+ 2 - 1
scene/gui/scroll_bar.h

@@ -53,7 +53,6 @@ class ScrollBar : public Range {
 	HighlightStatus highlight;
 
 	struct Drag {
-
 		bool active;
 		float pos_at_click;
 		float value_at_click;
@@ -70,6 +69,7 @@ class ScrollBar : public Range {
 
 	Node *drag_node;
 	NodePath drag_node_path;
+	bool drag_node_enabled;
 
 	Vector2 drag_node_speed;
 	Vector2 drag_node_accum;
@@ -101,6 +101,7 @@ public:
 
 	void set_drag_node(const NodePath &p_path);
 	NodePath get_drag_node() const;
+	void set_drag_node_enabled(bool p_enable);
 
 	void set_smooth_scroll_enabled(bool p_enable);
 	bool is_smooth_scroll_enabled() const;

+ 1 - 1
scene/resources/dynamic_font.cpp

@@ -577,7 +577,7 @@ DynamicFontAtSize::Character DynamicFontAtSize::_make_outline_char(CharType p_ch
 		goto cleanup_stroker;
 	if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0)
 		goto cleanup_glyph;
-	if (FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1) != 0)
+	if (FT_Glyph_To_Bitmap(&glyph, font->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0)
 		goto cleanup_glyph;
 
 	glyph_bitmap = (FT_BitmapGlyph)glyph;

+ 1 - 2
scene/resources/font.cpp

@@ -95,6 +95,7 @@ void Font::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_descent"), &Font::get_descent);
 	ClassDB::bind_method(D_METHOD("get_height"), &Font::get_height);
 	ClassDB::bind_method(D_METHOD("is_distance_field_hint"), &Font::is_distance_field_hint);
+	ClassDB::bind_method(D_METHOD("get_char_size", "char", "next"), &Font::get_char_size, DEFVAL(0));
 	ClassDB::bind_method(D_METHOD("get_string_size", "string"), &Font::get_string_size);
 	ClassDB::bind_method(D_METHOD("get_wordwrap_string_size", "string", "width"), &Font::get_wordwrap_string_size);
 	ClassDB::bind_method(D_METHOD("has_outline"), &Font::has_outline);
@@ -606,8 +607,6 @@ void BitmapFont::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_texture_count"), &BitmapFont::get_texture_count);
 	ClassDB::bind_method(D_METHOD("get_texture", "idx"), &BitmapFont::get_texture);
 
-	ClassDB::bind_method(D_METHOD("get_char_size", "char", "next"), &BitmapFont::get_char_size, DEFVAL(0));
-
 	ClassDB::bind_method(D_METHOD("set_distance_field_hint", "enable"), &BitmapFont::set_distance_field_hint);
 
 	ClassDB::bind_method(D_METHOD("clear"), &BitmapFont::clear);

+ 1 - 1
thirdparty/README.md

@@ -507,7 +507,7 @@ File extracted from upstream release tarball:
 ## xatlas
 
 - Upstream: https://github.com/jpcy/xatlas
-- Version: git (e12ea82, 2019)
+- Version: git (470576d3516f7e6d8b4554e7c941194a935969fd, 2020)
 - License: MIT
 
 Files extracted from upstream source:

+ 1 - 1
thirdparty/xatlas/LICENSE

@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2018-2019 Jonathan Young
+Copyright (c) 2018-2020 Jonathan Young
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

File diff suppressed because it is too large
+ 277 - 352
thirdparty/xatlas/xatlas.cpp


+ 36 - 27
thirdparty/xatlas/xatlas.h

@@ -1,7 +1,7 @@
 /*
 MIT License
 
-Copyright (c) 2018-2019 Jonathan Young
+Copyright (c) 2018-2020 Jonathan Young
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -42,18 +42,19 @@ struct ChartType
 		Planar,
 		Ortho,
 		LSCM,
-		Piecewise
+		Piecewise,
+		Invalid
 	};
 };
 
 // A group of connected faces, belonging to a single atlas.
 struct Chart
 {
-	uint32_t atlasIndex; // Sub-atlas index.
 	uint32_t *faceArray;
+	uint32_t atlasIndex; // Sub-atlas index.
 	uint32_t faceCount;
-	uint32_t material;
 	ChartType::Enum type;
+	uint32_t material;
 };
 
 // Output vertex.
@@ -69,10 +70,10 @@ struct Vertex
 struct Mesh
 {
 	Chart *chartArray;
-	uint32_t chartCount;
 	uint32_t *indexArray;
-	uint32_t indexCount;
 	Vertex *vertexArray;
+	uint32_t chartCount;
+	uint32_t indexCount;
 	uint32_t vertexCount;
 };
 
@@ -84,15 +85,15 @@ static const uint32_t kImageIsPaddingBit = 0x20000000;
 // Empty on creation. Populated after charts are packed.
 struct Atlas
 {
+	uint32_t *image;
+	Mesh *meshes; // The output meshes, corresponding to each AddMesh call.
 	uint32_t width; // Atlas width in texels.
 	uint32_t height; // Atlas height in texels.
 	uint32_t atlasCount; // Number of sub-atlases. Equal to 0 unless PackOptions resolution is changed from default (0).
 	uint32_t chartCount; // Total number of charts in all meshes.
 	uint32_t meshCount; // Number of output meshes. Equal to the number of times AddMesh was called.
-	Mesh *meshes; // The output meshes, corresponding to each AddMesh call.
 	float *utilization; // Normalized atlas texel utilization array. E.g. a value of 0.8 means 20% empty space. atlasCount in length.
 	float texelsPerUnit; // Equal to PackOptions texelsPerUnit if texelsPerUnit > 0, otherwise an estimated value to match PackOptions resolution.
-	uint32_t *image;
 };
 
 // Create an empty atlas.
@@ -112,22 +113,23 @@ struct IndexFormat
 // Input mesh declaration.
 struct MeshDecl
 {
-	uint32_t vertexCount = 0;
 	const void *vertexPositionData = nullptr;
-	uint32_t vertexPositionStride = 0;
 	const void *vertexNormalData = nullptr; // optional
-	uint32_t vertexNormalStride = 0; // optional
 	const void *vertexUvData = nullptr; // optional. The input UVs are provided as a hint to the chart generator.
-	uint32_t vertexUvStride = 0; // optional
-	uint32_t indexCount = 0;
 	const void *indexData = nullptr; // optional
-	int32_t indexOffset = 0; // optional. Add this offset to all indices.
-	IndexFormat::Enum indexFormat = IndexFormat::UInt16;
 	
 	// Optional. indexCount / 3 (triangle count) in length.
 	// Don't atlas faces set to true. Ignored faces still exist in the output meshes, Vertex uv is set to (0, 0) and Vertex atlasIndex to -1.
 	const bool *faceIgnoreData = nullptr;
 
+	uint32_t vertexCount = 0;
+	uint32_t vertexPositionStride = 0;
+	uint32_t vertexNormalStride = 0; // optional
+	uint32_t vertexUvStride = 0; // optional
+	uint32_t indexCount = 0;
+	int32_t indexOffset = 0; // optional. Add this offset to all indices.
+	IndexFormat::Enum indexFormat = IndexFormat::UInt16;
+
 	// Vertex positions within epsilon distance of each other are considered colocal.
 	float epsilon = 1.192092896e-07F;
 };
@@ -151,14 +153,14 @@ void AddMeshJoin(Atlas *atlas);
 
 struct UvMeshDecl
 {
+	const void *vertexUvData = nullptr;
+	const void *indexData = nullptr; // optional
+	const uint32_t *faceMaterialData = nullptr; // Optional. Faces with different materials won't be assigned to the same chart. Must be indexCount / 3 in length.
 	uint32_t vertexCount = 0;
 	uint32_t vertexStride = 0;
-	const void *vertexUvData = nullptr;
 	uint32_t indexCount = 0;
-	const void *indexData = nullptr; // optional
 	int32_t indexOffset = 0; // optional. Add this offset to all indices.
 	IndexFormat::Enum indexFormat = IndexFormat::UInt16;
-	const uint32_t *faceMaterialData = nullptr; // Optional. Faces with different materials won't be assigned to the same chart. Must be indexCount / 3 in length.
 	bool rotateCharts = true;
 };
 
@@ -170,24 +172,31 @@ struct ChartOptions
 	float maxBoundaryLength = 0.0f; // Don't grow charts to have a longer boundary than this. 0 means no limit.
 
 	// Weights determine chart growth. Higher weights mean higher cost for that metric.
-	float proxyFitMetricWeight = 2.0f; // Angle between face and average chart normal.
-	float roundnessMetricWeight = 0.01f;
-	float straightnessMetricWeight = 6.0f;
-	float normalSeamMetricWeight = 4.0f; // If > 1000, normal seams are fully respected.
-	float textureSeamMetricWeight = 0.5f;
+	float normalDeviationWeight = 2.0f; // Angle between face and average chart normal.
+	float roundnessWeight = 0.01f;
+	float straightnessWeight = 6.0f;
+	float normalSeamWeight = 4.0f; // If > 1000, normal seams are fully respected.
+	float textureSeamWeight = 0.5f;
 
-	float maxThreshold = 2.0f; // If total of all metrics * weights > maxThreshold, don't grow chart. Lower values result in more charts.
+	float maxCost = 2.0f; // If total of all metrics * weights > maxCost, don't grow chart. Lower values result in more charts.
 	uint32_t maxIterations = 1; // Number of iterations of the chart growing and seeding phases. Higher values result in better charts.
 };
 
 // Call after all AddMesh calls. Can be called multiple times to recompute charts with different options.
-void ComputeCharts(Atlas *atlas, ChartOptions chartOptions = ChartOptions());
+void ComputeCharts(Atlas *atlas, ChartOptions options = ChartOptions());
 
 // Custom parameterization function. texcoords initial values are an orthogonal parameterization.
 typedef void (*ParameterizeFunc)(const float *positions, float *texcoords, uint32_t vertexCount, const uint32_t *indices, uint32_t indexCount);
 
+struct ParameterizeOptions
+{
+	ParameterizeFunc func = nullptr;
+	bool closeHoles = true; // If the custom parameterization function works with multiple boundaries, this can be set to false to improve performance.
+	bool fixTJunctions = true; // If meshes don't have T-junctions, this can be set to false to improve performance.
+};
+
 // Call after ComputeCharts. Can be called multiple times to re-parameterize charts with a different ParameterizeFunc.
-void ParameterizeCharts(Atlas *atlas, ParameterizeFunc func = nullptr);
+void ParameterizeCharts(Atlas *atlas, ParameterizeOptions options = ParameterizeOptions());
 
 struct PackOptions
 {
@@ -224,7 +233,7 @@ struct PackOptions
 void PackCharts(Atlas *atlas, PackOptions packOptions = PackOptions());
 
 // Equivalent to calling ComputeCharts, ParameterizeCharts and PackCharts in sequence. Can be called multiple times to regenerate with different options.
-void Generate(Atlas *atlas, ChartOptions chartOptions = ChartOptions(), ParameterizeFunc paramFunc = nullptr, PackOptions packOptions = PackOptions());
+void Generate(Atlas *atlas, ChartOptions chartOptions = ChartOptions(), ParameterizeOptions parameterizeOptions = ParameterizeOptions(), PackOptions packOptions = PackOptions());
 
 // Progress tracking.
 struct ProgressCategory

Some files were not shown because too many files changed in this diff