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)
     Indah Sylvia (ISylvox)
     J08nY
     J08nY
     Jakub Grzesik (kubecz3k)
     Jakub Grzesik (kubecz3k)
+    James Buck (jbuck3)
     Jérôme Gully (Nutriz)
     Jérôme Gully (Nutriz)
     Joan Fons Sanchez (JFonS)
     Joan Fons Sanchez (JFonS)
     Johan Manuel (29jm)
     Johan Manuel (29jm)

+ 1 - 1
COPYRIGHT.txt

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

+ 60 - 27
DONORS.md

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

+ 9 - 4
SConstruct

@@ -177,12 +177,16 @@ for k in platform_opts.keys():
     for o in opt_list:
     for o in opt_list:
         opts.Add(o)
         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.
 # Detect modules.
 modules_detected = OrderedDict()
 modules_detected = OrderedDict()
 module_search_paths = ["modules"]  # Built-in path.
 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:
     for p in paths:
         try:
         try:
             module_search_paths.append(methods.convert_custom_modules_path(p))
             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)
 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
 # 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();
 		return MIDIDriver::get_singleton()->get_connected_inputs();
 
 
 	PoolStringArray list;
 	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() {
 void OS::open_midi_inputs() {
 
 
-	if (MIDIDriver::get_singleton())
+	if (MIDIDriver::get_singleton()) {
 		MIDIDriver::get_singleton()->open();
 		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() {
 void OS::close_midi_inputs() {
 
 
-	if (MIDIDriver::get_singleton())
+	if (MIDIDriver::get_singleton()) {
 		MIDIDriver::get_singleton()->close();
 		MIDIDriver::get_singleton()->close();
+	} else {
+		ERR_PRINT(vformat("MIDI input isn't supported on %s.", OS::get_singleton()->get_name()));
+	}
 }
 }
 
 
 OS::OS() {
 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:
  * using the following merit order:
  *  - If using NetworkClient, try to lookup project file or fail.
  *  - 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.
  *  - 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.
  *  - On relevant platforms (Android/iOS), lookup project file in OS resource path.
  *    If found, load it or fail.
  *    If found, load it or fail.
  *  - Lookup project file in passed `p_path` (--path passed by the user), i.e. we
  *  - 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();
 	String exec_path = OS::get_singleton()->get_executable_path();
 
 
 	if (exec_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_dir = exec_path.get_base_dir();
 		String exec_filename = exec_path.get_file();
 		String exec_filename = exec_path.get_file();
 		String exec_basename = exec_filename.get_basename();
 		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
 #ifdef OSX_ENABLED
 		if (!found) {
 		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"));
 			found = _load_resource_pack(OS::get_singleton()->get_bundle_resource_dir().plus_file(exec_basename + ".pck"));
 		}
 		}
 #endif
 #endif
 
 
 		if (!found) {
 		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"));
 			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) {
 		if (found) {
 			Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary");
 			Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary");
 			if (err == OK) {
 			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"));
 				_load_settings_text(exec_path.get_base_dir().plus_file("override.cfg"));
 			}
 			}
 			return err;
 			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() != "") {
 	if (OS::get_singleton()->get_resource_dir() != "") {
 		// OS will call ProjectSettings->get_resource_path which will be empty if not overridden!
 		// 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.
 		// If the OS would rather use a specific location, then it will not be empty.
 		resource_path = OS::get_singleton()->get_resource_dir().replace("\\", "/");
 		resource_path = OS::get_singleton()->get_resource_dir().replace("\\", "/");
 		if (resource_path != "" && resource_path[resource_path.length() - 1] == '/') {
 		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");
 		Error err = _load_settings_text_or_binary("res://project.godot", "res://project.binary");
 		if (err == OK) {
 		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");
 			_load_settings_text("res://override.cfg");
 		}
 		}
 		return err;
 		return err;
@@ -433,7 +442,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b
 	while (true) {
 	while (true) {
 		err = _load_settings_text_or_binary(current_dir.plus_file("project.godot"), current_dir.plus_file("project.binary"));
 		err = _load_settings_text_or_binary(current_dir.plus_file("project.godot"), current_dir.plus_file("project.binary"));
 		if (err == OK) {
 		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"));
 			_load_settings_text(current_dir.plus_file("override.cfg"));
 			candidate = current_dir;
 			candidate = current_dir;
 			found = true;
 			found = true;
@@ -452,14 +461,15 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b
 	}
 	}
 
 
 	resource_path = candidate;
 	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);
 	memdelete(d);
 
 
 	if (!found)
 	if (!found)
 		return err;
 		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;
 	return OK;
 }
 }

+ 4 - 0
doc/classes/AnimationTree.xml

@@ -4,6 +4,7 @@
 		A node to be used for advanced animation transitions in an [AnimationPlayer].
 		A node to be used for advanced animation transitions in an [AnimationPlayer].
 	</brief_description>
 	</brief_description>
 	<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>
 	</description>
 	<tutorials>
 	<tutorials>
 		<link>https://docs.godotengine.org/en/latest/tutorials/animation/animation_tree.html</link>
 		<link>https://docs.godotengine.org/en/latest/tutorials/animation/animation_tree.html</link>
@@ -23,6 +24,7 @@
 			<return type="Transform">
 			<return type="Transform">
 			</return>
 			</return>
 			<description>
 			<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>
 			</description>
 		</method>
 		</method>
 		<method name="rename_parameter">
 		<method name="rename_parameter">
@@ -47,6 +49,8 @@
 			The process mode of this [AnimationTree]. See [enum AnimationProcessMode] for available modes.
 			The process mode of this [AnimationTree]. See [enum AnimationProcessMode] for available modes.
 		</member>
 		</member>
 		<member name="root_motion_track" type="NodePath" setter="set_root_motion_track" getter="get_root_motion_track" default="NodePath(&quot;&quot;)">
 		<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>
 		<member name="tree_root" type="AnimationNode" setter="set_tree_root" getter="get_tree_root">
 		<member name="tree_root" type="AnimationNode" setter="set_tree_root" getter="get_tree_root">
 			The root animation node of this [AnimationTree]. See [AnimationNode].
 			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].
 				Creates a BitmapFont from the [code]*.fnt[/code] file at [code]path[/code].
 			</description>
 			</description>
 		</method>
 		</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">
 		<method name="get_kerning_pair" qualifiers="const">
 			<return type="int">
 			<return type="int">
 			</return>
 			</return>

+ 4 - 4
doc/classes/DirectionalLight.xml

@@ -43,19 +43,19 @@
 	</members>
 	</members>
 	<constants>
 	<constants>
 		<constant name="SHADOW_ORTHOGONAL" value="0" enum="ShadowMode">
 		<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>
 		<constant name="SHADOW_PARALLEL_2_SPLITS" value="1" enum="ShadowMode">
 		<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>
 		<constant name="SHADOW_PARALLEL_4_SPLITS" value="2" enum="ShadowMode">
 		<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>
 		<constant name="SHADOW_DEPTH_RANGE_STABLE" value="0" enum="ShadowDepthRange">
 		<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.
 			Keeps the shadow stable when the camera moves, at the cost of lower effective shadow resolution.
 		</constant>
 		</constant>
 		<constant name="SHADOW_DEPTH_RANGE_OPTIMIZED" value="1" enum="ShadowDepthRange">
 		<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>
 		</constant>
 	</constants>
 	</constants>
 </class>
 </class>

+ 1 - 1
doc/classes/DynamicFontData.xml

@@ -12,7 +12,7 @@
 	</methods>
 	</methods>
 	<members>
 	<members>
 		<member name="antialiased" type="bool" setter="set_antialiased" getter="is_antialiased" default="true">
 		<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>
 		<member name="font_path" type="String" setter="set_font_path" getter="get_font_path" default="&quot;&quot;">
 		<member name="font_path" type="String" setter="set_font_path" getter="get_font_path" default="&quot;&quot;">
 			The path to the vector font file.
 			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.
 				Selects the file, with the path provided by [code]file[/code], in the FileSystem dock.
 			</description>
 			</description>
 		</method>
 		</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">
 		<method name="set_main_screen_editor">
 			<return type="void">
 			<return type="void">
 			</return>
 			</return>
@@ -210,6 +202,10 @@
 			</description>
 			</description>
 		</method>
 		</method>
 	</methods>
 	</methods>
+	<members>
+		<member name="distraction_free_mode" type="bool" setter="set_distraction_free_mode" getter="is_distraction_free_mode_enabled">
+		</member>
+	</members>
 	<constants>
 	<constants>
 	</constants>
 	</constants>
 </class>
 </class>

+ 11 - 0
doc/classes/Font.xml

@@ -54,6 +54,17 @@
 				Returns the font ascent (number of pixels above the baseline).
 				Returns the font ascent (number of pixels above the baseline).
 			</description>
 			</description>
 		</method>
 		</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">
 		<method name="get_descent" qualifiers="const">
 			<return type="float">
 			<return type="float">
 			</return>
 			</return>

+ 1 - 1
doc/classes/ProjectSettings.xml

@@ -137,7 +137,7 @@
 			<argument index="0" name="file" type="String">
 			<argument index="0" name="file" type="String">
 			</argument>
 			</argument>
 			<description>
 			<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>
 			</description>
 		</method>
 		</method>
 		<method name="set_initial_value">
 		<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.")
     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()
     args = parser.parse_args()
 
 
+    print("Checking for errors in the XML class reference...")
+
     file_list = []  # type: List[str]
     file_list = []  # type: List[str]
 
 
     for path in args.path:
     for path in args.path:
@@ -334,7 +336,10 @@ def main():  # type: () -> None
         state.current_class = class_name
         state.current_class = class_name
         make_rst_class(class_def, state, args.dry_run, args.output)
         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)
         exit(1)
 
 
 def make_rst_class(class_def, state, dry_run, output_dir):  # type: (ClassDef, State, bool, str) -> None
 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
                 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
 def escape_rst(text, until_pos=-1):  # type: (str) -> str
     # Escape \ character, otherwise it ends up as an escape character in rst
     # Escape \ character, otherwise it ends up as an escape character in rst
     pos = 0
     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);
 	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;
 	DWORD fileAttr;
 
 
 	fileAttr = GetFileAttributesW(p_path.c_str());
 	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();
 		TreeItem *root = recent->create_item();
 
 
+		String icon_fallback = has_icon(base_type, "EditorIcons") ? base_type : "Object";
+
 		while (!f->eof_reached()) {
 		while (!f->eof_reached()) {
 			String l = f->get_line().strip_edges();
 			String l = f->get_line().strip_edges();
 			String name = l.split(" ")[0];
 			String name = l.split(" ")[0];
 			if ((ClassDB::class_exists(name) || ScriptServer::is_global_class(name)) && !_is_class_disabled_by_feature_profile(name)) {
 			if ((ClassDB::class_exists(name) || ScriptServer::is_global_class(name)) && !_is_class_disabled_by_feature_profile(name)) {
 				TreeItem *ti = recent->create_item(root);
 				TreeItem *ti = recent->create_item(root);
 				ti->set_text(0, l);
 				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;
 	const String &description = EditorHelp::get_doc_data()->class_list[p_type].brief_description;
 	item->set_tooltip(0, 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;
 	p_types[p_type] = item;
 }
 }
@@ -310,9 +313,8 @@ void CreateDialog::_update_search() {
 	EditorData &ed = EditorNode::get_editor_data();
 	EditorData &ed = EditorNode::get_editor_data();
 
 
 	root->set_text(0, base_type);
 	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;
 	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);
 				TreeItem *item = search_options->create_item(ti);
 				item->set_metadata(0, type);
 				item->set_metadata(0, type);
 				item->set_text(0, ct[i].name);
 				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()) {
 				if (!to_select || ct[i].name == search_box->get_text()) {
 					to_select = item;
 					to_select = item;
@@ -600,15 +600,20 @@ void CreateDialog::_save_favorite_list() {
 void CreateDialog::_update_favorite_list() {
 void CreateDialog::_update_favorite_list() {
 
 
 	favorites->clear();
 	favorites->clear();
+
 	TreeItem *root = favorites->create_item();
 	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++) {
 	for (int i = 0; i < favorite_list.size(); i++) {
 		String l = favorite_list[i];
 		String l = favorite_list[i];
 		String name = l.split(" ")[0];
 		String name = l.split(" ")[0];
 		if (!((ClassDB::class_exists(name) || ScriptServer::is_global_class(name)) && !_is_class_disabled_by_feature_profile(name)))
 		if (!((ClassDB::class_exists(name) || ScriptServer::is_global_class(name)) && !_is_class_disabled_by_feature_profile(name)))
 			continue;
 			continue;
+
 		TreeItem *ti = favorites->create_item(root);
 		TreeItem *ti = favorites->create_item(root);
 		ti->set_text(0, l);
 		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");
 	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;
 	String error;
 	if (!_autoload_name_is_valid(name, &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;
 		return false;
 	}
 	}
 
 
 	const String &path = p_path;
 	const String &path = p_path;
 	if (!FileAccess::exists(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;
 		return false;
 	}
 	}
 
 
 	if (!path.begins_with("res://")) {
 	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;
 		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_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_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_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_deploy_remote) _menu_option_confirm(RUN_DEPLOY_REMOTE_DEBUG, true);
 	if (check_file_server) _menu_option_confirm(RUN_FILE_SERVER, 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();
 	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->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->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();
 	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->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_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->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_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");
 	p->connect("id_pressed", this, "_menu_option");
 
 
 	menu_hb->add_spacer();
 	menu_hb->add_spacer();

+ 1 - 1
editor/editor_node.h

@@ -691,7 +691,7 @@ public:
 	bool get_docks_visible() const;
 	bool get_docks_visible() const;
 
 
 	void set_distraction_free_mode(bool p_enter);
 	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 add_control_to_dock(DockSlot p_slot, Control *p_control);
 	void remove_control_from_dock(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;
 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() {
 void EditorInterface::_bind_methods() {
 
 
 	ClassDB::bind_method(D_METHOD("inspect_object", "object", "for_property"), &EditorInterface::inspect_object, DEFVAL(String()));
 	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_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("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() {
 EditorInterface::EditorInterface() {

+ 1 - 0
editor/editor_plugin.h

@@ -105,6 +105,7 @@ public:
 
 
 	void set_main_screen_editor(const String &p_name);
 	void set_main_screen_editor(const String &p_name);
 	void set_distraction_free_mode(bool p_enter);
 	void set_distraction_free_mode(bool p_enter);
+	bool is_distraction_free_mode_enabled() const;
 
 
 	EditorInterface();
 	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 {
 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::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::INT, "flags/repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirrored"), 0));
 	r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "flags/filter"), true));
 	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 dx_low = double(x[2] - x[1]) / (y[2] - y[1] + 1);
 	double xf = x[0];
 	double xf = x[0];
 	double xt = x[0] + dx_upper; // if y[0] == y[1], special case
 	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) {
 		if (yi >= 0) {
 			for (int xi = (xf > 0 ? int(xf) : 0); xi <= (xt < width ? xt : width - 1); xi++) {
 			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;
 		GridContainer *child_container;
 
 
 		set_title(TTR("Configure Snap"));
 		set_title(TTR("Configure Snap"));
-		get_ok()->set_text(TTR("Close"));
 
 
 		container = memnew(VBoxContainer);
 		container = memnew(VBoxContainer);
 		add_child(container);
 		add_child(container);
@@ -5490,6 +5489,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
 	hb->add_child(pan_button);
 	hb->add_child(pan_button);
 	pan_button->set_toggle_mode(true);
 	pan_button->set_toggle_mode(true);
 	pan_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_PAN));
 	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"));
 	pan_button->set_tooltip(TTR("Pan Mode"));
 
 
 	ruler_button = memnew(ToolButton);
 	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()) {
 	for (List<ScriptLanguage::Warning>::Element *E = warnings.front(); E; E = E->next()) {
 		ScriptLanguage::Warning w = E->get();
 		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_cell();
 		warnings_panel->push_meta(w.line - 1);
 		warnings_panel->push_meta(w.line - 1);
 		warnings_panel->push_color(warnings_panel->get_color("warning_color", "Editor"));
 		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->push_cell();
 		warnings_panel->add_text(w.message);
 		warnings_panel->add_text(w.message);
 		warnings_panel->pop(); // Cell.
 		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.
 	warnings_panel->pop(); // Table.
 
 
@@ -1805,6 +1808,8 @@ ScriptTextEditor::ScriptTextEditor() {
 
 
 	warnings_panel = memnew(RichTextLabel);
 	warnings_panel = memnew(RichTextLabel);
 	editor_box->add_child(warnings_panel);
 	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_custom_minimum_size(Size2(0, 100 * EDSCALE));
 	warnings_panel->set_h_size_flags(SIZE_EXPAND_FILL);
 	warnings_panel->set_h_size_flags(SIZE_EXPAND_FILL);
 	warnings_panel->set_meta_underline(true);
 	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();
 	_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() {
 void TileMapEditor::_canvas_mouse_enter() {
 
 
 	mouse_over = true;
 	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("_clear_transform"), &TileMapEditor::_clear_transform);
 	ClassDB::bind_method(D_METHOD("_palette_selected"), &TileMapEditor::_palette_selected);
 	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_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("_fill_points"), &TileMapEditor::_fill_points);
 	ClassDB::bind_method(D_METHOD("_erase_points"), &TileMapEditor::_erase_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->add_constant_override("vseparation", 8 * EDSCALE);
 	palette->connect("item_selected", this, "_palette_selected");
 	palette->connect("item_selected", this, "_palette_selected");
 	palette->connect("multi_selected", this, "_palette_multi_selected");
 	palette->connect("multi_selected", this, "_palette_multi_selected");
+	palette->connect("gui_input", this, "_palette_input");
 	palette_container->add_child(palette);
 	palette_container->add_child(palette);
 
 
 	// Add message for when no texture is selected.
 	// Add message for when no texture is selected.
@@ -2034,7 +2051,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
 	toolbar->add_child(paint_button);
 	toolbar->add_child(paint_button);
 
 
 	bucket_fill_button = memnew(ToolButton);
 	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->connect("pressed", this, "_button_tool_select", make_binds(TOOL_BUCKET));
 	bucket_fill_button->set_toggle_mode(true);
 	bucket_fill_button->set_toggle_mode(true);
 	toolbar->add_child(bucket_fill_button);
 	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 _menu_option(int p_option);
 	void _palette_selected(int index);
 	void _palette_selected(int index);
 	void _palette_multi_selected(int index, bool selected);
 	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);
 	Dictionary _create_cell_dictionary(int tile, bool flip_x, bool flip_y, bool transpose, Vector2 autotile_coord);
 	void _start_undo(const String &p_action);
 	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_tileset_toolbar_confirm", &TileSetEditor::_on_tileset_toolbar_confirm);
 	ClassDB::bind_method("_on_texture_list_selected", &TileSetEditor::_on_texture_list_selected);
 	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_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_mode_changed", &TileSetEditor::_on_workspace_mode_changed);
 	ClassDB::bind_method("_on_workspace_overlay_draw", &TileSetEditor::_on_workspace_overlay_draw);
 	ClassDB::bind_method("_on_workspace_overlay_draw", &TileSetEditor::_on_workspace_overlay_draw);
 	ClassDB::bind_method("_on_workspace_process", &TileSetEditor::_on_workspace_process);
 	ClassDB::bind_method("_on_workspace_process", &TileSetEditor::_on_workspace_process);
@@ -592,6 +593,7 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) {
 	scroll = memnew(ScrollContainer);
 	scroll = memnew(ScrollContainer);
 	main_vb->add_child(scroll);
 	main_vb->add_child(scroll);
 	scroll->set_v_size_flags(SIZE_EXPAND_FILL);
 	scroll->set_v_size_flags(SIZE_EXPAND_FILL);
+	scroll->connect("gui_input", this, "_on_scroll_container_input");
 	scroll->set_clip_contents(true);
 	scroll->set_clip_contents(true);
 
 
 	empty_message = memnew(Label);
 	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;
 	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) {
 void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
 
 
 	if (tileset.is_null() || !get_current_texture().is_valid())
 	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;
 	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_valid()) {
 		if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && !creating_shape) {
 		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;
 				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
 	// Drag Middle Mouse
 	if (mm.is_valid()) {
 	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_overlay_draw();
 	void _on_workspace_draw();
 	void _on_workspace_draw();
 	void _on_workspace_process();
 	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_workspace_input(const Ref<InputEvent> &p_ie);
 	void _on_tool_clicked(int p_tool);
 	void _on_tool_clicked(int p_tool);
 	void _on_priority_changed(float val);
 	void _on_priority_changed(float val);

+ 6 - 0
editor/project_manager.cpp

@@ -945,6 +945,8 @@ public:
 		icon = NULL;
 		icon = NULL;
 		icon_needs_reload = true;
 		icon_needs_reload = true;
 		hover = false;
 		hover = false;
+
+		set_focus_mode(FocusMode::FOCUS_ALL);
 	}
 	}
 
 
 	void set_is_favorite(bool fav) {
 	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);
 			select_project(clicked_index);
 		}
 		}
 
 
+		if (_selected_project_keys.has(clicked_project.project_key)) {
+			clicked_project.control->grab_focus();
+		}
+
 		emit_signal(SIGNAL_SELECTION_CHANGED);
 		emit_signal(SIGNAL_SELECTION_CHANGED);
 
 
 		if (!mb->get_control() && mb->is_doubleclick()) {
 		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) {
 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) {
 	if (!profile_allow_script_editing) {
 		return;
 		return;
 	}
 	}

+ 4 - 3
editor/script_create_dialog.cpp

@@ -800,7 +800,7 @@ ScriptCreateDialog::ScriptCreateDialog() {
 	gc->add_child(memnew(Label(TTR("Language:"))));
 	gc->add_child(memnew(Label(TTR("Language:"))));
 	gc->add_child(language_menu);
 	gc->add_child(language_menu);
 
 
-	default_language = 0;
+	default_language = -1;
 	for (int i = 0; i < ScriptServer::get_language_count(); i++) {
 	for (int i = 0; i < ScriptServer::get_language_count(); i++) {
 
 
 		String lang = ScriptServer::get_language(i)->get_name();
 		String lang = ScriptServer::get_language(i)->get_name();
@@ -809,8 +809,9 @@ ScriptCreateDialog::ScriptCreateDialog() {
 			default_language = i;
 			default_language = i;
 		}
 		}
 	}
 	}
-
-	language_menu->select(default_language);
+	if (default_language >= 0) {
+		language_menu->select(default_language);
+	}
 	current_language = default_language;
 	current_language = default_language;
 
 
 	language_menu->connect("item_selected", this, "_lang_changed");
 	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):
 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):
 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
 #ifdef DEBUG_ENABLED
 				GET_VARIANT_PTR(test, 1);
 				GET_VARIANT_PTR(test, 1);
-				GET_VARIANT_PTR(message, 2);
 				bool result = test->booleanize();
 				bool result = test->booleanize();
 
 
 				if (!result) {
 				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()) {
 					if (message_str.empty()) {
 						err_text = "Assertion failed.";
 						err_text = "Assertion failed.";
 					} else {
 					} 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;
 		return true;
 	}
 	}
 
 
+#ifdef TOOLS_ENABLED
 	MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute));
 	MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute));
+#endif
 
 
 	PropertyHint hint = PROPERTY_HINT_NONE;
 	PropertyHint hint = PROPERTY_HINT_NONE;
 	String hint_string;
 	String hint_string;

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

@@ -436,7 +436,7 @@ namespace GodotTools
                 aboutLabel.Text =
                 aboutLabel.Text =
                     "C# support in Godot Engine is in late alpha stage and, while already usable, " +
                     "C# support in Godot Engine is in late alpha stage and, while already usable, " +
                     "it is not meant for use in production.\n\n" +
                     "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, " +
                     "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" +
                     "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" +
                     "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)
 #if defined(TOOLS_ENABLED)
 	bool tool_assemblies_loaded = _load_tools_assemblies();
 	bool tool_assemblies_loaded = _load_tools_assemblies();
 	CRASH_COND_MSG(!tool_assemblies_loaded, "Mono: Failed to load '" TOOLS_ASM_NAME "' assemblies.");
 	CRASH_COND_MSG(!tool_assemblies_loaded, "Mono: Failed to load '" TOOLS_ASM_NAME "' assemblies.");
-#endif
 
 
 	if (Main::is_project_manager())
 	if (Main::is_project_manager())
 		return;
 		return;
+#endif
 
 
 	// Load the project's main assembly. This doesn't necessarily need to succeed.
 	// 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
 	// 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;
 		List<VisualScript::DataConnection> data_connections;
 		script->get_data_connection_list(p_func_from, &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;
 		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 out_p = E->get().from_port;
 			int in_p = E->get().to_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))
 			if (!connections.has(to))
 				connections.set(to, Map<int, Pair<int, int> >());
 				connections.set(to, Map<int, Pair<int, int> >());
 			connections[to].insert(in_p, Pair<int, int>(from, out_p));
 			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));
 	ERR_FAIL_COND_V_MSG(err != xatlas::AddMeshError::Enum::Success, false, xatlas::StringForEnum(err));
 
 
 	printf("Generate..\n");
 	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_x = atlas->width;
 	*r_size_hint_y = atlas->height;
 	*r_size_hint_y = atlas->height;

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

