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

Cherry-picks for the 3.2 branch (future 3.2.2) - 7th batch
This commit is contained in:
Rémi Verschelde 2020-06-04 13:29:52 +02:00 committed by GitHub
commit ed1fc50bb9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 3282 additions and 2493 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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