@@ -257,6 +257,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
 	};
 	};
 
 
 	Vector<PluginConfig> plugins;
 	Vector<PluginConfig> plugins;
+	String last_plugin_names;
+	uint64_t last_custom_build_time = 0;
 	volatile bool plugins_changed;
 	volatile bool plugins_changed;
 	Mutex *plugins_lock;
 	Mutex *plugins_lock;
 	Vector<Device> devices;
 	Vector<Device> devices;
@@ -1786,23 +1788,32 @@ public:
 		// Look for export templates (first official, and if defined custom templates).
 		// Look for export templates (first official, and if defined custom templates).
 
 
 		if (!bool(p_preset->get("custom_template/use_custom_build"))) {
 		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") != "") {
 			if (p_preset->get("custom_template/debug") != "") {
 				dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
 				dvalid = FileAccess::exists(p_preset->get("custom_template/debug"));
 				if (!dvalid) {
 				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") != "") {
 			if (p_preset->get("custom_template/release") != "") {
 				rvalid = FileAccess::exists(p_preset->get("custom_template/release"));
 				rvalid = FileAccess::exists(p_preset->get("custom_template/release"));
 				if (!rvalid) {
 				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;
 			valid = dvalid || rvalid;
+			if (!valid) {
+				err += template_err;
+			}
 		} else {
 		} else {
 			valid = exists_export_template("android_source.zip", &err);
 			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) {
 	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);
 		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 local_plugins_binaries = get_plugins_binaries(BINARY_TYPE_LOCAL, enabled_plugins);
 			String remote_plugins_binaries = get_plugins_binaries(BINARY_TYPE_REMOTE, 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);
 			String custom_maven_repos = get_plugins_custom_maven_repos(enabled_plugins);
+			bool clean_build_required = is_clean_build_required(enabled_plugins);
 
 
 			List<String> cmdline;
 			List<String> cmdline;
+			if (clean_build_required) {
+				cmdline.push_back("clean");
+			}
 			cmdline.push_back("build");
 			cmdline.push_back("build");
 			cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name.
 			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.
 			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 {
     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.
         // Feel free to modify the application id to your own.
         applicationId getExportPackageName()
         applicationId getExportPackageName()
         minSdkVersion versions.minSdk
         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 {
 struct PluginConfig {
 	// Set to true when the config file is properly loaded.
 	// Set to true when the config file is properly loaded.
 	bool valid_config = false;
 	bool valid_config = false;
+	// Unix timestamp of last change to this plugin.
+	uint64_t last_updated = 0;
 
 
 	// Required config section
 	// Required config section
 	String name;
 	String name;
@@ -87,6 +89,7 @@ struct PluginConfig {
  */
  */
 static const PluginConfig GODOT_PAYMENT = {
 static const PluginConfig GODOT_PAYMENT = {
 	/*.valid_config =*/true,
 	/*.valid_config =*/true,
+	/*.last_updated =*/0,
 	/*.name =*/"GodotPayment",
 	/*.name =*/"GodotPayment",
 	/*.binary_type =*/"local",
 	/*.binary_type =*/"local",
 	/*.binary =*/"res://android/build/libs/plugins/GodotPayment.release.aar",
 	/*.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;
 	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) {
 static inline PluginConfig load_plugin_config(Ref<ConfigFile> config_file, const String &path) {
 	PluginConfig plugin_config = {};
 	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.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) {
 	if (refCF) {
 		CFNumberGetValue((CFNumberRef)refCF, kCFNumberSInt32Type, &product_id);
 		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) {
 	if (vendor && product_id) {
 		char uid[128];
 		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);
 		input->joy_connection_changed(id, true, name, uid);
 	} else {
 	} else {
 		//bluetooth device
 		//bluetooth device

+ 23 - 14
platform/windows/joypad_windows.cpp

@@ -33,10 +33,6 @@
 #include <oleauto.h>
 #include <oleauto.h>
 #include <wbemidl.h>
 #include <wbemidl.h>
 
 
-#ifndef __GNUC__
-#define __builtin_bswap32 _byteswap_ulong
-#endif
-
 #if defined(__GNUC__)
 #if defined(__GNUC__)
 // Workaround GCC warning from -Wcast-function-type.
 // Workaround GCC warning from -Wcast-function-type.
 #define GetProcAddress (void *)GetProcAddress
 #define GetProcAddress (void *)GetProcAddress
@@ -67,18 +63,26 @@ JoypadWindows::JoypadWindows(InputDefault *_input, HWND *hwnd) {
 	for (int i = 0; i < JOYPADS_MAX; i++)
 	for (int i = 0; i < JOYPADS_MAX; i++)
 		attached_joypads[i] = false;
 		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() {
 JoypadWindows::~JoypadWindows() {
 
 
 	close_joypad();
 	close_joypad();
-	dinput->Release();
+	if (dinput) {
+		dinput->Release();
+	}
 	unload_xinput();
 	unload_xinput();
 }
 }
 
 
@@ -142,6 +146,7 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) {
 
 
 bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) {
 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;
 	HRESULT hr;
 	int num = input->get_unused_joy_id();
 	int num = input->get_unused_joy_id();
 
 
@@ -165,10 +170,13 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) {
 
 
 	const GUID &guid = instance->guidProduct;
 	const GUID &guid = instance->guidProduct;
 	char uid[128];
 	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;
 	id_to_change = joypad_count;
 
 
@@ -280,6 +288,7 @@ void JoypadWindows::close_joypad(int id) {
 
 
 void JoypadWindows::probe_joypads() {
 void JoypadWindows::probe_joypads() {
 
 
+	ERR_FAIL_NULL_MSG(dinput, "DirectInput not initialized. Rebooting your PC may solve this issue.");
 	DWORD dwResult;
 	DWORD dwResult;
 	for (DWORD i = 0; i < XUSER_MAX_COUNT; i++) {
 	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) {
 void OS_Windows::set_current_tablet_driver(const String &p_driver) {
+	if (get_tablet_driver_count() == 0) {
+		return;
+	}
 	bool found = false;
 	bool found = false;
 	for (int i = 0; i < get_tablet_driver_count(); i++) {
 	for (int i = 0; i < get_tablet_driver_count(); i++) {
 		if (p_driver == get_tablet_driver_name(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);
 	ClassDB::bind_method(D_METHOD("is_blend_splits_enabled"), &DirectionalLight::is_blend_splits_enabled);
 
 
 	ADD_GROUP("Directional Shadow", "directional_shadow_");
 	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_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_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);
 	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: {
 		case NOTIFICATION_PARENTED: {
 
 
 			for (int i = 0; i < 4; i++)
 			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;
 		} break;
 		case NOTIFICATION_VISIBILITY_CHANGED: {
 		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;                                                                                     \
 			lh = line < l.height_caches.size() ? l.height_caches[line] : 1;                                                                                     \
 			line_ascent = line < l.ascent_caches.size() ? l.ascent_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;                                                                         \
 			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 (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;                                                                                                                   \
 			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) {
 void RichTextLabel::_scroll_changed(double) {
 
 
-	if (updating_scroll || !scroll_active)
+	if (updating_scroll)
 		return;
 		return;
 
 
 	if (scroll_follow && vscroll->get_value() >= (vscroll->get_max() - vscroll->get_page()))
 	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;
 		return;
 
 
 	scroll_active = p_active;
 	scroll_active = p_active;
+	vscroll->set_drag_node_enabled(p_active);
 	update();
 	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) {
 void ScrollBar::_drag_node_input(const Ref<InputEvent> &p_input) {
+	if (!drag_node_enabled) {
+		return;
+	}
 
 
 	Ref<InputEventMouseButton> mb = p_input;
 	Ref<InputEventMouseButton> mb = p_input;
 
 
@@ -638,6 +641,10 @@ NodePath ScrollBar::get_drag_node() const {
 	return drag_node_path;
 	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) {
 void ScrollBar::set_smooth_scroll_enabled(bool p_enable) {
 	smooth_scroll_enabled = p_enable;
 	smooth_scroll_enabled = p_enable;
 }
 }
@@ -668,6 +675,7 @@ ScrollBar::ScrollBar(Orientation p_orientation) {
 
 
 	drag.active = false;
 	drag.active = false;
 
 
+	drag_node_enabled = true;
 	drag_node_speed = Vector2();
 	drag_node_speed = Vector2();
 	drag_node_touching = false;
 	drag_node_touching = false;
 	drag_node_touching_deaccel = false;
 	drag_node_touching_deaccel = false;

+ 2 - 1
scene/gui/scroll_bar.h

@@ -53,7 +53,6 @@ class ScrollBar : public Range {
 	HighlightStatus highlight;
 	HighlightStatus highlight;
 
 
 	struct Drag {
 	struct Drag {
-
 		bool active;
 		bool active;
 		float pos_at_click;
 		float pos_at_click;
 		float value_at_click;
 		float value_at_click;
@@ -70,6 +69,7 @@ class ScrollBar : public Range {
 
 
 	Node *drag_node;
 	Node *drag_node;
 	NodePath drag_node_path;
 	NodePath drag_node_path;
+	bool drag_node_enabled;
 
 
 	Vector2 drag_node_speed;
 	Vector2 drag_node_speed;
 	Vector2 drag_node_accum;
 	Vector2 drag_node_accum;
@@ -101,6 +101,7 @@ public:
 
 
 	void set_drag_node(const NodePath &p_path);
 	void set_drag_node(const NodePath &p_path);
 	NodePath get_drag_node() const;
 	NodePath get_drag_node() const;
+	void set_drag_node_enabled(bool p_enable);
 
 
 	void set_smooth_scroll_enabled(bool p_enable);
 	void set_smooth_scroll_enabled(bool p_enable);
 	bool is_smooth_scroll_enabled() const;
 	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;
 		goto cleanup_stroker;
 	if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0)
 	if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0)
 		goto cleanup_glyph;
 		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;
 		goto cleanup_glyph;
 
 
 	glyph_bitmap = (FT_BitmapGlyph)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_descent"), &Font::get_descent);
 	ClassDB::bind_method(D_METHOD("get_height"), &Font::get_height);
 	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("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_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("get_wordwrap_string_size", "string", "width"), &Font::get_wordwrap_string_size);
 	ClassDB::bind_method(D_METHOD("has_outline"), &Font::has_outline);
 	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_count"), &BitmapFont::get_texture_count);
 	ClassDB::bind_method(D_METHOD("get_texture", "idx"), &BitmapFont::get_texture);
 	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("set_distance_field_hint", "enable"), &BitmapFont::set_distance_field_hint);
 
 
 	ClassDB::bind_method(D_METHOD("clear"), &BitmapFont::clear);
 	ClassDB::bind_method(D_METHOD("clear"), &BitmapFont::clear);

+ 1 - 1
thirdparty/README.md

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

+ 1 - 1
thirdparty/xatlas/LICENSE

@@ -1,6 +1,6 @@
 MIT License
 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
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 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
 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
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 of this software and associated documentation files (the "Software"), to deal
@@ -42,18 +42,19 @@ struct ChartType
 		Planar,
 		Planar,
 		Ortho,
 		Ortho,
 		LSCM,
 		LSCM,
-		Piecewise
+		Piecewise,
+		Invalid
 	};
 	};
 };
 };
 
 
 // A group of connected faces, belonging to a single atlas.
 // A group of connected faces, belonging to a single atlas.
 struct Chart
 struct Chart
 {
 {
-	uint32_t atlasIndex; // Sub-atlas index.
 	uint32_t *faceArray;
 	uint32_t *faceArray;
+	uint32_t atlasIndex; // Sub-atlas index.
 	uint32_t faceCount;
 	uint32_t faceCount;
-	uint32_t material;
 	ChartType::Enum type;
 	ChartType::Enum type;
+	uint32_t material;
 };
 };
 
 
 // Output vertex.
 // Output vertex.
@@ -69,10 +70,10 @@ struct Vertex
 struct Mesh
 struct Mesh
 {
 {
 	Chart *chartArray;
 	Chart *chartArray;
-	uint32_t chartCount;
 	uint32_t *indexArray;
 	uint32_t *indexArray;
-	uint32_t indexCount;
 	Vertex *vertexArray;
 	Vertex *vertexArray;
+	uint32_t chartCount;
+	uint32_t indexCount;
 	uint32_t vertexCount;
 	uint32_t vertexCount;
 };
 };
 
 
@@ -84,15 +85,15 @@ static const uint32_t kImageIsPaddingBit = 0x20000000;
 // Empty on creation. Populated after charts are packed.
 // Empty on creation. Populated after charts are packed.
 struct Atlas
 struct Atlas
 {
 {
+	uint32_t *image;
+	Mesh *meshes; // The output meshes, corresponding to each AddMesh call.
 	uint32_t width; // Atlas width in texels.
 	uint32_t width; // Atlas width in texels.
 	uint32_t height; // Atlas height 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 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 chartCount; // Total number of charts in all meshes.
 	uint32_t meshCount; // Number of output meshes. Equal to the number of times AddMesh was called.
 	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 *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.
 	float texelsPerUnit; // Equal to PackOptions texelsPerUnit if texelsPerUnit > 0, otherwise an estimated value to match PackOptions resolution.
-	uint32_t *image;
 };
 };
 
 
 // Create an empty atlas.
 // Create an empty atlas.
@@ -112,22 +113,23 @@ struct IndexFormat
 // Input mesh declaration.
 // Input mesh declaration.
 struct MeshDecl
 struct MeshDecl
 {
 {
-	uint32_t vertexCount = 0;
 	const void *vertexPositionData = nullptr;
 	const void *vertexPositionData = nullptr;
-	uint32_t vertexPositionStride = 0;
 	const void *vertexNormalData = nullptr; // optional
 	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.
 	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
 	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.
 	// 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.
 	// 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;
 	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.
 	// Vertex positions within epsilon distance of each other are considered colocal.
 	float epsilon = 1.192092896e-07F;
 	float epsilon = 1.192092896e-07F;
 };
 };
@@ -151,14 +153,14 @@ void AddMeshJoin(Atlas *atlas);
 
 
 struct UvMeshDecl
 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 vertexCount = 0;
 	uint32_t vertexStride = 0;
 	uint32_t vertexStride = 0;
-	const void *vertexUvData = nullptr;
 	uint32_t indexCount = 0;
 	uint32_t indexCount = 0;
-	const void *indexData = nullptr; // optional
 	int32_t indexOffset = 0; // optional. Add this offset to all indices.
 	int32_t indexOffset = 0; // optional. Add this offset to all indices.
 	IndexFormat::Enum indexFormat = IndexFormat::UInt16;
 	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;
 	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.
 	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.
 	// 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.
 	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.
 // 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.
 // 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);
 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.
 // 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
 struct PackOptions
 {
 {
@@ -224,7 +233,7 @@ struct PackOptions
 void PackCharts(Atlas *atlas, PackOptions packOptions = 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.
 // 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.
 // Progress tracking.
 struct ProgressCategory
 struct ProgressCategory

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